Snap for 12609205 from bc5637296a to 25Q1-release
Change-Id: I25c49dd7adabfc62091a4b42dc177dd7cf076ee4
This commit is contained in:
@@ -23,6 +23,8 @@ import static android.view.WindowInsets.Type.statusBars;
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import android.appwidget.AppWidgetManager;
|
||||
import android.appwidget.AppWidgetProviderInfo;
|
||||
import android.content.ClipData;
|
||||
@@ -44,6 +46,7 @@ import com.android.launcher3.dragndrop.SimpleDragLayer;
|
||||
import com.android.launcher3.model.StringCache;
|
||||
import com.android.launcher3.model.WidgetItem;
|
||||
import com.android.launcher3.model.WidgetPredictionsRequester;
|
||||
import com.android.launcher3.model.WidgetsFilterDataProvider;
|
||||
import com.android.launcher3.model.WidgetsModel;
|
||||
import com.android.launcher3.model.data.ItemInfo;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
@@ -112,6 +115,7 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
private WidgetPredictionsRequester mWidgetPredictionsRequester;
|
||||
private final WidgetPickerDataProvider mWidgetPickerDataProvider =
|
||||
new WidgetPickerDataProvider();
|
||||
private WidgetsFilterDataProvider mWidgetsFilterDataProvider;
|
||||
|
||||
private int mDesiredWidgetWidth;
|
||||
private int mDesiredWidgetHeight;
|
||||
@@ -133,13 +137,13 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
@Nullable
|
||||
private WidgetsFullSheet mWidgetSheet;
|
||||
|
||||
private final Predicate<WidgetItem> mWidgetsFilter = widget -> {
|
||||
private final Predicate<WidgetItem> mNoShortcutsFilter = widget -> {
|
||||
final WidgetAcceptabilityVerdict verdict =
|
||||
isWidgetAcceptable(widget, /* applySizeFilter=*/ false);
|
||||
verdict.maybeLogVerdict();
|
||||
return verdict.isAcceptable;
|
||||
};
|
||||
private final Predicate<WidgetItem> mDefaultWidgetsFilter = widget -> {
|
||||
private final Predicate<WidgetItem> mHostSizeAndNoShortcutsFilter = widget -> {
|
||||
final WidgetAcceptabilityVerdict verdict =
|
||||
isWidgetAcceptable(widget, /* applySizeFilter=*/ true);
|
||||
verdict.maybeLogVerdict();
|
||||
@@ -157,6 +161,7 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
|
||||
mDeviceProfile = idp.getDeviceProfile(this);
|
||||
mModel = new WidgetsModel();
|
||||
mWidgetsFilterDataProvider = WidgetsFilterDataProvider.Companion.newInstance(this);
|
||||
|
||||
setContentView(R.layout.widget_picker_activity);
|
||||
mDragLayer = findViewById(R.id.drag_layer);
|
||||
@@ -288,13 +293,16 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
private void refreshAndBindWidgets() {
|
||||
MODEL_EXECUTOR.execute(() -> {
|
||||
LauncherAppState app = LauncherAppState.getInstance(this);
|
||||
// Don't have to setup filters - its setup when launcher loads
|
||||
// Just refresh filters with available cached info.
|
||||
mModel.updateWidgetFilters(mWidgetsFilterDataProvider);
|
||||
mModel.update(app, null);
|
||||
|
||||
StringCache stringCache = new StringCache();
|
||||
stringCache.loadStrings(this);
|
||||
|
||||
bindStringCache(stringCache);
|
||||
bindWidgets(mModel.getWidgetsByPackageItem());
|
||||
bindWidgets(mModel.getWidgetsByPackageItem(), mModel.getDefaultWidgetsFilter());
|
||||
// Open sheet once widgets are available, so that it doesn't interrupt the open
|
||||
// animation.
|
||||
openWidgetsSheet();
|
||||
@@ -310,14 +318,23 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
MAIN_EXECUTOR.execute(() -> mStringCache = stringCache);
|
||||
}
|
||||
|
||||
private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets) {
|
||||
private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets,
|
||||
@Nullable Predicate<WidgetItem> defaultWidgetsFilter) {
|
||||
WidgetsListBaseEntriesBuilder builder = new WidgetsListBaseEntriesBuilder(
|
||||
mApp.getContext());
|
||||
|
||||
final List<WidgetsListBaseEntry> allWidgets = builder.build(widgets, mWidgetsFilter);
|
||||
final List<WidgetsListBaseEntry> defaultWidgets =
|
||||
shouldShowDefaultWidgets() ? builder.build(widgets,
|
||||
mDefaultWidgetsFilter) : List.of();
|
||||
final List<WidgetsListBaseEntry> allWidgets = builder.build(widgets, mNoShortcutsFilter);
|
||||
|
||||
// Default list is shown if either defaultWidgetsFilter exists or host has additionally
|
||||
// enforced size filtering.
|
||||
@Nullable Predicate<WidgetItem> defaultListFilter =
|
||||
hasHostSizeFilters() ? mHostSizeAndNoShortcutsFilter : null;
|
||||
if (defaultWidgetsFilter != null) {
|
||||
defaultListFilter = defaultListFilter != null ? defaultListFilter.and(
|
||||
defaultWidgetsFilter) : defaultWidgetsFilter;
|
||||
}
|
||||
final List<WidgetsListBaseEntry> defaultWidgets = defaultListFilter != null ? builder.build(
|
||||
widgets, defaultListFilter) : emptyList();
|
||||
|
||||
MAIN_EXECUTOR.execute(
|
||||
() -> mWidgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets));
|
||||
@@ -342,6 +359,7 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
MODEL_EXECUTOR.execute(() -> mWidgetsFilterDataProvider.destroy());
|
||||
if (mWidgetPredictionsRequester != null) {
|
||||
mWidgetPredictionsRequester.clear();
|
||||
}
|
||||
@@ -398,7 +416,7 @@ public class WidgetPickerActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
|
||||
private boolean shouldShowDefaultWidgets() {
|
||||
private boolean hasHostSizeFilters() {
|
||||
// If optional filters such as size filter are present, we display them as default widgets.
|
||||
return mDesiredWidgetWidth != 0 || mDesiredWidgetHeight != 0;
|
||||
}
|
||||
|
||||
@@ -50,8 +50,12 @@ public class FallbackTaskbarUIController
|
||||
public void onStateTransitionStart(RecentsState toState) {
|
||||
animateToRecentsState(toState);
|
||||
|
||||
RecentsView recentsView = getRecentsView();
|
||||
if (recentsView == null) {
|
||||
return;
|
||||
}
|
||||
// Handle tapping on live tile.
|
||||
getRecentsView().setTaskLaunchListener(toState == RecentsState.DEFAULT
|
||||
recentsView.setTaskLaunchListener(toState == RecentsState.DEFAULT
|
||||
? (() -> animateToRecentsState(RecentsState.BACKGROUND_APP)) : null);
|
||||
}
|
||||
|
||||
@@ -81,7 +85,10 @@ public class FallbackTaskbarUIController
|
||||
@Override
|
||||
protected void onDestroy() {
|
||||
super.onDestroy();
|
||||
getRecentsView().setTaskLaunchListener(null);
|
||||
RecentsView recentsView = getRecentsView();
|
||||
if (recentsView != null) {
|
||||
recentsView.setTaskLaunchListener(null);
|
||||
}
|
||||
mRecentsContainer.setTaskbarUIController(null);
|
||||
mRecentsContainer.getStateManager().removeStateListener(mStateListener);
|
||||
}
|
||||
@@ -112,7 +119,7 @@ public class FallbackTaskbarUIController
|
||||
}
|
||||
|
||||
@Override
|
||||
public RecentsView getRecentsView() {
|
||||
public @Nullable RecentsView getRecentsView() {
|
||||
return mRecentsContainer.getOverviewPanel();
|
||||
}
|
||||
|
||||
|
||||
@@ -20,9 +20,9 @@ import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
|
||||
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
import static com.android.quickstep.util.SplitScreenUtils.convertShellSplitBoundsToLauncher;
|
||||
import static com.android.wm.shell.shared.GroupedRecentTaskInfo.TYPE_FREEFORM;
|
||||
import static com.android.wm.shell.shared.GroupedTaskInfo.TYPE_FREEFORM;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.ComponentName;
|
||||
@@ -40,7 +40,7 @@ import com.android.quickstep.util.DesktopTask;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||
import com.android.wm.shell.shared.GroupedRecentTaskInfo;
|
||||
import com.android.wm.shell.shared.GroupedTaskInfo;
|
||||
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
|
||||
|
||||
import java.io.PrintWriter;
|
||||
@@ -76,7 +76,7 @@ public class RecentTasksList {
|
||||
private @Nullable RecentsModel.RunningTasksListener mRunningTasksListener;
|
||||
private @Nullable RecentsModel.RecentTasksChangedListener mRecentTasksChangedListener;
|
||||
// Tasks are stored in order of least recently launched to most recently launched.
|
||||
private ArrayList<ActivityManager.RunningTaskInfo> mRunningTasks;
|
||||
private ArrayList<RunningTaskInfo> mRunningTasks;
|
||||
|
||||
public RecentTasksList(Context context, LooperExecutor mainThreadExecutor,
|
||||
KeyguardManager keyguardManager, SystemUiProxy sysUiProxy,
|
||||
@@ -93,35 +93,38 @@ public class RecentTasksList {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
public void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
|
||||
mMainThreadExecutor.execute(() -> {
|
||||
RecentTasksList.this.onRunningTaskAppeared(taskInfo);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
public void onRunningTaskVanished(RunningTaskInfo taskInfo) {
|
||||
mMainThreadExecutor.execute(() -> {
|
||||
RecentTasksList.this.onRunningTaskVanished(taskInfo);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
public void onRunningTaskChanged(RunningTaskInfo taskInfo) {
|
||||
mMainThreadExecutor.execute(() -> {
|
||||
RecentTasksList.this.onRunningTaskChanged(taskInfo);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskMovedToFront(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
public void onTaskMovedToFront(GroupedTaskInfo[] visibleTasks) {
|
||||
mMainThreadExecutor.execute(() -> {
|
||||
topTaskTracker.onTaskMovedToFront(taskInfo);
|
||||
// TODO(b/346588978): We currently are only sending a single task, but this will
|
||||
// be updated once we send the full set of visible tasks
|
||||
final TaskInfo info = visibleTasks[0].getTaskInfo1();
|
||||
topTaskTracker.handleTaskMovedToFront(info);
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
public void onTaskInfoChanged(RunningTaskInfo taskInfo) {
|
||||
mMainThreadExecutor.execute(() -> topTaskTracker.onTaskChanged(taskInfo));
|
||||
}
|
||||
});
|
||||
@@ -250,7 +253,7 @@ public class RecentTasksList {
|
||||
mRecentTasksChangedListener = null;
|
||||
}
|
||||
|
||||
private void initRunningTasks(ArrayList<ActivityManager.RunningTaskInfo> runningTasks) {
|
||||
private void initRunningTasks(ArrayList<RunningTaskInfo> runningTasks) {
|
||||
// Tasks are retrieved in order of most recently launched/used to least recently launched.
|
||||
mRunningTasks = new ArrayList<>(runningTasks);
|
||||
Collections.reverse(mRunningTasks);
|
||||
@@ -259,13 +262,13 @@ public class RecentTasksList {
|
||||
/**
|
||||
* Gets the set of running tasks.
|
||||
*/
|
||||
public ArrayList<ActivityManager.RunningTaskInfo> getRunningTasks() {
|
||||
public ArrayList<RunningTaskInfo> getRunningTasks() {
|
||||
return mRunningTasks;
|
||||
}
|
||||
|
||||
private void onRunningTaskAppeared(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
private void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
|
||||
// Make sure this task is not already in the list
|
||||
for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
|
||||
for (RunningTaskInfo existingTask : mRunningTasks) {
|
||||
if (taskInfo.taskId == existingTask.taskId) {
|
||||
return;
|
||||
}
|
||||
@@ -276,9 +279,9 @@ public class RecentTasksList {
|
||||
}
|
||||
}
|
||||
|
||||
private void onRunningTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
private void onRunningTaskVanished(RunningTaskInfo taskInfo) {
|
||||
// Find the task from the list of running tasks, if it exists
|
||||
for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
|
||||
for (RunningTaskInfo existingTask : mRunningTasks) {
|
||||
if (existingTask.taskId != taskInfo.taskId) continue;
|
||||
|
||||
mRunningTasks.remove(existingTask);
|
||||
@@ -289,9 +292,9 @@ public class RecentTasksList {
|
||||
}
|
||||
}
|
||||
|
||||
private void onRunningTaskChanged(ActivityManager.RunningTaskInfo taskInfo) {
|
||||
private void onRunningTaskChanged(RunningTaskInfo taskInfo) {
|
||||
// Find the task from the list of running tasks, if it exists
|
||||
for (ActivityManager.RunningTaskInfo existingTask : mRunningTasks) {
|
||||
for (RunningTaskInfo existingTask : mRunningTasks) {
|
||||
if (existingTask.taskId != taskInfo.taskId) continue;
|
||||
|
||||
mRunningTasks.remove(existingTask);
|
||||
@@ -309,7 +312,7 @@ public class RecentTasksList {
|
||||
@VisibleForTesting
|
||||
TaskLoadResult loadTasksInBackground(int numTasks, int requestId, boolean loadKeysOnly) {
|
||||
int currentUserId = Process.myUserHandle().getIdentifier();
|
||||
ArrayList<GroupedRecentTaskInfo> rawTasks;
|
||||
ArrayList<GroupedTaskInfo> rawTasks;
|
||||
try {
|
||||
rawTasks = mSysUiProxy.getRecentTasks(numTasks, currentUserId);
|
||||
} catch (SystemUiProxy.GetRecentTasksException e) {
|
||||
@@ -332,7 +335,7 @@ public class RecentTasksList {
|
||||
TaskLoadResult allTasks = new TaskLoadResult(requestId, loadKeysOnly, rawTasks.size());
|
||||
|
||||
int numVisibleTasks = 0;
|
||||
for (GroupedRecentTaskInfo rawTask : rawTasks) {
|
||||
for (GroupedTaskInfo rawTask : rawTasks) {
|
||||
if (rawTask.getType() == TYPE_FREEFORM) {
|
||||
// TYPE_FREEFORM tasks is only created when desktop mode can be entered,
|
||||
// leftover TYPE_FREEFORM tasks created when flag was on should be ignored.
|
||||
@@ -344,14 +347,13 @@ public class RecentTasksList {
|
||||
}
|
||||
continue;
|
||||
}
|
||||
ActivityManager.RecentTaskInfo taskInfo1 = rawTask.getTaskInfo1();
|
||||
ActivityManager.RecentTaskInfo taskInfo2 = rawTask.getTaskInfo2();
|
||||
TaskInfo taskInfo1 = rawTask.getTaskInfo1();
|
||||
TaskInfo taskInfo2 = rawTask.getTaskInfo2();
|
||||
Task.TaskKey task1Key = new Task.TaskKey(taskInfo1);
|
||||
Task task1 = loadKeysOnly
|
||||
? new Task(task1Key)
|
||||
: Task.from(task1Key, taskInfo1,
|
||||
tmpLockedUsers.get(task1Key.userId) /* isLocked */);
|
||||
task1.setLastSnapshotData(taskInfo1);
|
||||
Task task2 = null;
|
||||
if (taskInfo2 != null) {
|
||||
// Is split task
|
||||
@@ -360,7 +362,6 @@ public class RecentTasksList {
|
||||
? new Task(task2Key)
|
||||
: Task.from(task2Key, taskInfo2,
|
||||
tmpLockedUsers.get(task2Key.userId) /* isLocked */);
|
||||
task2.setLastSnapshotData(taskInfo2);
|
||||
} else {
|
||||
// Is fullscreen task
|
||||
if (numVisibleTasks > 0) {
|
||||
@@ -384,17 +385,16 @@ public class RecentTasksList {
|
||||
return allTasks;
|
||||
}
|
||||
|
||||
private @Nullable DesktopTask createDesktopTask(GroupedRecentTaskInfo recentTaskInfo) {
|
||||
private @Nullable DesktopTask createDesktopTask(GroupedTaskInfo recentTaskInfo) {
|
||||
ArrayList<Task> tasks = new ArrayList<>(recentTaskInfo.getTaskInfoList().size());
|
||||
int[] minimizedTaskIds = recentTaskInfo.getMinimizedTaskIds();
|
||||
if (minimizedTaskIds.length == recentTaskInfo.getTaskInfoList().size()) {
|
||||
// All Tasks are minimized -> don't create a DesktopTask
|
||||
return null;
|
||||
}
|
||||
for (ActivityManager.RecentTaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
|
||||
for (TaskInfo taskInfo : recentTaskInfo.getTaskInfoList()) {
|
||||
Task.TaskKey key = new Task.TaskKey(taskInfo);
|
||||
Task task = Task.from(key, taskInfo, false);
|
||||
task.setLastSnapshotData(taskInfo);
|
||||
task.positionInParent = taskInfo.positionInParent;
|
||||
task.appBounds = taskInfo.configuration.windowConfiguration.getAppBounds();
|
||||
task.isVisible = taskInfo.isVisible;
|
||||
@@ -429,14 +429,14 @@ public class RecentTasksList {
|
||||
}
|
||||
writer.println(prefix + " ]");
|
||||
int currentUserId = Process.myUserHandle().getIdentifier();
|
||||
ArrayList<GroupedRecentTaskInfo> rawTasks;
|
||||
ArrayList<GroupedTaskInfo> rawTasks;
|
||||
try {
|
||||
rawTasks = mSysUiProxy.getRecentTasks(Integer.MAX_VALUE, currentUserId);
|
||||
} catch (SystemUiProxy.GetRecentTasksException e) {
|
||||
rawTasks = new ArrayList<>();
|
||||
}
|
||||
writer.println(prefix + " rawTasks=[");
|
||||
for (GroupedRecentTaskInfo task : rawTasks) {
|
||||
for (GroupedTaskInfo task : rawTasks) {
|
||||
TaskInfo taskInfo1 = task.getTaskInfo1();
|
||||
TaskInfo taskInfo2 = task.getTaskInfo2();
|
||||
ComponentName cn1 = taskInfo1.topActivity;
|
||||
|
||||
@@ -91,7 +91,7 @@ import com.android.wm.shell.recents.IRecentTasks;
|
||||
import com.android.wm.shell.recents.IRecentTasksListener;
|
||||
import com.android.wm.shell.recents.IRecentsAnimationController;
|
||||
import com.android.wm.shell.recents.IRecentsAnimationRunner;
|
||||
import com.android.wm.shell.shared.GroupedRecentTaskInfo;
|
||||
import com.android.wm.shell.shared.GroupedTaskInfo;
|
||||
import com.android.wm.shell.shared.IShellTransitions;
|
||||
import com.android.wm.shell.shared.bubbles.BubbleBarLocation;
|
||||
import com.android.wm.shell.shared.desktopmode.DesktopModeStatus;
|
||||
@@ -1358,15 +1358,15 @@ public class SystemUiProxy implements ISystemUiProxy, NavHandle {
|
||||
* @throws GetRecentTasksException if IRecentTasks is not initialized, or when we get
|
||||
* RemoteException from server side
|
||||
*/
|
||||
public ArrayList<GroupedRecentTaskInfo> getRecentTasks(int numTasks, int userId)
|
||||
throws GetRecentTasksException {
|
||||
public ArrayList<GroupedTaskInfo> getRecentTasks(int numTasks,
|
||||
int userId) throws GetRecentTasksException {
|
||||
if (mRecentTasks == null) {
|
||||
Log.e(TAG, "getRecentTasks() failed due to null mRecentTasks");
|
||||
throw new GetRecentTasksException("null mRecentTasks");
|
||||
}
|
||||
try {
|
||||
final GroupedRecentTaskInfo[] rawTasks = mRecentTasks.getRecentTasks(numTasks,
|
||||
RECENT_IGNORE_UNAVAILABLE, userId);
|
||||
final GroupedTaskInfo[] rawTasks =
|
||||
mRecentTasks.getRecentTasks(numTasks, RECENT_IGNORE_UNAVAILABLE, userId);
|
||||
if (rawTasks == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@ import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITIO
|
||||
|
||||
import android.annotation.UserIdInt;
|
||||
import android.app.ActivityManager.RunningTaskInfo;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.Context;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -67,7 +68,7 @@ public class TopTaskTracker extends ISplitScreenListener.Stub
|
||||
private static final int HISTORY_SIZE = 5;
|
||||
|
||||
// Ordered list with first item being the most recent task.
|
||||
private final LinkedList<RunningTaskInfo> mOrderedTaskList = new LinkedList<>();
|
||||
private final LinkedList<TaskInfo> mOrderedTaskList = new LinkedList<>();
|
||||
|
||||
private final Context mContext;
|
||||
private final SplitStageInfo mMainStagePosition = new SplitStageInfo();
|
||||
@@ -96,6 +97,10 @@ public class TopTaskTracker extends ISplitScreenListener.Stub
|
||||
|
||||
@Override
|
||||
public void onTaskMovedToFront(RunningTaskInfo taskInfo) {
|
||||
handleTaskMovedToFront(taskInfo);
|
||||
}
|
||||
|
||||
public void handleTaskMovedToFront(TaskInfo taskInfo) {
|
||||
mOrderedTaskList.removeIf(rto -> rto.taskId == taskInfo.taskId);
|
||||
mOrderedTaskList.addFirst(taskInfo);
|
||||
|
||||
@@ -103,7 +108,7 @@ public class TopTaskTracker extends ISplitScreenListener.Stub
|
||||
// display's task to the list, to avoid showing non-home display's task upon going to
|
||||
// Recents animation.
|
||||
if (taskInfo.displayId != DEFAULT_DISPLAY) {
|
||||
final RunningTaskInfo topTaskOnHomeDisplay = mOrderedTaskList.stream()
|
||||
final TaskInfo topTaskOnHomeDisplay = mOrderedTaskList.stream()
|
||||
.filter(rto -> rto.displayId == DEFAULT_DISPLAY).findFirst().orElse(null);
|
||||
if (topTaskOnHomeDisplay != null) {
|
||||
mOrderedTaskList.removeIf(rto -> rto.taskId == topTaskOnHomeDisplay.taskId);
|
||||
@@ -113,9 +118,9 @@ public class TopTaskTracker extends ISplitScreenListener.Stub
|
||||
|
||||
if (mOrderedTaskList.size() >= HISTORY_SIZE) {
|
||||
// If we grow in size, remove the last taskInfo which is not part of the split task.
|
||||
Iterator<RunningTaskInfo> itr = mOrderedTaskList.descendingIterator();
|
||||
Iterator<TaskInfo> itr = mOrderedTaskList.descendingIterator();
|
||||
while (itr.hasNext()) {
|
||||
RunningTaskInfo info = itr.next();
|
||||
TaskInfo info = itr.next();
|
||||
if (info.taskId != taskInfo.taskId
|
||||
&& info.taskId != mMainStagePosition.taskId
|
||||
&& info.taskId != mSideStagePosition.taskId) {
|
||||
@@ -215,13 +220,13 @@ public class TopTaskTracker extends ISplitScreenListener.Stub
|
||||
Collections.addAll(mOrderedTaskList, tasks);
|
||||
}
|
||||
|
||||
ArrayList<RunningTaskInfo> tasks = new ArrayList<>(mOrderedTaskList);
|
||||
ArrayList<TaskInfo> tasks = new ArrayList<>(mOrderedTaskList);
|
||||
// Strip the pinned task and recents task
|
||||
tasks.removeIf(t -> t.taskId == mPinnedTaskId || isRecentsTask(t));
|
||||
return new CachedTaskInfo(tasks);
|
||||
}
|
||||
|
||||
private static boolean isRecentsTask(RunningTaskInfo task) {
|
||||
private static boolean isRecentsTask(TaskInfo task) {
|
||||
return task != null && task.configuration.windowConfiguration
|
||||
.getActivityType() == ACTIVITY_TYPE_RECENTS;
|
||||
}
|
||||
@@ -233,10 +238,10 @@ public class TopTaskTracker extends ISplitScreenListener.Stub
|
||||
public static class CachedTaskInfo {
|
||||
|
||||
@Nullable
|
||||
private final RunningTaskInfo mTopTask;
|
||||
public final List<RunningTaskInfo> mAllCachedTasks;
|
||||
private final TaskInfo mTopTask;
|
||||
public final List<TaskInfo> mAllCachedTasks;
|
||||
|
||||
CachedTaskInfo(List<RunningTaskInfo> allCachedTasks) {
|
||||
CachedTaskInfo(List<TaskInfo> allCachedTasks) {
|
||||
mAllCachedTasks = allCachedTasks;
|
||||
mTopTask = allCachedTasks.isEmpty() ? null : allCachedTasks.get(0);
|
||||
}
|
||||
@@ -262,7 +267,7 @@ public class TopTaskTracker extends ISplitScreenListener.Stub
|
||||
// Not an excluded task.
|
||||
return null;
|
||||
}
|
||||
List<RunningTaskInfo> visibleNonExcludedTasks = mAllCachedTasks.stream()
|
||||
List<TaskInfo> visibleNonExcludedTasks = mAllCachedTasks.stream()
|
||||
.filter(t -> t.isVisible
|
||||
&& (t.baseIntent.getFlags() & FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS) == 0
|
||||
&& t.getActivityType() != ACTIVITY_TYPE_HOME
|
||||
|
||||
@@ -73,13 +73,17 @@ class TasksRepository(
|
||||
getTaskDataById(taskId).map { it?.thumbnail }.distinctUntilChangedBy { it?.snapshotId }
|
||||
|
||||
override fun setVisibleTasks(visibleTaskIdList: Set<Int>) {
|
||||
Log.d(TAG, "setVisibleTasks: $visibleTaskIdList")
|
||||
|
||||
// Remove tasks are no longer visible
|
||||
val tasksNoLongerVisible = taskRequests.keys.subtract(visibleTaskIdList)
|
||||
removeTasks(tasksNoLongerVisible)
|
||||
// Add new tasks to be requested
|
||||
visibleTaskIdList.subtract(taskRequests.keys).forEach { taskId -> requestTaskData(taskId) }
|
||||
val newlyVisibleTasks = visibleTaskIdList.subtract(taskRequests.keys)
|
||||
newlyVisibleTasks.forEach { taskId -> requestTaskData(taskId) }
|
||||
|
||||
if (tasksNoLongerVisible.isNotEmpty() || newlyVisibleTasks.isNotEmpty()) {
|
||||
Log.d(TAG, "setVisibleTasks to: $visibleTaskIdList, " +
|
||||
"removed: $tasksNoLongerVisible, added: $newlyVisibleTasks")
|
||||
}
|
||||
}
|
||||
|
||||
private fun requestTaskData(taskId: Int) {
|
||||
|
||||
@@ -28,7 +28,9 @@ import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
import android.app.ActivityManager;
|
||||
import android.app.ActivityManager.RecentTaskInfo;
|
||||
import android.app.KeyguardManager;
|
||||
import android.app.TaskInfo;
|
||||
import android.content.Context;
|
||||
import android.content.res.Resources;
|
||||
|
||||
@@ -39,7 +41,7 @@ import com.android.launcher3.util.LooperExecutor;
|
||||
import com.android.quickstep.util.GroupTask;
|
||||
import com.android.quickstep.views.TaskViewType;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.wm.shell.shared.GroupedRecentTaskInfo;
|
||||
import com.android.wm.shell.shared.GroupedTaskInfo;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
@@ -91,8 +93,8 @@ public class RecentTasksListTest {
|
||||
|
||||
@Test
|
||||
public void loadTasksInBackground_onlyKeys_noValidTaskDescription() throws Exception {
|
||||
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(
|
||||
new ActivityManager.RecentTaskInfo(), new ActivityManager.RecentTaskInfo(), null);
|
||||
GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forSplitTasks(
|
||||
new RecentTaskInfo(), new RecentTaskInfo(), null);
|
||||
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
|
||||
|
||||
@@ -119,12 +121,11 @@ public class RecentTasksListTest {
|
||||
@Test
|
||||
public void loadTasksInBackground_moreThanKeys_hasValidTaskDescription() throws Exception {
|
||||
String taskDescription = "Wheeee!";
|
||||
ActivityManager.RecentTaskInfo task1 = new ActivityManager.RecentTaskInfo();
|
||||
RecentTaskInfo task1 = new RecentTaskInfo();
|
||||
task1.taskDescription = new ActivityManager.TaskDescription(taskDescription);
|
||||
ActivityManager.RecentTaskInfo task2 = new ActivityManager.RecentTaskInfo();
|
||||
RecentTaskInfo task2 = new RecentTaskInfo();
|
||||
task2.taskDescription = new ActivityManager.TaskDescription();
|
||||
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forSplitTasks(task1, task2,
|
||||
null);
|
||||
GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forSplitTasks(task1, task2, null);
|
||||
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
|
||||
|
||||
@@ -138,11 +139,11 @@ public class RecentTasksListTest {
|
||||
|
||||
@Test
|
||||
public void loadTasksInBackground_freeformTask_createsDesktopTask() throws Exception {
|
||||
ActivityManager.RecentTaskInfo[] tasks = {
|
||||
List<TaskInfo> tasks = Arrays.asList(
|
||||
createRecentTaskInfo(1 /* taskId */),
|
||||
createRecentTaskInfo(4 /* taskId */),
|
||||
createRecentTaskInfo(5 /* taskId */)};
|
||||
GroupedRecentTaskInfo recentTaskInfos = GroupedRecentTaskInfo.forFreeformTasks(
|
||||
createRecentTaskInfo(5 /* taskId */));
|
||||
GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forFreeformTasks(
|
||||
tasks, Collections.emptySet() /* minimizedTaskIds */);
|
||||
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
|
||||
@@ -162,14 +163,13 @@ public class RecentTasksListTest {
|
||||
@Test
|
||||
public void loadTasksInBackground_freeformTask_onlyMinimizedTasks_doesNotCreateDesktopTask()
|
||||
throws Exception {
|
||||
ActivityManager.RecentTaskInfo[] tasks = {
|
||||
List<TaskInfo> tasks = Arrays.asList(
|
||||
createRecentTaskInfo(1 /* taskId */),
|
||||
createRecentTaskInfo(4 /* taskId */),
|
||||
createRecentTaskInfo(5 /* taskId */)};
|
||||
createRecentTaskInfo(5 /* taskId */));
|
||||
Set<Integer> minimizedTaskIds =
|
||||
Arrays.stream(new Integer[]{1, 4, 5}).collect(Collectors.toSet());
|
||||
GroupedRecentTaskInfo recentTaskInfos =
|
||||
GroupedRecentTaskInfo.forFreeformTasks(tasks, minimizedTaskIds);
|
||||
GroupedTaskInfo recentTaskInfos = GroupedTaskInfo.forFreeformTasks(tasks, minimizedTaskIds);
|
||||
when(mSystemUiProxy.getRecentTasks(anyInt(), anyInt()))
|
||||
.thenReturn(new ArrayList<>(Collections.singletonList(recentTaskInfos)));
|
||||
|
||||
@@ -179,8 +179,8 @@ public class RecentTasksListTest {
|
||||
assertEquals(0, taskList.size());
|
||||
}
|
||||
|
||||
private ActivityManager.RecentTaskInfo createRecentTaskInfo(int taskId) {
|
||||
ActivityManager.RecentTaskInfo recentTaskInfo = new ActivityManager.RecentTaskInfo();
|
||||
private TaskInfo createRecentTaskInfo(int taskId) {
|
||||
RecentTaskInfo recentTaskInfo = new RecentTaskInfo();
|
||||
recentTaskInfo.taskId = taskId;
|
||||
return recentTaskInfo;
|
||||
}
|
||||
|
||||
@@ -85,6 +85,9 @@
|
||||
<string name="local_colors_extraction_class" translatable="false"></string>
|
||||
<string name="search_session_manager_class" translatable="false"></string>
|
||||
|
||||
<!-- Filters for widgets displayed in the widget picker -->
|
||||
<string name="widgets_filter_data_provider_class" translatable="false"></string>
|
||||
|
||||
<!-- Scalable Grid configuration -->
|
||||
<!-- This is a float because it is converted to dp later in DeviceProfile -->
|
||||
<dimen name="hotseat_bar_bottom_space_default">48</dimen>
|
||||
|
||||
@@ -2629,8 +2629,9 @@ public class Launcher extends StatefulActivity<LauncherState>
|
||||
* See {@code LauncherBindingDelegate}
|
||||
*/
|
||||
@Override
|
||||
public void bindAllWidgets(final List<WidgetsListBaseEntry> allWidgets) {
|
||||
mModelCallbacks.bindAllWidgets(allWidgets);
|
||||
public void bindAllWidgets(@NonNull final List<WidgetsListBaseEntry> allWidgets,
|
||||
@NonNull final List<WidgetsListBaseEntry> defaultWidgets) {
|
||||
mModelCallbacks.bindAllWidgets(allWidgets, defaultWidgets);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -48,6 +48,7 @@ import com.android.launcher3.icons.IconProvider;
|
||||
import com.android.launcher3.icons.LauncherIconProvider;
|
||||
import com.android.launcher3.icons.LauncherIcons;
|
||||
import com.android.launcher3.model.ModelLauncherCallbacks;
|
||||
import com.android.launcher3.model.WidgetsFilterDataProvider;
|
||||
import com.android.launcher3.notification.NotificationListener;
|
||||
import com.android.launcher3.pm.InstallSessionHelper;
|
||||
import com.android.launcher3.pm.InstallSessionTracker;
|
||||
@@ -197,7 +198,8 @@ public class LauncherAppState implements SafeCloseable {
|
||||
mIconProvider = new LauncherIconProvider(context);
|
||||
mIconCache = new IconCache(mContext, mInvariantDeviceProfile,
|
||||
iconCacheFileName, mIconProvider);
|
||||
mModel = new LauncherModel(context, this, mIconCache, new AppFilter(mContext),
|
||||
mModel = new LauncherModel(context, this, mIconCache,
|
||||
WidgetsFilterDataProvider.Companion.newInstance(context), new AppFilter(mContext),
|
||||
PackageManagerHelper.INSTANCE.get(context), iconCacheFileName != null);
|
||||
mOnTerminateCallback.add(mIconCache::close);
|
||||
mOnTerminateCallback.add(mModel::destroy);
|
||||
|
||||
@@ -42,6 +42,7 @@ import com.android.launcher3.model.PackageUpdatedTask
|
||||
import com.android.launcher3.model.ReloadStringCacheTask
|
||||
import com.android.launcher3.model.ShortcutsChangedTask
|
||||
import com.android.launcher3.model.UserLockStateChangedTask
|
||||
import com.android.launcher3.model.WidgetsFilterDataProvider
|
||||
import com.android.launcher3.model.data.ItemInfo
|
||||
import com.android.launcher3.model.data.WorkspaceItemInfo
|
||||
import com.android.launcher3.pm.UserCache
|
||||
@@ -66,6 +67,7 @@ class LauncherModel(
|
||||
private val context: Context,
|
||||
private val mApp: LauncherAppState,
|
||||
private val iconCache: IconCache,
|
||||
private val widgetsFilterDataProvider: WidgetsFilterDataProvider,
|
||||
appFilter: AppFilter,
|
||||
mPmHelper: PackageManagerHelper,
|
||||
isPrimaryInstance: Boolean,
|
||||
@@ -140,6 +142,11 @@ class LauncherModel(
|
||||
owner: BgDataModel.Callbacks?,
|
||||
) = ModelWriter(mApp.context, this, mBgDataModel, verifyChanges, cellPosMapper, owner)
|
||||
|
||||
/** Returns the [WidgetsFilterDataProvider] that manages widget filters. */
|
||||
fun getWidgetsFilterDataProvider(): WidgetsFilterDataProvider {
|
||||
return widgetsFilterDataProvider
|
||||
}
|
||||
|
||||
/** Called when the icon for an app changes, outside of package event */
|
||||
@WorkerThread
|
||||
fun onAppIconChanged(packageName: String, user: UserHandle) {
|
||||
@@ -160,7 +167,10 @@ class LauncherModel(
|
||||
/** Called when the model is destroyed */
|
||||
fun destroy() {
|
||||
mModelDestroyed = true
|
||||
MODEL_EXECUTOR.execute(modelDelegate::destroy)
|
||||
MODEL_EXECUTOR.execute {
|
||||
modelDelegate.destroy()
|
||||
widgetsFilterDataProvider.destroy()
|
||||
}
|
||||
}
|
||||
|
||||
fun onBroadcastIntent(intent: Intent) {
|
||||
@@ -312,6 +322,7 @@ class LauncherModel(
|
||||
mBgDataModel,
|
||||
this.modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
)
|
||||
|
||||
// Always post the loader task, instead of running directly
|
||||
@@ -417,6 +428,14 @@ class LauncherModel(
|
||||
}
|
||||
}
|
||||
|
||||
/** Called when the widget filters are refreshed and available to bind to the model. */
|
||||
fun onWidgetFiltersLoaded() {
|
||||
enqueueModelUpdateTask { taskController, dataModel, _ ->
|
||||
dataModel.widgetsModel.updateWidgetFilters(widgetsFilterDataProvider)
|
||||
taskController.bindUpdatedWidgets(dataModel)
|
||||
}
|
||||
}
|
||||
|
||||
fun enqueueModelUpdateTask(task: ModelUpdateTask) {
|
||||
if (mModelDestroyed) {
|
||||
return
|
||||
|
||||
@@ -252,8 +252,11 @@ class ModelCallbacks(private var launcher: Launcher) : BgDataModel.Callbacks {
|
||||
PopupContainerWithArrow.dismissInvalidPopup(launcher)
|
||||
}
|
||||
|
||||
override fun bindAllWidgets(allWidgets: List<WidgetsListBaseEntry>) {
|
||||
launcher.widgetPickerDataProvider.setWidgets(allWidgets, /* defaultWidgets= */ listOf())
|
||||
override fun bindAllWidgets(
|
||||
allWidgets: List<WidgetsListBaseEntry>,
|
||||
defaultWidgets: List<WidgetsListBaseEntry>,
|
||||
) {
|
||||
launcher.widgetPickerDataProvider.setWidgets(allWidgets, defaultWidgets)
|
||||
}
|
||||
|
||||
/** Returns the ids of the workspaces to bind. */
|
||||
|
||||
@@ -26,6 +26,7 @@ import com.android.launcher3.util.PackageManagerHelper;
|
||||
import com.android.launcher3.util.PluginManagerWrapper;
|
||||
import com.android.launcher3.util.ScreenOnTracker;
|
||||
import com.android.launcher3.util.SettingsCache;
|
||||
import com.android.launcher3.util.VibratorWrapper;
|
||||
import com.android.launcher3.util.window.RefreshRateTracker;
|
||||
import com.android.launcher3.widget.custom.CustomWidgetManager;
|
||||
|
||||
@@ -48,8 +49,9 @@ public interface LauncherBaseAppComponent {
|
||||
RefreshRateTracker getRefreshRateTracker();
|
||||
ScreenOnTracker getScreenOnTracker();
|
||||
SettingsCache getSettingsCache();
|
||||
PluginManagerWrapper getPluginManagerWrapper();
|
||||
PackageManagerHelper getPackageManagerHelper();
|
||||
PluginManagerWrapper getPluginManagerWrapper();
|
||||
VibratorWrapper getVibratorWrapper();
|
||||
|
||||
/** Builder for LauncherBaseAppComponent. */
|
||||
interface Builder {
|
||||
|
||||
@@ -305,7 +305,9 @@ public class PreviewSurfaceRenderer {
|
||||
bgModel,
|
||||
LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
|
||||
new BaseLauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
|
||||
/* bgAllAppsList= */ null, new Callbacks[0])) {
|
||||
/* bgAllAppsList= */ null, new Callbacks[0]),
|
||||
LauncherAppState.getInstance(
|
||||
previewContext).getModel().getWidgetsFilterDataProvider()) {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
|
||||
@@ -24,6 +24,8 @@ import static com.android.launcher3.model.ModelUtils.filterCurrentWorkspaceItems
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
import android.os.Process;
|
||||
import android.os.Trace;
|
||||
import android.util.Log;
|
||||
@@ -43,6 +45,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.LauncherAppWidgetInfo;
|
||||
import com.android.launcher3.model.data.PackageItemInfo;
|
||||
import com.android.launcher3.util.ComponentKey;
|
||||
import com.android.launcher3.util.IntArray;
|
||||
import com.android.launcher3.util.IntSet;
|
||||
@@ -62,6 +65,7 @@ import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.function.Predicate;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
@@ -162,9 +166,17 @@ public class BaseLauncherBinder {
|
||||
if (!WIDGETS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
Map<PackageItemInfo, List<WidgetItem>>
|
||||
widgetsByPackageItem = mBgDataModel.widgetsModel.getWidgetsByPackageItem();
|
||||
List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mApp.getContext())
|
||||
.build(mBgDataModel.widgetsModel.getWidgetsByPackageItem());
|
||||
executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
|
||||
.build(widgetsByPackageItem);
|
||||
Predicate<WidgetItem> filter = mBgDataModel.widgetsModel.getDefaultWidgetsFilter();
|
||||
List<WidgetsListBaseEntry> defaultWidgets =
|
||||
filter != null ? new WidgetsListBaseEntriesBuilder(
|
||||
mApp.getContext()).build(widgetsByPackageItem,
|
||||
mBgDataModel.widgetsModel.getDefaultWidgetsFilter()) : emptyList();
|
||||
|
||||
executeCallbacksTask(c -> c.bindAllWidgets(widgets, defaultWidgets), mUiExecutor);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -537,7 +537,13 @@ public class BgDataModel {
|
||||
default void bindWidgetsRestored(ArrayList<LauncherAppWidgetInfo> widgets) { }
|
||||
default void bindRestoreItemsChange(HashSet<ItemInfo> updates) { }
|
||||
default void bindWorkspaceComponentsRemoved(Predicate<ItemInfo> matcher) { }
|
||||
default void bindAllWidgets(List<WidgetsListBaseEntry> widgets) { }
|
||||
|
||||
/**
|
||||
* Binds the app widgets to the providers that share widgets with the UI.
|
||||
*/
|
||||
default void bindAllWidgets(@NonNull List<WidgetsListBaseEntry> widgets,
|
||||
@NonNull List<WidgetsListBaseEntry> defaultWidgets) {
|
||||
}
|
||||
default void bindSmartspaceWidget() { }
|
||||
|
||||
/** Called when workspace has been bound. */
|
||||
|
||||
@@ -20,6 +20,7 @@ import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
|
||||
import static com.android.launcher3.Flags.enableLauncherBrMetricsFixed;
|
||||
import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
|
||||
import static com.android.launcher3.Flags.enableSmartspaceRemovalToggle;
|
||||
import static com.android.launcher3.Flags.enableTieredWidgetsByDefaultInPicker;
|
||||
import static com.android.launcher3.LauncherPrefs.IS_FIRST_LOAD_AFTER_RESTORE;
|
||||
import static com.android.launcher3.LauncherPrefs.SHOULD_SHOW_SMARTSPACE;
|
||||
import static com.android.launcher3.LauncherSettings.Favorites.TABLE_NAME;
|
||||
@@ -142,6 +143,7 @@ public class LoaderTask implements Runnable {
|
||||
private final UserManager mUserManager;
|
||||
private final UserCache mUserCache;
|
||||
private final PackageManagerHelper mPmHelper;
|
||||
private final WidgetsFilterDataProvider mWidgetsFilterDataProvider;
|
||||
|
||||
private final InstallSessionHelper mSessionHelper;
|
||||
private final IconCache mIconCache;
|
||||
@@ -158,13 +160,16 @@ public class LoaderTask implements Runnable {
|
||||
private String mDbName;
|
||||
|
||||
public LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
|
||||
ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder) {
|
||||
this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, new UserManagerState());
|
||||
ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
|
||||
@NonNull WidgetsFilterDataProvider widgetsFilterDataProvider) {
|
||||
this(app, bgAllAppsList, bgModel, modelDelegate, launcherBinder, widgetsFilterDataProvider,
|
||||
new UserManagerState());
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
|
||||
ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
|
||||
WidgetsFilterDataProvider widgetsFilterDataProvider,
|
||||
UserManagerState userManagerState) {
|
||||
mApp = app;
|
||||
mBgAllAppsList = bgAllAppsList;
|
||||
@@ -179,6 +184,7 @@ public class LoaderTask implements Runnable {
|
||||
mIconCache = mApp.getIconCache();
|
||||
mUserManagerState = userManagerState;
|
||||
mInstallingPkgsCached = null;
|
||||
mWidgetsFilterDataProvider = widgetsFilterDataProvider;
|
||||
}
|
||||
|
||||
protected synchronized void waitForIdle() {
|
||||
@@ -330,7 +336,15 @@ public class LoaderTask implements Runnable {
|
||||
verifyNotStopped();
|
||||
|
||||
// fourth step
|
||||
List<CachedObject> allWidgetsList = mBgDataModel.widgetsModel.update(mApp, null);
|
||||
WidgetsModel widgetsModel = mBgDataModel.widgetsModel;
|
||||
if (enableTieredWidgetsByDefaultInPicker()) {
|
||||
// Begin periodic refresh of filters
|
||||
mWidgetsFilterDataProvider.initPeriodicDataRefresh(
|
||||
mApp.getModel()::onWidgetFiltersLoaded);
|
||||
// And, update model with currently cached data.
|
||||
widgetsModel.updateWidgetFilters(mWidgetsFilterDataProvider);
|
||||
}
|
||||
List<CachedObject> allWidgetsList = widgetsModel.update(mApp, /*packageUser=*/null);
|
||||
logASplit("load widgets");
|
||||
|
||||
verifyNotStopped();
|
||||
|
||||
@@ -35,7 +35,7 @@ class ModelTaskController(
|
||||
val dataModel: BgDataModel,
|
||||
val allAppsList: AllAppsList,
|
||||
private val model: LauncherModel,
|
||||
private val uiExecutor: Executor
|
||||
private val uiExecutor: Executor,
|
||||
) {
|
||||
|
||||
/** Schedules a {@param task} to be executed on the current callbacks. */
|
||||
@@ -79,10 +79,19 @@ class ModelTaskController(
|
||||
}
|
||||
|
||||
fun bindUpdatedWidgets(dataModel: BgDataModel) {
|
||||
val widgets =
|
||||
WidgetsListBaseEntriesBuilder(app.context)
|
||||
.build(dataModel.widgetsModel.widgetsByPackageItem)
|
||||
scheduleCallbackTask { it.bindAllWidgets(widgets) }
|
||||
val widgetsByPackageItem = dataModel.widgetsModel.widgetsByPackageItem
|
||||
val allWidgets = WidgetsListBaseEntriesBuilder(app.context).build(widgetsByPackageItem)
|
||||
|
||||
val defaultWidgetsFilter = dataModel.widgetsModel.defaultWidgetsFilter
|
||||
val defaultWidgets =
|
||||
if (defaultWidgetsFilter != null) {
|
||||
WidgetsListBaseEntriesBuilder(app.context)
|
||||
.build(widgetsByPackageItem, defaultWidgetsFilter)
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
|
||||
scheduleCallbackTask { it.bindAllWidgets(allWidgets, defaultWidgets) }
|
||||
}
|
||||
|
||||
fun deleteAndBindComponentsRemoved(matcher: Predicate<ItemInfo?>, reason: String?) {
|
||||
@@ -99,7 +108,7 @@ class ModelTaskController(
|
||||
val packageUserKeyToUidMap =
|
||||
apps.associateBy(
|
||||
keySelector = { PackageUserKey(it.componentName!!.packageName, it.user) },
|
||||
valueTransform = { it.uid }
|
||||
valueTransform = { it.uid },
|
||||
)
|
||||
scheduleCallbackTask { it.bindAllApplications(apps, flags, packageUserKeyToUidMap) }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* 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.model
|
||||
|
||||
import android.content.Context
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.util.ResourceBasedOverride
|
||||
import java.util.function.Predicate
|
||||
|
||||
/** Helper for the widgets model to load the filters that can be applied to available widgets. */
|
||||
open class WidgetsFilterDataProvider(val context: Context) : ResourceBasedOverride {
|
||||
/**
|
||||
* Start regular periodic refresh of widget filtering data starting now (if not started
|
||||
* already).
|
||||
*/
|
||||
@WorkerThread
|
||||
open fun initPeriodicDataRefresh(callback: WidgetsFilterLoadedCallback? = null) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a filter that should be applied to the widget predictions.
|
||||
*
|
||||
* @return null if no filter needs to be applied
|
||||
*/
|
||||
@WorkerThread open fun getPredictedWidgetsFilter(): Predicate<WidgetItem>? = null
|
||||
|
||||
/**
|
||||
* Returns a filter that should be applied to the widgets list to see which widgets can be shown
|
||||
* by default.
|
||||
*
|
||||
* @return null if no separate "default" list is supported
|
||||
*/
|
||||
@WorkerThread open fun getDefaultWidgetsFilter(): Predicate<WidgetItem>? = null
|
||||
|
||||
/** Called when filter data provider is no longer needed. */
|
||||
open fun destroy() {}
|
||||
|
||||
companion object {
|
||||
/** Returns a new instance of the [WidgetsFilterDataProvider] based on resource override. */
|
||||
fun newInstance(context: Context?): WidgetsFilterDataProvider {
|
||||
return ResourceBasedOverride.Overrides.getObject(
|
||||
WidgetsFilterDataProvider::class.java,
|
||||
context,
|
||||
R.string.widgets_filter_data_provider_class,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Interface for the model callback to be invoked when filters are loaded. */
|
||||
interface WidgetsFilterLoadedCallback {
|
||||
/** Method called back when widget filters are loaded */
|
||||
fun onWidgetsFilterLoaded()
|
||||
}
|
||||
@@ -18,6 +18,8 @@ import android.os.UserHandle;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import androidx.annotation.AnyThread;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.collection.ArrayMap;
|
||||
|
||||
@@ -65,6 +67,8 @@ public class WidgetsModel {
|
||||
|
||||
/* Map of widgets and shortcuts that are tracked per package. */
|
||||
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsByPackageItem = new HashMap<>();
|
||||
@Nullable private Predicate<WidgetItem> mDefaultWidgetsFilter = null;
|
||||
@Nullable private Predicate<WidgetItem> mPredictedWidgetsFilter = null;
|
||||
|
||||
/**
|
||||
* Returns all widgets keyed by their component key.
|
||||
@@ -91,6 +95,37 @@ public class WidgetsModel {
|
||||
return new HashMap<>(mWidgetsByPackageItem);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns widget filter that can be applied to {@link WidgetItem}s to check if they can be
|
||||
* shown in the default widgets list.
|
||||
* <p>Returns null if filtering isn't available</p>
|
||||
*/
|
||||
@AnyThread
|
||||
public @Nullable Predicate<WidgetItem> getDefaultWidgetsFilter() {
|
||||
return mDefaultWidgetsFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns widget filter that can be applied to {@link WidgetItem}s to check if they can be
|
||||
* part of widget predictions.
|
||||
* <p>Returns null if filter isn't available</p>
|
||||
*/
|
||||
@AnyThread
|
||||
public @Nullable Predicate<WidgetItem> getPredictedWidgetsFilter() {
|
||||
return mPredictedWidgetsFilter;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates model with latest filter data in cache.
|
||||
*/
|
||||
public void updateWidgetFilters(@NonNull WidgetsFilterDataProvider widgetsFilterDataProvider) {
|
||||
if (!WIDGETS_ENABLED) {
|
||||
return;
|
||||
}
|
||||
mDefaultWidgetsFilter = widgetsFilterDataProvider.getDefaultWidgetsFilter();
|
||||
mPredictedWidgetsFilter = widgetsFilterDataProvider.getPredictedWidgetsFilter();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
|
||||
* only widgets and shortcuts associated with the package/user are.
|
||||
@@ -299,7 +334,7 @@ public class WidgetsModel {
|
||||
if (pInfo == null) {
|
||||
pInfo = new PackageItemInfo(key.mPackageName, key.mWidgetCategory, key.mUser);
|
||||
pInfo.user = key.mUser;
|
||||
mMap.put(key, pInfo);
|
||||
mMap.put(key, pInfo);
|
||||
}
|
||||
return pInfo;
|
||||
}
|
||||
|
||||
@@ -28,6 +28,8 @@ import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.UiThread;
|
||||
|
||||
import com.android.launcher3.dagger.ApplicationContext;
|
||||
import com.android.launcher3.dagger.LauncherAppSingleton;
|
||||
import com.android.launcher3.dagger.LauncherBaseAppComponent;
|
||||
@@ -140,7 +142,9 @@ public class SettingsCache extends ContentObserver implements SafeCloseable {
|
||||
* Does not de-dupe if you add same listeners for the same key multiple times.
|
||||
* Unregister once complete using {@link #unregister(Uri, OnChangeListener)}
|
||||
*/
|
||||
@UiThread
|
||||
public void register(Uri uri, OnChangeListener changeListener) {
|
||||
Preconditions.assertUIThread();
|
||||
if (mListenerMap.containsKey(uri)) {
|
||||
mListenerMap.get(uri).add(changeListener);
|
||||
} else {
|
||||
|
||||
@@ -19,6 +19,7 @@ import static android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK;
|
||||
import static android.os.VibrationEffect.createPredefined;
|
||||
import static android.provider.Settings.System.HAPTIC_FEEDBACK_ENABLED;
|
||||
|
||||
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
|
||||
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
@@ -31,13 +32,20 @@ import android.provider.Settings;
|
||||
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
|
||||
import com.android.launcher3.dagger.ApplicationContext;
|
||||
import com.android.launcher3.dagger.LauncherAppSingleton;
|
||||
import com.android.launcher3.dagger.LauncherBaseAppComponent;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
/**
|
||||
* Wrapper around {@link Vibrator} to easily perform haptic feedback where necessary.
|
||||
*/
|
||||
public class VibratorWrapper implements SafeCloseable {
|
||||
@LauncherAppSingleton
|
||||
public class VibratorWrapper {
|
||||
|
||||
public static final MainThreadInitializedObject<VibratorWrapper> INSTANCE =
|
||||
new MainThreadInitializedObject<>(VibratorWrapper::new);
|
||||
public static final DaggerSingletonObject<VibratorWrapper> INSTANCE =
|
||||
new DaggerSingletonObject<>(LauncherBaseAppComponent::getVibratorWrapper);
|
||||
|
||||
public static final AudioAttributes VIBRATION_ATTRS = new AudioAttributes.Builder()
|
||||
.setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
|
||||
@@ -59,38 +67,29 @@ public class VibratorWrapper implements SafeCloseable {
|
||||
private final Vibrator mVibrator;
|
||||
private final boolean mHasVibrator;
|
||||
|
||||
private final SettingsCache mSettingsCache;
|
||||
|
||||
@VisibleForTesting
|
||||
final SettingsCache.OnChangeListener mHapticChangeListener =
|
||||
isEnabled -> mIsHapticFeedbackEnabled = isEnabled;
|
||||
|
||||
private boolean mIsHapticFeedbackEnabled;
|
||||
|
||||
private VibratorWrapper(Context context) {
|
||||
this(context.getSystemService(Vibrator.class), SettingsCache.INSTANCE.get(context));
|
||||
}
|
||||
@Inject
|
||||
public VibratorWrapper(@ApplicationContext Context context, SettingsCache settingsCache,
|
||||
DaggerSingletonTracker tracker) {
|
||||
|
||||
@VisibleForTesting
|
||||
VibratorWrapper(Vibrator vibrator, SettingsCache settingsCache) {
|
||||
mVibrator = vibrator;
|
||||
mVibrator = context.getSystemService(Vibrator.class);
|
||||
mHasVibrator = mVibrator.hasVibrator();
|
||||
mSettingsCache = settingsCache;
|
||||
if (mHasVibrator) {
|
||||
mSettingsCache.register(HAPTIC_FEEDBACK_URI, mHapticChangeListener);
|
||||
mIsHapticFeedbackEnabled = mSettingsCache.getValue(HAPTIC_FEEDBACK_URI, 0);
|
||||
MAIN_EXECUTOR.execute(
|
||||
() -> settingsCache.register(HAPTIC_FEEDBACK_URI, mHapticChangeListener));
|
||||
mIsHapticFeedbackEnabled = settingsCache.getValue(HAPTIC_FEEDBACK_URI, 0);
|
||||
tracker.addCloseable(
|
||||
() -> settingsCache.unregister(HAPTIC_FEEDBACK_URI, mHapticChangeListener));
|
||||
} else {
|
||||
mIsHapticFeedbackEnabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() {
|
||||
if (mHasVibrator) {
|
||||
mSettingsCache.unregister(HAPTIC_FEEDBACK_URI, mHapticChangeListener);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This should be used to cancel a haptic in case where the haptic shouldn't be vibrating. For
|
||||
* example, when no animation is happening but a vibrator happens to be vibrating still.
|
||||
|
||||
@@ -43,6 +43,7 @@ import com.android.launcher3.widget.WidgetSections.NO_CATEGORY
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.concurrent.TimeUnit
|
||||
import java.util.function.Predicate
|
||||
import org.junit.Assert.fail
|
||||
import org.junit.Before
|
||||
import org.junit.Rule
|
||||
@@ -64,6 +65,7 @@ class WidgetsModelTest {
|
||||
@Mock private lateinit var appWidgetManager: AppWidgetManager
|
||||
@Mock private lateinit var app: LauncherAppState
|
||||
@Mock private lateinit var iconCacheMock: IconCache
|
||||
@Mock private lateinit var widgetsFilterDataProvider: WidgetsFilterDataProvider
|
||||
|
||||
private lateinit var context: Context
|
||||
private lateinit var idp: InvariantDeviceProfile
|
||||
@@ -215,6 +217,27 @@ class WidgetsModelTest {
|
||||
// No exception
|
||||
}
|
||||
|
||||
@Test
|
||||
fun updateWidgetFilters_setsFiltersCorrectly() {
|
||||
val testDefaultWidgetFilter = Predicate<WidgetItem> { w -> w.widgetInfo != null }
|
||||
whenever(widgetsFilterDataProvider.getDefaultWidgetsFilter())
|
||||
.thenReturn(testDefaultWidgetFilter)
|
||||
val testPredicatedWidgetFilter = Predicate<WidgetItem> { w -> w.widgetInfo != null }
|
||||
whenever(widgetsFilterDataProvider.getPredictedWidgetsFilter())
|
||||
.thenReturn(testPredicatedWidgetFilter)
|
||||
|
||||
underTest.updateWidgetFilters(widgetsFilterDataProvider)
|
||||
|
||||
assertThat(underTest.defaultWidgetsFilter).isEqualTo(testDefaultWidgetFilter)
|
||||
assertThat(underTest.predictedWidgetsFilter).isEqualTo(testPredicatedWidgetFilter)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun widgetFilters_nullInitially() {
|
||||
assertThat(underTest.defaultWidgetsFilter).isNull()
|
||||
assertThat(underTest.predictedWidgetsFilter).isNull()
|
||||
}
|
||||
|
||||
private fun loadWidgets() {
|
||||
val latch = CountDownLatch(1)
|
||||
Executors.MODEL_EXECUTOR.execute {
|
||||
|
||||
@@ -21,8 +21,8 @@ import android.os.VibrationEffect
|
||||
import android.os.VibrationEffect.Composition.PRIMITIVE_LOW_TICK
|
||||
import android.os.VibrationEffect.Composition.PRIMITIVE_TICK
|
||||
import android.os.Vibrator
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
|
||||
import com.android.launcher3.util.VibratorWrapper.HAPTIC_FEEDBACK_URI
|
||||
import com.android.launcher3.util.VibratorWrapper.OVERVIEW_HAPTIC
|
||||
import com.android.launcher3.util.VibratorWrapper.VIBRATION_ATTRS
|
||||
@@ -41,25 +41,27 @@ import org.mockito.kotlin.never
|
||||
import org.mockito.kotlin.same
|
||||
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@RunWith(LauncherMultivalentJUnit::class)
|
||||
class VibratorWrapperTest {
|
||||
|
||||
@Mock private lateinit var settingsCache: SettingsCache
|
||||
@Mock private lateinit var vibrator: Vibrator
|
||||
private lateinit var vibrator: Vibrator
|
||||
private val context: SandboxModelContext = SandboxModelContext()
|
||||
@Captor private lateinit var vibrationEffectCaptor: ArgumentCaptor<VibrationEffect>
|
||||
|
||||
@Mock private lateinit var tracker: DaggerSingletonTracker
|
||||
private lateinit var underTest: VibratorWrapper
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
vibrator = context.spyService(Vibrator::class.java)
|
||||
`when`(settingsCache.getValue(HAPTIC_FEEDBACK_URI, 0)).thenReturn(true)
|
||||
`when`(vibrator.hasVibrator()).thenReturn(true)
|
||||
`when`(vibrator.areAllPrimitivesSupported(PRIMITIVE_TICK)).thenReturn(true)
|
||||
`when`(vibrator.areAllPrimitivesSupported(PRIMITIVE_LOW_TICK)).thenReturn(true)
|
||||
`when`(vibrator.getPrimitiveDurations(PRIMITIVE_LOW_TICK)).thenReturn(intArrayOf(10))
|
||||
|
||||
underTest = VibratorWrapper(vibrator, settingsCache)
|
||||
underTest = VibratorWrapper(context, settingsCache, tracker)
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -67,13 +69,6 @@ class VibratorWrapperTest {
|
||||
verify(settingsCache).register(HAPTIC_FEEDBACK_URI, underTest.mHapticChangeListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun close_unregister_onChangeListener() {
|
||||
underTest.close()
|
||||
|
||||
verify(settingsCache).unregister(HAPTIC_FEEDBACK_URI, underTest.mHapticChangeListener)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun vibrate() {
|
||||
underTest.vibrate(OVERVIEW_HAPTIC)
|
||||
@@ -117,7 +112,7 @@ class VibratorWrapperTest {
|
||||
@Test
|
||||
fun haptic_feedback_disabled_no_vibrate() {
|
||||
`when`(vibrator.hasVibrator()).thenReturn(false)
|
||||
underTest = VibratorWrapper(vibrator, settingsCache)
|
||||
underTest = VibratorWrapper(context, settingsCache, tracker)
|
||||
|
||||
underTest.vibrate(OVERVIEW_HAPTIC)
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ import com.android.launcher3.util.TestUtil
|
||||
import com.android.launcher3.util.UserIconInfo
|
||||
import com.google.common.truth.Truth
|
||||
import java.util.concurrent.CountDownLatch
|
||||
import java.util.function.Predicate
|
||||
import junit.framework.Assert.assertEquals
|
||||
import org.junit.After
|
||||
import org.junit.Before
|
||||
@@ -76,6 +77,7 @@ class LoaderTaskTest {
|
||||
@Mock private lateinit var modelDelegate: ModelDelegate
|
||||
@Mock private lateinit var launcherBinder: BaseLauncherBinder
|
||||
private lateinit var launcherModel: LauncherModel
|
||||
@Mock private lateinit var widgetsFilterDataProvider: WidgetsFilterDataProvider
|
||||
@Mock private lateinit var transaction: LoaderTransaction
|
||||
@Mock private lateinit var iconCache: IconCache
|
||||
@Mock private lateinit var idleLock: LooperIdleLock
|
||||
@@ -89,6 +91,7 @@ class LoaderTaskTest {
|
||||
@Before
|
||||
fun setup() {
|
||||
MockitoAnnotations.initMocks(this)
|
||||
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_TIERED_WIDGETS_BY_DEFAULT_IN_PICKER)
|
||||
launcherModel = mock(LauncherModel::class.java)
|
||||
mockitoSession =
|
||||
ExtendedMockito.mockitoSession()
|
||||
@@ -118,6 +121,7 @@ class LoaderTaskTest {
|
||||
`when`(launcherBinder.newIdleLock(any())).thenReturn(idleLock)
|
||||
`when`(idleLock.awaitLocked(1000)).thenReturn(false)
|
||||
`when`(iconCache.updateHandler).thenReturn(iconCacheUpdateHandler)
|
||||
`when`(widgetsFilterDataProvider.getDefaultWidgetsFilter()).thenReturn(Predicate { true })
|
||||
context.putObject(UserCache.INSTANCE, userCache)
|
||||
|
||||
TestUtil.grantWriteSecurePermission()
|
||||
@@ -136,17 +140,32 @@ class LoaderTaskTest {
|
||||
val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
|
||||
`when`(userCache.userProfiles).thenReturn(mockUserHandles)
|
||||
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
|
||||
LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder)
|
||||
LoaderTask(
|
||||
app,
|
||||
bgAllAppsList,
|
||||
this,
|
||||
modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
)
|
||||
.runSyncOnBackgroundThread()
|
||||
Truth.assertThat(workspaceItems.size).isAtLeast(25)
|
||||
Truth.assertThat(appWidgets.size).isAtLeast(7)
|
||||
Truth.assertThat(collections.size()).isAtLeast(8)
|
||||
Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
|
||||
Truth.assertThat(widgetsModel.defaultWidgetsFilter).isNotNull()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun bindsLoadedDataCorrectly() {
|
||||
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
|
||||
LoaderTask(
|
||||
app,
|
||||
bgAllAppsList,
|
||||
BgDataModel(),
|
||||
modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
)
|
||||
.runSyncOnBackgroundThread()
|
||||
|
||||
verify(launcherBinder).bindWorkspace(true, false)
|
||||
@@ -155,6 +174,7 @@ class LoaderTaskTest {
|
||||
verify(launcherBinder).bindAllApps()
|
||||
verify(iconCacheUpdateHandler, times(4)).updateIcons(any(), any<CachingLogic<Any>>(), any())
|
||||
verify(launcherBinder).bindDeepShortcuts()
|
||||
verify(widgetsFilterDataProvider).initPeriodicDataRefresh(any())
|
||||
verify(launcherBinder).bindWidgets()
|
||||
verify(modelDelegate).loadAndBindOtherItems(anyOrNull())
|
||||
verify(iconCacheUpdateHandler).finish()
|
||||
@@ -172,7 +192,15 @@ class LoaderTaskTest {
|
||||
`when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
|
||||
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
|
||||
|
||||
LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
|
||||
LoaderTask(
|
||||
app,
|
||||
bgAllAppsList,
|
||||
this,
|
||||
modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
userManagerState,
|
||||
)
|
||||
.runSyncOnBackgroundThread()
|
||||
|
||||
verify(bgAllAppsList)
|
||||
@@ -193,7 +221,15 @@ class LoaderTaskTest {
|
||||
`when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
|
||||
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 3))
|
||||
|
||||
LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
|
||||
LoaderTask(
|
||||
app,
|
||||
bgAllAppsList,
|
||||
this,
|
||||
modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
userManagerState,
|
||||
)
|
||||
.runSyncOnBackgroundThread()
|
||||
|
||||
verify(bgAllAppsList)
|
||||
@@ -232,7 +268,14 @@ class LoaderTaskTest {
|
||||
RestoreDbTask.setPending(spyContext)
|
||||
|
||||
// When
|
||||
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
|
||||
LoaderTask(
|
||||
app,
|
||||
bgAllAppsList,
|
||||
BgDataModel(),
|
||||
modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
)
|
||||
.runSyncOnBackgroundThread()
|
||||
|
||||
// Then
|
||||
@@ -301,7 +344,14 @@ class LoaderTaskTest {
|
||||
RestoreDbTask.setPending(spyContext)
|
||||
|
||||
// When
|
||||
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
|
||||
LoaderTask(
|
||||
app,
|
||||
bgAllAppsList,
|
||||
BgDataModel(),
|
||||
modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
)
|
||||
.runSyncOnBackgroundThread()
|
||||
|
||||
// Then
|
||||
@@ -369,7 +419,14 @@ class LoaderTaskTest {
|
||||
Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 1)
|
||||
|
||||
// When
|
||||
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
|
||||
LoaderTask(
|
||||
app,
|
||||
bgAllAppsList,
|
||||
BgDataModel(),
|
||||
modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
)
|
||||
.runSyncOnBackgroundThread()
|
||||
|
||||
// Then
|
||||
@@ -404,7 +461,14 @@ class LoaderTaskTest {
|
||||
RestoreDbTask.setPending(spyContext)
|
||||
|
||||
// When
|
||||
LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
|
||||
LoaderTask(
|
||||
app,
|
||||
bgAllAppsList,
|
||||
BgDataModel(),
|
||||
modelDelegate,
|
||||
launcherBinder,
|
||||
widgetsFilterDataProvider,
|
||||
)
|
||||
.runSyncOnBackgroundThread()
|
||||
|
||||
// Then
|
||||
|
||||
Reference in New Issue
Block a user