Merge "Add Arrows to Folder PageIndicatorDots for Accessibility Purposes." into main
diff --git a/aconfig/launcher.aconfig b/aconfig/launcher.aconfig
index c5774bb..9f505a4 100644
--- a/aconfig/launcher.aconfig
+++ b/aconfig/launcher.aconfig
@@ -656,3 +656,13 @@
purpose: PURPOSE_BUGFIX
}
}
+
+flag {
+ name: "sync_app_launch_with_taskbar_stash"
+ namespace: "launcher"
+ description: "Syncs the two animations (app launch, taskbar stash) so they play at the same time."
+ bug: "319162553"
+ metadata {
+ purpose: PURPOSE_BUGFIX
+ }
+}
diff --git a/quickstep/res/color/app_chip_menu_item_color_fg.xml b/quickstep/res/color/app_chip_menu_item_color_fg.xml
new file mode 100644
index 0000000..fa1dc34
--- /dev/null
+++ b/quickstep/res/color/app_chip_menu_item_color_fg.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.15" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_pressed="true" />
+ <item android:alpha="0.11" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_hovered="true" />
+ <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/color/app_chip_state_color_fg.xml b/quickstep/res/color/app_chip_state_color_fg.xml
new file mode 100644
index 0000000..58dfee0
--- /dev/null
+++ b/quickstep/res/color/app_chip_state_color_fg.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 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.
+ -->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:alpha="0.15" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_pressed="true" />
+ <item android:alpha="0.11" android:color="@color/materialColorOnSurface" android:state_enabled="true" android:state_hovered="true" />
+ <item android:color="@android:color/transparent" />
+</selector>
\ No newline at end of file
diff --git a/quickstep/res/drawable/app_chip_fg.xml b/quickstep/res/drawable/app_chip_fg.xml
new file mode 100644
index 0000000..7b19c9e
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_fg.xml
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ Copyright (C) 2025 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/app_chip_state_color_fg" />
+</shape>
\ No newline at end of file
diff --git a/quickstep/res/drawable/app_chip_menu_item_bg.xml b/quickstep/res/drawable/app_chip_menu_item_bg.xml
new file mode 100644
index 0000000..39e88d2
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_menu_item_bg.xml
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/materialColorSurfaceBright" />
+</shape>
diff --git a/quickstep/res/drawable/app_chip_menu_item_fg.xml b/quickstep/res/drawable/app_chip_menu_item_fg.xml
new file mode 100644
index 0000000..96d067d
--- /dev/null
+++ b/quickstep/res/drawable/app_chip_menu_item_fg.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2025 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.
+ -->
+<shape xmlns:android="http://schemas.android.com/apk/res/android">
+ <solid android:color="@color/app_chip_menu_item_color_fg" />
+ <corners android:radius="@dimen/task_menu_item_corner_radius" />
+</shape>
diff --git a/quickstep/res/layout/icon_app_chip_view.xml b/quickstep/res/layout/icon_app_chip_view.xml
index 0972be1..de05d59 100644
--- a/quickstep/res/layout/icon_app_chip_view.xml
+++ b/quickstep/res/layout/icon_app_chip_view.xml
@@ -26,6 +26,7 @@
android:importantForAccessibility="no"
android:autoMirrored="true"
android:elevation="@dimen/task_thumbnail_icon_menu_elevation"
+ android:foreground="@drawable/app_chip_fg"
android:background="@color/materialColorSurfaceBright">
<!-- ignoring warning because the user of the anchor is a Rect where RTL is not needed -->
@@ -77,4 +78,5 @@
android:background="@drawable/icon_menu_arrow_background"
android:src="@drawable/ic_chevron_down"
android:importantForAccessibility="no" />
+
</com.android.quickstep.views.IconAppChipView>
\ No newline at end of file
diff --git a/quickstep/res/layout/keyboard_quick_switch_view.xml b/quickstep/res/layout/keyboard_quick_switch_view.xml
index 885bdb9..2dea79c 100644
--- a/quickstep/res/layout/keyboard_quick_switch_view.xml
+++ b/quickstep/res/layout/keyboard_quick_switch_view.xml
@@ -18,6 +18,8 @@
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/keyboard_quick_switch_view"
+ android:contentDescription="@string/quick_switch_content_description"
+ android:accessibilityPaneTitle="@string/quick_switch_pane_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/keyboard_quick_switch_margin_top"
diff --git a/quickstep/res/values/strings.xml b/quickstep/res/values/strings.xml
index 65f4b3c..7578bd5 100644
--- a/quickstep/res/values/strings.xml
+++ b/quickstep/res/values/strings.xml
@@ -325,6 +325,12 @@
<!-- Label for creating an application bubble (from the Taskbar only). -->
<string name="open_app_as_a_bubble">Open app as a bubble</string>
+ <!-- Accessibility pane title for quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
+ <string name="quick_switch_pane_title">Recent apps</string>
+
+ <!-- Content description for the quick switch view, which lists apps opened by the user, ordered by how recently the app was opened. -->
+ <string name="quick_switch_content_description">Recent app list</string>
+
<!-- Label for quick switch tile showing how many more apps are available. The number will be displayed above this text. [CHAR LIMIT=NONE] -->
<string name="quick_switch_overflow">{count, plural,
=1{more app}
@@ -336,6 +342,8 @@
<!-- Accessibility label for quick switch tiles showing split tasks [CHAR LIMIT=NONE] -->
<string name="quick_switch_split_task"><xliff:g id="app_name_1" example="Chrome">%1$s</xliff:g> and <xliff:g id="app_name_2" example="Gmail">%2$s</xliff:g></string>
+ <!-- Accessibility label for quick switch tiles that include information about the tile's position in the parent list [CHAR LIMIT=NONE] -->
+ <string name="quick_switch_task_with_position_in_parent"><xliff:g id="task_description" example="Chrome">%1$s</xliff:g>, item <xliff:g id="index_in_parent" example="1">%2$d</xliff:g> of <xliff:g id="total_tasks" example="5">%3$d</xliff:g></string>
<!-- Accessibility label for an arrow button within quick switch UI that scrolls the quick switch content left
TODO(b/397975686): Make these translatable when verified by UX. -->
diff --git a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
index 4d3e3be..cdae9ca 100644
--- a/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
+++ b/quickstep/src/com/android/launcher3/WidgetPickerActivity.java
@@ -166,7 +166,7 @@
mApp = LauncherAppState.getInstance(this);
InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
mDeviceProfile = idp.getDeviceProfile(this);
- mModel = new WidgetsModel();
+ mModel = new WidgetsModel(mApp.getContext());
mWidgetPickerDataProvider = new WidgetPickerDataProvider(this);
setContentView(R.layout.widget_picker_activity);
@@ -309,8 +309,7 @@
*/
private void refreshAndBindWidgets() {
MODEL_EXECUTOR.execute(() -> {
- LauncherAppState app = LauncherAppState.getInstance(this);
- mModel.update(app, null);
+ mModel.update(null);
StringCache stringCache = new StringCache();
stringCache.loadStrings(this);
@@ -321,9 +320,10 @@
// animation.
openWidgetsSheet();
if (mUiSurface != null) {
- mWidgetPredictionsRequester = new WidgetPredictionsRequester(app.getContext(),
- mUiSurface, mModel.getWidgetsByComponentKeyForPicker());
- mWidgetPredictionsRequester.request(mAddedWidgets, /*listener=*/ this);
+ mWidgetPredictionsRequester = new WidgetPredictionsRequester(
+ getApplicationContext(), mUiSurface,
+ mModel.getWidgetsByComponentKeyForPicker());
+ mWidgetPredictionsRequester.request(mAddedWidgets, this);
}
});
}
diff --git a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
index 40cfe92..a01846d 100644
--- a/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
+++ b/quickstep/src/com/android/launcher3/desktop/DesktopRecentsTransitionController.kt
@@ -30,6 +30,7 @@
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.quickstep.SystemUiProxy
import com.android.quickstep.TaskViewUtils
+import com.android.quickstep.util.DesksUtils.Companion.areMultiDesksFlagsEnabled
import com.android.quickstep.views.DesktopTaskView
import com.android.quickstep.views.TaskContainer
import com.android.quickstep.views.TaskView
@@ -60,7 +61,11 @@
callback,
)
val transition = RemoteTransition(animRunner, appThread, "RecentsToDesktop")
- systemUiProxy.showDesktopApps(desktopTaskView.displayId, transition)
+ if (areMultiDesksFlagsEnabled()) {
+ systemUiProxy.activateDesk(desktopTaskView.deskId, transition)
+ } else {
+ systemUiProxy.showDesktopApps(desktopTaskView.displayId, transition)
+ }
}
/** Launch desktop tasks from recents view */
diff --git a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
index fd71151..3544844 100644
--- a/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/PredictionUpdateTask.java
@@ -32,9 +32,9 @@
import androidx.annotation.NonNull;
import com.android.launcher3.ConstantItem;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherPrefs;
+import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.QuickstepModelDelegate.PredictorState;
import com.android.launcher3.model.data.AppInfo;
@@ -65,8 +65,8 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- LauncherAppState app = taskController.getApp();
- Context context = app.getContext();
+ IconCache iconCache = taskController.getIconCache();
+ Context context = taskController.getContext();
// TODO: remove this
LauncherPrefs.get(context).put(LAST_PREDICTION_ENABLED, !mTargets.isEmpty());
@@ -84,7 +84,7 @@
if (si != null) {
usersForChangedShortcuts.add(si.getUserHandle());
itemInfo = new WorkspaceItemInfo(si, context);
- app.getIconCache().getShortcutIcon(itemInfo, si);
+ iconCache.getShortcutIcon(itemInfo, si);
} else {
String className = target.getClassName();
if (COMPONENT_CLASS_MARKER.equals(className)) {
@@ -96,7 +96,7 @@
itemInfo = apps.data.stream()
.filter(info -> user.equals(info.user) && cn.equals(info.componentName))
.map(ai -> {
- app.getIconCache().getTitleAndIcon(ai, mPredictorState.lookupFlag);
+ iconCache.getTitleAndIcon(ai, mPredictorState.lookupFlag);
return ai.makeWorkspaceItem(context);
})
.findAny()
@@ -107,7 +107,7 @@
return null;
}
AppInfo ai = new AppInfo(context, lai, user);
- app.getIconCache().getTitleAndIcon(ai, lai, DEFAULT_LOOKUP_FLAG);
+ iconCache.getTitleAndIcon(ai, lai, DEFAULT_LOOKUP_FLAG);
return ai.makeWorkspaceItem(context);
});
@@ -123,8 +123,7 @@
FixedContainerItems fci = new FixedContainerItems(mPredictorState.containerId, items);
dataModel.extraItems.put(fci.containerId, fci);
taskController.bindExtraContainerItems(fci);
- usersForChangedShortcuts.forEach(
- u -> dataModel.updateShortcutPinnedState(app.getContext(), u));
+ usersForChangedShortcuts.forEach(u -> dataModel.updateShortcutPinnedState(context, u));
// Save to disk
mPredictorState.storage.write(context, fci.items);
diff --git a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
index 0a4b7c8..da55b40 100644
--- a/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
+++ b/quickstep/src/com/android/launcher3/model/WidgetsPredictionUpdateTask.java
@@ -82,7 +82,7 @@
&& !widgetsInWorkspace.contains(entry.getValue())
).collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
- Context context = taskController.getApp().getContext();
+ Context context = taskController.getContext();
List<WidgetItem> servicePredictedItems = new ArrayList<>();
List<String> addedWidgetApps = new ArrayList<>();
diff --git a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
index eb24df1..810fa6f 100644
--- a/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
+++ b/quickstep/src/com/android/launcher3/statehandlers/DesktopVisibilityController.kt
@@ -401,6 +401,39 @@
DisplayController.INSTANCE.get(context).notifyConfigChange()
}
+ private fun notifyOnDeskAdded(displayId: Int, deskId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyOnDeskAdded: displayId=$displayId, deskId=$deskId")
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onDeskAdded(displayId, deskId)
+ }
+ }
+
+ private fun notifyOnDeskRemoved(displayId: Int, deskId: Int) {
+ if (DEBUG) {
+ Log.d(TAG, "notifyOnDeskRemoved: displayId=$displayId, deskId=$deskId")
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onDeskRemoved(displayId, deskId)
+ }
+ }
+
+ private fun notifyOnActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
+ if (DEBUG) {
+ Log.d(
+ TAG,
+ "notifyOnActiveDeskChanged: displayId=$displayId, newActiveDesk=$newActiveDesk, oldActiveDesk=$oldActiveDesk",
+ )
+ }
+
+ for (listener in desktopVisibilityListeners) {
+ listener.onActiveDeskChanged(displayId, newActiveDesk, oldActiveDesk)
+ }
+ }
+
/** TODO: b/333533253 - Remove after flag rollout */
private fun setBackgroundStateEnabled(backgroundStateEnabled: Boolean) {
if (DEBUG) {
@@ -511,6 +544,8 @@
"Found a duplicate desk Id: $deskId on display: $displayId"
}
}
+
+ notifyOnDeskAdded(displayId, deskId)
}
private fun onDeskRemoved(displayId: Int, deskId: Int) {
@@ -526,6 +561,8 @@
it.activeDeskId = INACTIVE_DESK_ID
}
}
+
+ notifyOnDeskRemoved(displayId, deskId)
}
private fun onActiveDeskChanged(displayId: Int, newActiveDesk: Int, oldActiveDesk: Int) {
@@ -539,12 +576,16 @@
check(oldActiveDesk == it.activeDeskId) {
"Mismatch between the Shell's oldActiveDesk: $oldActiveDesk, and Launcher's: ${it.activeDeskId}"
}
- check(it.deskIds.contains(newActiveDesk)) {
+ check(newActiveDesk == INACTIVE_DESK_ID || it.deskIds.contains(newActiveDesk)) {
"newActiveDesk: $newActiveDesk was never added to display: $displayId"
}
it.activeDeskId = newActiveDesk
}
+ if (newActiveDesk != oldActiveDesk) {
+ notifyOnActiveDeskChanged(displayId, newActiveDesk, oldActiveDesk)
+ }
+
if (wasInDesktopMode != isInDesktopModeAndNotInOverview(displayId)) {
notifyIsInDesktopModeChanged(displayId, !wasInDesktopMode)
}
@@ -650,8 +691,11 @@
"duration= " +
transitionDuration),
)
- controller.get()?.isInDesktopMode = true
- controller.get()?.notifyTaskbarDesktopModeListenersForEntry(transitionDuration)
+ val controller = controller.get()
+ if (controller != null && !controller.isInDesktopMode) {
+ controller.isInDesktopMode = true
+ controller.notifyTaskbarDesktopModeListenersForEntry(transitionDuration)
+ }
}
}
@@ -663,8 +707,11 @@
"duration= " +
transitionDuration),
)
- controller.get()?.isInDesktopMode = false
- controller.get()?.notifyTaskbarDesktopModeListenersForExit(transitionDuration)
+ val controller = controller.get()
+ if (controller != null && controller.isInDesktopMode) {
+ controller.isInDesktopMode = false
+ controller.notifyTaskbarDesktopModeListenersForExit(transitionDuration)
+ }
}
}
@@ -718,6 +765,6 @@
private const val TAG = "DesktopVisController"
private const val DEBUG = false
- public const val INACTIVE_DESK_ID = -1
+ const val INACTIVE_DESK_ID = -1
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
index 09a8670..aa3feb7 100644
--- a/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/BaseTaskbarContext.java
@@ -23,6 +23,7 @@
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.util.BaseContext;
+import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.quickstep.SystemUiProxy;
@@ -32,10 +33,20 @@
implements SystemShortcut.BubbleActivityStarter {
protected final LayoutInflater mLayoutInflater;
+ private final boolean mIsPrimaryDisplay;
- public BaseTaskbarContext(Context windowContext) {
+ public BaseTaskbarContext(Context windowContext, boolean isPrimaryDisplay) {
super(windowContext, Themes.getActivityThemeRes(windowContext));
mLayoutInflater = LayoutInflater.from(this).cloneInContext(this);
+ mIsPrimaryDisplay = isPrimaryDisplay;
+ }
+
+ public boolean isTransientTaskbar() {
+ return DisplayController.isTransientTaskbar(this) && mIsPrimaryDisplay;
+ }
+
+ public boolean isPrimaryDisplay() {
+ return mIsPrimaryDisplay;
}
@Override
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
index cc94824..1698050 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchController.java
@@ -170,7 +170,7 @@
mHasDesktopTask,
mWasDesktopTaskFilteredOut);
}, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
- : RecentsFilterState.getEmptyDesktopTaskFilter());
+ : RecentsFilterState.getDesktopTaskFilter());
}
mQuickSwitchViewController.updateLayoutForSurface(wasOpenedFromTaskbar,
@@ -232,7 +232,7 @@
mWasDesktopTaskFilteredOut,
wasOpenedFromTaskbar);
}, shouldShowDesktopTasks ? RecentsFilterState.EMPTY_FILTER
- : RecentsFilterState.getEmptyDesktopTaskFilter());
+ : RecentsFilterState.getDesktopTaskFilter());
}
private boolean shouldExcludeTask(GroupTask task, Set<Integer> taskIdsToExclude) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
index 5f867cd..15be03a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchTaskView.java
@@ -66,6 +66,11 @@
@Nullable private ImageView mIcon2;
@Nullable private View mContent;
+ // Describe the task position in the parent container. Used to add information about the task's
+ // position in a task list to the task view's content description.
+ private int mIndexInParent = -1;
+ private int mTotalTasksInParent = -1;
+
public KeyboardQuickSwitchTaskView(@NonNull Context context) {
this(context, null);
}
@@ -155,36 +160,51 @@
applyThumbnail(mThumbnailView1, task1, thumbnailUpdateFunction);
applyThumbnail(mThumbnailView2, task2, thumbnailUpdateFunction);
+ // Update content description, even in cases task icons, and content descriptions need to be
+ // loaded asynchronously to ensure that the task has non empty description (assuming task
+ // position information was set), as KeyboardQuickSwitch view may request accessibility
+ // focus to be moved to the task when the quick switch UI gets shown. The description will
+ // be updated once the task metadata has been loaded - the delay should be very short, and
+ // the content description when task titles are not available still gives some useful
+ // information to the user (the task's position in the list).
+ updateContentDesctiptionForTasks(task1, task2);
+
if (iconUpdateFunction == null) {
applyIcon(mIcon1, task1);
applyIcon(mIcon2, task2);
- setContentDescription(task2 == null
- ? task1.titleDescription
- : getContext().getString(
- R.string.quick_switch_split_task,
- task1.titleDescription,
- task2.titleDescription));
return;
}
+
iconUpdateFunction.updateIconInBackground(task1, t -> {
applyIcon(mIcon1, task1);
if (task2 != null) {
return;
}
- setContentDescription(task1.titleDescription);
+ updateContentDesctiptionForTasks(task1, null);
});
+
if (task2 == null) {
return;
}
iconUpdateFunction.updateIconInBackground(task2, t -> {
applyIcon(mIcon2, task2);
- setContentDescription(getContext().getString(
- R.string.quick_switch_split_task,
- task1.titleDescription,
- task2.titleDescription));
+ updateContentDesctiptionForTasks(task1, task2);
});
}
+ /**
+ * Initializes information about the task's position within the parent container context - used
+ * to add position information to the view's content description.
+ * Should be called before associating the view with tasks.
+ *
+ * @param index The view's 0-based index within the parent task container.
+ * @param totalTasks The total number of tasks in the parent task container.
+ */
+ protected void setPositionInformation(int index, int totalTasks) {
+ mIndexInParent = index;
+ mTotalTasksInParent = totalTasks;
+ }
+
protected void setThumbnailsForSplitTasks(
@NonNull Task task1,
@Nullable Task task2,
@@ -283,6 +303,28 @@
constantState.newDrawable(getResources(), getContext().getTheme()));
}
+ /**
+ * Updates the task view's content description to reflect tasks represented by the view.
+ */
+ private void updateContentDesctiptionForTasks(@NonNull Task task1, @Nullable Task task2) {
+ String tasksDescription = task1.titleDescription == null || task2 == null
+ ? task1.titleDescription
+ : getContext().getString(
+ R.string.quick_switch_split_task,
+ task1.titleDescription,
+ task2.titleDescription);
+ if (mIndexInParent < 0) {
+ setContentDescription(tasksDescription);
+ return;
+ }
+
+ setContentDescription(
+ getContext().getString(R.string.quick_switch_task_with_position_in_parent,
+ tasksDescription != null ? tasksDescription : "",
+ mIndexInParent + 1,
+ mTotalTasksInParent));
+ }
+
protected interface ThumbnailUpdateFunction {
void updateThumbnailInBackground(Task task, Consumer<ThumbnailData> callback);
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
index 845e19f..ab147bb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchView.java
@@ -301,6 +301,7 @@
continue;
}
+ currentTaskView.setPositionInformation(i, tasksToDisplay);
currentTaskView.setThumbnailsForSplitTasks(
task1,
task2,
@@ -548,6 +549,9 @@
ViewOutlineProvider outlineProvider = getOutlineProvider();
+ int defaultFocusedTaskIndex = Math.min(
+ getTaskCount() - 1,
+ currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride);
mOpenAnimation.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
@@ -601,9 +605,7 @@
});
}
- animateFocusMove(-1, Math.min(
- getTaskCount() - 1,
- currentFocusIndexOverride == -1 ? 1 : currentFocusIndexOverride));
+ animateFocusMove(-1, defaultFocusedTaskIndex);
displayedContent.setVisibility(VISIBLE);
setVisibility(VISIBLE);
requestFocus();
@@ -623,6 +625,11 @@
invalidateOutline();
mOpenAnimation = null;
InteractionJankMonitorWrapper.end(Cuj.CUJ_LAUNCHER_KEYBOARD_QUICK_SWITCH_OPEN);
+
+ View focusedTask = getTaskAt(defaultFocusedTaskIndex);
+ if (focusedTask != null) {
+ focusedTask.requestAccessibilityFocus();
+ }
}
});
diff --git a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
index 5f7a026..b5f2532 100644
--- a/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/KeyboardQuickSwitchViewController.java
@@ -40,7 +40,6 @@
import com.android.launcher3.desktop.DesktopAppLaunchTransition;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayDragLayer;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.views.BaseDragLayer;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.util.GroupTask;
@@ -106,8 +105,7 @@
boolean hasDesktopTask,
boolean wasDesktopTaskFilteredOut,
boolean wasOpenedFromTaskbar) {
- final boolean isTransientTaskBar = DisplayController.isTransientTaskbar(
- mControllers.taskbarActivityContext);
+ final boolean isTransientTaskBar = mControllers.taskbarActivityContext.isTransientTaskbar();
positionView(wasOpenedFromTaskbar, isTransientTaskBar);
// Keep the taskbar unstashed if the KQS is opened.
diff --git a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
index 2272d11..e998388 100644
--- a/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/LauncherTaskbarUIController.java
@@ -135,11 +135,11 @@
@Override
protected void onDestroy() {
onLauncherVisibilityChanged(false /* isVisible */, true /* fromInitOrDestroy */);
+ mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
super.onDestroy();
mTaskbarLauncherStateController.onDestroy();
mLauncher.setTaskbarUIController(null);
- mLauncher.removeOnDeviceProfileChangeListener(mOnDeviceProfileChangeListener);
mHomeState.removeListener(mVisibilityChangeListener);
}
@@ -225,9 +225,8 @@
if (isVisible || isPinnedTaskbar) {
return getTaskbarToHomeDuration(shouldOverrideToFastAnimation, isPinnedTaskbar);
} else {
- return DisplayController.isTransientTaskbar(mLauncher)
- ? TRANSIENT_TASKBAR_TRANSITION_DURATION
- : TASKBAR_TO_APP_DURATION;
+ return mControllers.taskbarActivityContext.isTransientTaskbar()
+ ? TRANSIENT_TASKBAR_TRANSITION_DURATION : TASKBAR_TO_APP_DURATION;
}
}
@@ -279,7 +278,10 @@
private void postAdjustHotseatForBubbleBar() {
Hotseat hotseat = mLauncher.getHotseat();
if (hotseat == null || !isBubbleBarVisible()) return;
- hotseat.post(() -> adjustHotseatForBubbleBar(isBubbleBarVisible()));
+ hotseat.post(() -> {
+ if (mControllers == null) return;
+ adjustHotseatForBubbleBar(isBubbleBarVisible());
+ });
}
private boolean isBubbleBarVisible() {
@@ -334,7 +336,7 @@
}
// Persistent features EDU tooltip.
- if (!DisplayController.isTransientTaskbar(mLauncher)) {
+ if (!mControllers.taskbarActivityContext.isTransientTaskbar()) {
mControllers.taskbarEduTooltipController.maybeShowFeaturesEdu();
return;
}
@@ -357,7 +359,7 @@
}
// Persistent features EDU tooltip.
- if (!DisplayController.isTransientTaskbar(mLauncher)) {
+ if (!mControllers.taskbarActivityContext.isTransientTaskbar()) {
return !OnboardingPrefs.TASKBAR_EDU_TOOLTIP_STEP.hasReachedMax(mLauncher);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
index ee5b8d1..27b38e5 100644
--- a/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/NavbarButtonsViewController.java
@@ -870,6 +870,9 @@
if (predictiveBackThreeButtonNav() && buttonType == BUTTON_BACK) {
// set up special touch listener for back button to support predictive back
setBackButtonTouchListener(buttonView, navButtonController);
+ // Set this View clickable, so that NearestTouchFrame.java forwards closeby touches to
+ // this View
+ buttonView.setClickable(true);
} else {
buttonView.setOnClickListener(view ->
navButtonController.onButtonClick(buttonType, view));
diff --git a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
index 2e5bebc..6bd3d85 100644
--- a/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/StashedHandleViewController.java
@@ -36,7 +36,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RevealOutlineAnimation;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.MultiPropertyFactory;
import com.android.launcher3.util.MultiValueAlpha;
@@ -217,7 +216,7 @@
.getTransientTaskbarIconLayoutBounds();
float startRadius = mStashedHandleRadius;
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
// Account for the full visual height of the transient taskbar.
int heightDiff = (mTaskbarSize - visualBounds.height()) / 2;
visualBounds.top -= heightDiff;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
index 6afbebf..de91c54 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarActivityContext.java
@@ -99,6 +99,7 @@
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.R;
import com.android.launcher3.allapps.ActivityAllAppsContainerView;
+import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.apppairs.AppPairIcon;
import com.android.launcher3.config.FeatureFlags;
@@ -255,7 +256,7 @@
TaskbarNavButtonController buttonController,
ScopedUnfoldTransitionProgressProvider unfoldTransitionProgressProvider,
boolean isPrimaryDisplay, SystemUiProxy sysUiProxy) {
- super(windowContext);
+ super(windowContext, isPrimaryDisplay);
mIsPrimaryDisplay = isPrimaryDisplay;
mNavigationBarPanelContext = navigationBarPanelContext;
mSysUiProxy = sysUiProxy;
@@ -386,13 +387,6 @@
onViewCreated();
}
- /**
- * Returns whether this is a primary display.
- */
- public boolean isPrimaryDisplay() {
- return mIsPrimaryDisplay;
- }
-
/** Updates {@link DeviceProfile} instances for any Taskbar windows. */
public void updateDeviceProfile(DeviceProfile launcherDp) {
applyDeviceProfile(launcherDp);
@@ -411,7 +405,7 @@
/** Returns whether current taskbar is transient. */
public boolean isTransientTaskbar() {
- return DisplayController.isTransientTaskbar(this) && !isPhoneMode();
+ return super.isTransientTaskbar() && !isPhoneMode();
}
/**
@@ -432,7 +426,7 @@
mDeviceProfile = originDeviceProfile.toBuilder(this)
.withDimensionsOverride(overrideProvider).build();
- if (DisplayController.isTransientTaskbar(this)) {
+ if (isTransientTaskbar()) {
mTransientTaskbarDeviceProfile = mDeviceProfile;
mPersistentTaskbarDeviceProfile = mDeviceProfile
.toBuilder(this)
@@ -450,7 +444,6 @@
mNavMode = (DesktopExperienceFlags.ENABLE_TASKBAR_CONNECTED_DISPLAYS.isTrue()
&& !mIsPrimaryDisplay) ? NavigationMode.THREE_BUTTONS
: DisplayController.getNavigationMode(this);
-
}
/** Called when the visibility of the bubble bar changed. */
@@ -661,8 +654,7 @@
int windowFlags = WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
| WindowManager.LayoutParams.FLAG_SLIPPERY
| WindowManager.LayoutParams.FLAG_SPLIT_TOUCH;
- boolean watchOutside = DisplayController.isTransientTaskbar(this)
- || isThreeButtonNav();
+ boolean watchOutside = isTransientTaskbar() || isThreeButtonNav();
if (watchOutside && !isRunningInTestHarness()) {
windowFlags |= WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
| WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
@@ -1223,8 +1215,8 @@
bubbleControllers.bubbleBarViewController.getBubbleBarWithFlyoutMaximumHeight()
).orElse(0);
int taskbarWindowSize;
- boolean shouldTreatAsTransient = DisplayController.isTransientTaskbar(this)
- || (enableTaskbarPinning() && !isThreeButtonNav());
+ boolean shouldTreatAsTransient =
+ isTransientTaskbar() || (enableTaskbarPinning() && !isThreeButtonNav());
int extraHeightForTaskbarTooltips = enableCursorHoverStates()
? resources.getDimensionPixelSize(R.dimen.arrow_toast_arrow_height)
@@ -1297,7 +1289,7 @@
* Applies forcibly show flag to taskbar window iff transient taskbar is unstashed.
*/
public void applyForciblyShownFlagWhileTransientTaskbarUnstashed(boolean shouldForceShow) {
- if (!DisplayController.isTransientTaskbar(this) || isPhoneMode()) {
+ if (!isTransientTaskbar() || isPhoneMode()) {
return;
}
if (shouldForceShow) {
@@ -1344,7 +1336,7 @@
mControllers.uiController.startSplitSelection(splitSelectSource);
}
- boolean areDesktopTasksVisible() {
+ boolean isInDesktopMode() {
return mControllers != null
&& mControllers.taskbarDesktopModeController.isInDesktopMode(getDisplayId());
}
@@ -1360,23 +1352,23 @@
// TODO: b/316004172, b/343289567: Handle `DesktopTask` and `SplitTask`.
if (tag instanceof SingleTask singleTask) {
RemoteTransition remoteTransition =
- (areDesktopTasksVisible() && canUnminimizeDesktopTask(
+ (isInDesktopMode() && canUnminimizeDesktopTask(
singleTask.getTask().key.id))
? createDesktopAppLaunchRemoteTransition(AppLaunchType.UNMINIMIZE,
Cuj.CUJ_DESKTOP_MODE_APP_LAUNCH_FROM_ICON)
: null;
- if (areDesktopTasksVisible() && mControllers.uiController.isInOverviewUi()) {
+ if (isInDesktopMode() && mControllers.uiController.isInOverviewUi()) {
RunnableList runnableList = recents.launchRunningDesktopTaskView();
// Wrapping it in runnable so we post after DW is ready for the app
// launch.
if (runnableList != null) {
runnableList.add(() -> UI_HELPER_EXECUTOR.execute(
() -> handleGroupTaskLaunch(singleTask, remoteTransition,
- areDesktopTasksVisible(),
+ isInDesktopMode(),
DesktopTaskToFrontReason.TASKBAR_TAP)));
}
} else {
- handleGroupTaskLaunch(singleTask, remoteTransition, areDesktopTasksVisible(),
+ handleGroupTaskLaunch(singleTask, remoteTransition, isInDesktopMode(),
DesktopTaskToFrontReason.TASKBAR_TAP);
}
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(true);
@@ -1402,7 +1394,7 @@
: null;
- if (areDesktopTasksVisible() && mControllers.uiController.isInOverviewUi()) {
+ if (isInDesktopMode() && mControllers.uiController.isInOverviewUi()) {
RunnableList runnableList = recents.launchRunningDesktopTaskView();
if (runnableList != null) {
runnableList.add(() ->
@@ -1676,7 +1668,7 @@
.launchAppPair((AppPairIcon) launchingIconView,
-1 /*cuj*/)));
} else {
- if (areDesktopTasksVisible()
+ if (isInDesktopMode()
&& mControllers.uiController.isInOverviewUi()) {
RunnableList runnableList = recents.launchRunningDesktopTaskView();
// Wrapping it in runnable so we post after DW is ready for the app
@@ -1722,7 +1714,7 @@
return;
}
}
- if (areDesktopTasksVisible()
+ if (isInDesktopMode()
&& DesktopModeFlags.ENABLE_DESKTOP_APP_LAUNCH_TRANSITIONS_BUGFIX.isTrue()) {
launchDesktopApp(intent, info, displayId);
} else {
@@ -1887,7 +1879,7 @@
*/
@VisibleForTesting
public void unstashTaskbarIfStashed() {
- if (DisplayController.isTransientTaskbar(this)) {
+ if (isTransientTaskbar()) {
mControllers.taskbarStashController.updateAndAnimateTransientTaskbar(false);
}
}
@@ -1950,6 +1942,8 @@
// Override the alpha updates in the icon alignment animation.
allAppsButton.setAlpha(0);
});
+ alphaOverride.addListener(AnimatorListeners.forSuccessCallback(
+ () -> allAppsButton.setAlpha(1f)));
fullAnimation.play(alphaOverride);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
index 89cc991..5e02d81 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarBackgroundRenderer.kt
@@ -30,7 +30,6 @@
import com.android.launcher3.icons.GraphicsUtils.setColorAlphaBound
import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_PERSISTENT
import com.android.launcher3.taskbar.TaskbarPinningController.Companion.PINNING_TRANSIENT
-import com.android.launcher3.util.DisplayController
import kotlin.math.min
/** Helps draw the taskbar background, made up of a rectangle plus two inverted rounded corners. */
@@ -43,7 +42,7 @@
private val maxPersistentTaskbarHeight =
context.persistentTaskbarDeviceProfile.taskbarHeight.toFloat()
var backgroundProgress =
- if (DisplayController.isTransientTaskbar(context)) {
+ if (context.isTransientTaskbar) {
PINNING_TRANSIENT
} else {
PINNING_PERSISTENT
@@ -102,7 +101,7 @@
fullCornerRadius = context.cornerRadius.toFloat()
cornerRadius = fullCornerRadius
- if (!context.areDesktopTasksVisible()) {
+ if (!context.isInDesktopMode()) {
setCornerRoundness(MAX_ROUNDNESS)
}
}
@@ -124,7 +123,7 @@
* @param cornerRoundness 0 has no round corner, 1 has complete round corner.
*/
fun setCornerRoundness(cornerRoundness: Float) {
- if (DisplayController.isTransientTaskbar(context) && !transientBackgroundBounds.isEmpty) {
+ if (context.isTransientTaskbar && !transientBackgroundBounds.isEmpty) {
return
}
@@ -146,7 +145,7 @@
/** Draws the background with the given paint and height, on the provided canvas. */
fun draw(canvas: Canvas) {
if (isInSetup) return
- val isTransientTaskbar = DisplayController.isTransientTaskbar(context)
+ val isTransientTaskbar = context.isTransientTaskbar
canvas.save()
if (!isTransientTaskbar || transientBackgroundBounds.isEmpty || isAnimatingPinning) {
drawPersistentBackground(canvas)
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
index 58606de..a2b6423 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarControllers.java
@@ -260,6 +260,7 @@
mAreAllControllersInitialized = false;
mSharedState = null;
+ taskbarDragController.onDestroy();
navbarButtonsViewController.onDestroy();
uiController.onDestroy();
rotationButtonController.onDestroy();
@@ -280,7 +281,6 @@
taskbarStashController.onDestroy();
bubbleControllers.ifPresent(controllers -> controllers.onDestroy());
taskbarDesktopModeController.onDestroy();
-
mControllersToLog = null;
mBackgroundRendererControllers = null;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
index 3f6ebe2..e3e7499 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDividerPopupView.kt
@@ -35,7 +35,6 @@
import com.android.launcher3.R
import com.android.launcher3.popup.ArrowPopup
import com.android.launcher3.popup.RoundedArrowDrawable
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Themes
import com.android.launcher3.views.ActivityContext
import kotlin.math.max
@@ -64,12 +63,17 @@
false,
) as TaskbarDividerPopupView<*>
- return taskMenuViewWithArrow.populateForView(view, horizontalPosition)
+ return taskMenuViewWithArrow.populateForView(
+ view,
+ horizontalPosition,
+ taskbarActivityContext,
+ )
}
}
private lateinit var dividerView: View
private var horizontalPosition = 0.0f
+ private lateinit var taskbarActivityContext: TaskbarActivityContext
private val popupCornerRadius = Themes.getDialogCornerRadius(context)
private val arrowWidth = resources.getDimension(R.dimen.popup_arrow_width)
@@ -78,7 +82,7 @@
private val minPaddingFromScreenEdge =
resources.getDimension(R.dimen.taskbar_pinning_popup_menu_min_padding_from_screen_edge)
- private var alwaysShowTaskbarOn = !DisplayController.isTransientTaskbar(context)
+ private var alwaysShowTaskbarOn = !taskbarActivityContext.isTransientTaskbar
private var didPreferenceChange = false
private var verticalOffsetForPopupView =
resources.getDimensionPixelSize(R.dimen.taskbar_pinning_popup_menu_vertical_margin)
@@ -175,8 +179,13 @@
return false
}
- private fun populateForView(view: View, horizontalPosition: Float): TaskbarDividerPopupView<*> {
+ private fun populateForView(
+ view: View,
+ horizontalPosition: Float,
+ taskbar: TaskbarActivityContext,
+ ): TaskbarDividerPopupView<*> {
dividerView = view
+ taskbarActivityContext = taskbar
this@TaskbarDividerPopupView.horizontalPosition = horizontalPosition
tryUpdateBackground()
return this
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
index 1b516be..142f458 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragController.java
@@ -80,7 +80,6 @@
import com.android.launcher3.taskbar.bubbles.BubbleBarViewController;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.IntSet;
import com.android.launcher3.util.ItemInfoMatcher;
import com.android.launcher3.views.BubbleTextHolder;
@@ -132,6 +131,14 @@
public void init(TaskbarControllers controllers) {
mControllers = controllers;
+ mControllers.bubbleControllers.ifPresent(
+ c -> c.bubbleBarViewController.addBubbleBarDropTargets(this));
+ }
+
+ /** Called when the controller is destroyed. */
+ public void onDestroy() {
+ mControllers.bubbleControllers.ifPresent(
+ c -> c.bubbleBarViewController.removeBubbleBarDropTargets(this));
}
public void setDisallowGlobalDrag(boolean disallowGlobalDrag) {
@@ -463,7 +470,7 @@
com.android.launcher3.logging.InstanceId launcherInstanceId = instanceIds.second;
intent.putExtra(ClipDescription.EXTRA_LOGGING_INSTANCE_ID, internalInstanceId);
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
// Tell WM Shell to ignore drag events in the provided transient taskbar region.
TaskbarDragLayer dragLayer = mControllers.taskbarActivityContext.getDragLayer();
int[] locationOnScreen = dragLayer.getLocationOnScreen();
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
index 55ecc37..1e193f6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarDragLayerController.java
@@ -34,7 +34,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.AnimatorListeners;
import com.android.launcher3.util.DimensionUtils;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.MultiPropertyFactory.MultiProperty;
import com.android.launcher3.util.TouchController;
@@ -108,19 +107,18 @@
if (startAnimation != null) {
// set taskbar background render animation boolean
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
mTaskbarDragLayer.setIsAnimatingTransientTaskbarBackground(true);
} else {
mTaskbarDragLayer.setIsAnimatingPersistentTaskbarBackground(true);
}
- float desiredValue = DisplayController.isTransientTaskbar(mActivity)
+ float desiredValue = mActivity.isTransientTaskbar()
? PINNING_TRANSIENT
: PINNING_PERSISTENT;
- float nonDesiredvalue = !DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT;
+ float nonDesiredvalue =
+ !mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT;
ObjectAnimator objectAnimator = mTaskbarBackgroundProgress.animateToValue(
nonDesiredvalue, desiredValue);
@@ -133,9 +131,8 @@
}));
} else {
- mTaskbarBackgroundProgress.updateValue(DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT);
+ mTaskbarBackgroundProgress.updateValue(
+ mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT);
}
mBgTaskbar.value = 1;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
index d624413..7a23006 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarEduTooltipController.kt
@@ -155,7 +155,7 @@
fun maybeShowSwipeEdu() {
if (
!isTooltipEnabled ||
- !DisplayController.isTransientTaskbar(activityContext) ||
+ !activityContext.isTransientTaskbar ||
tooltipStep > TOOLTIP_STEP_SWIPE
) {
return
@@ -200,7 +200,7 @@
suggestionsAnim.supportLightTheme()
pinningAnim.supportLightTheme()
handleEduAnimations(listOf(splitscreenAnim, suggestionsAnim, pinningAnim))
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
splitscreenAnim.setAnimation(R.raw.taskbar_edu_splitscreen_transient)
suggestionsAnim.setAnimation(R.raw.taskbar_edu_suggestions_transient)
pinningEdu.visibility = if (enableTaskbarPinning()) VISIBLE else GONE
@@ -230,7 +230,7 @@
// Set up layout parameters.
content.updateLayoutParams { width = MATCH_PARENT }
updateLayoutParams<MarginLayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
width =
resources.getDimensionPixelSize(
if (enableTaskbarPinning())
@@ -263,7 +263,7 @@
// for the original 2 edu steps) as a proxy to needing to show the separate pinning edu
if (
!enableTaskbarPinning() ||
- !DisplayController.isTransientTaskbar(activityContext) ||
+ !activityContext.isTransientTaskbar ||
!isTooltipEnabled ||
tooltipStep > TOOLTIP_STEP_PINNING ||
tooltipStep < TOOLTIP_STEP_FEATURES
@@ -289,7 +289,7 @@
pinningAnim.supportLightTheme()
handleEduAnimations(listOf(pinningAnim))
updateLayoutParams<BaseDragLayer.LayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
bottomMargin += activityContext.deviceProfile.taskbarHeight
}
// Unlike other tooltips, we want to align with taskbar divider rather than center.
@@ -344,7 +344,7 @@
showDisclosureText(eduSubtitle)
updateLayoutParams<BaseDragLayer.LayoutParams> {
- if (DisplayController.isTransientTaskbar(activityContext)) {
+ if (activityContext.isTransientTaskbar) {
bottomMargin += activityContext.deviceProfile.taskbarHeight
}
// Unlike other tooltips, we want to align with the all apps button rather than
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
index a8ce10f..3af2ab6 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarInsetsController.kt
@@ -235,8 +235,7 @@
context.resources,
)
val isPinnedTaskbar =
- context.deviceProfile.isTaskbarPresent &&
- !context.deviceProfile.isTransientTaskbar
+ context.deviceProfile.isTaskbarPresent && !context.isTransientTaskbar
val mandatoryGestureHeight = if (isPinnedTaskbar) contentHeight else gestureHeight
provider.insetsSize =
getInsetsForGravityWithCutout(mandatoryGestureHeight, gravity, endRotation)
@@ -388,10 +387,7 @@
bubbleBarVisible
) {
// Taskbar has some touchable elements, take over the full taskbar area
- if (
- controllers.uiController.isInOverviewUi &&
- DisplayController.isTransientTaskbar(context)
- ) {
+ if (controllers.uiController.isInOverviewUi && context.isTransientTaskbar) {
val region =
controllers.taskbarActivityContext.dragLayer.lastDrawnTransientRect.toRegion()
val bubbleBarBounds =
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
index cc340ce..e8e5b30 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarManager.java
@@ -15,8 +15,8 @@
*/
package com.android.launcher3.taskbar;
-import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.content.Context.RECEIVER_EXPORTED;
+import static android.content.Context.RECEIVER_NOT_EXPORTED;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR;
import static android.view.WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL;
@@ -25,6 +25,7 @@
import static com.android.launcher3.Flags.enableUnfoldStateAnimation;
import static com.android.launcher3.config.FeatureFlags.ENABLE_TASKBAR_NAVBAR_UNIFICATION;
import static com.android.launcher3.config.FeatureFlags.enableTaskbarNoRecreate;
+import static com.android.launcher3.taskbar.growth.GrowthConstants.BROADCAST_SHOW_NUDGE;
import static com.android.launcher3.util.DisplayController.CHANGE_DENSITY;
import static com.android.launcher3.util.DisplayController.CHANGE_DESKTOP_MODE;
import static com.android.launcher3.util.DisplayController.CHANGE_NAVIGATION_MODE;
@@ -32,7 +33,6 @@
import static com.android.launcher3.util.DisplayController.CHANGE_TASKBAR_PINNING;
import static com.android.launcher3.util.Executors.UI_HELPER_EXECUTOR;
import static com.android.launcher3.util.FlagDebugUtils.formatFlagChange;
-import static com.android.launcher3.taskbar.growth.GrowthConstants.BROADCAST_SHOW_NUDGE;
import static com.android.quickstep.util.SystemActionConstants.ACTION_SHOW_TASKBAR;
import static com.android.quickstep.util.SystemActionConstants.SYSTEM_ACTION_ID_TASKBAR;
@@ -81,6 +81,7 @@
import com.android.quickstep.RecentsActivity;
import com.android.quickstep.SystemUiProxy;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ContextualSearchInvoker;
import com.android.quickstep.util.GroupTask;
@@ -628,7 +629,7 @@
/** Creates a {@link TaskbarUIController} to use with non default displays. */
private TaskbarUIController createTaskbarUIControllerForNonDefaultDisplay(int displayId) {
debugPrimaryTaskbar("createTaskbarUIControllerForNonDefaultDisplay");
- if (RecentsDisplayModel.enableOverviewInWindow()) {
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
RecentsViewContainer rvc = mRecentsDisplayModel.getRecentsWindowManager(displayId);
if (rvc != null) {
return createTaskbarUIControllerForRecentsViewContainer(rvc);
@@ -732,8 +733,7 @@
displayId);
taskbar.updateDeviceProfile(dp);
}
- mSharedState.startTaskbarVariantIsTransient =
- DisplayController.isTransientTaskbar(taskbar);
+ mSharedState.startTaskbarVariantIsTransient = taskbar.isTransientTaskbar();
mSharedState.allAppsVisible = mSharedState.allAppsVisible && isLargeScreenTaskbar;
taskbar.init(mSharedState, duration);
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
index 4a7e4f0..bf73f02 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarScrimViewController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.taskbar.bubbles.BubbleControllers;
-import com.android.launcher3.util.DisplayController;
import com.android.quickstep.SystemUiProxy;
import com.android.systemui.shared.system.QuickStepContract.SystemUiStateFlags;
@@ -93,7 +92,8 @@
// There is no scrim for the bar in the phone mode.
return;
}
- if (isBubbleBarEnabled() && DisplayController.isTransientTaskbar(mActivity)) {
+ boolean isTransient = mActivity.isTransientTaskbar();
+ if (isBubbleBarEnabled() && isTransient) {
// These scrims aren't used if bubble bar & transient taskbar are active.
return;
}
@@ -112,7 +112,7 @@
boolean showScrimForBubbles = bubblesExpanded
&& !mTaskbarVisible
&& isBubbleControllersPresented
- && !DisplayController.isTransientTaskbar(mActivity)
+ && !mActivity.isTransientTaskbar()
&& !bubbleControllers.bubbleStashController.isBubblesShowingOnHome();
return bubblesExpanded && !mControllers.navbarButtonsViewController.isImeVisible()
&& !isShadeVisible
@@ -122,8 +122,8 @@
}
private float computeScrimAlpha() {
- final boolean isPersistentTaskBarVisible =
- mTaskbarVisible && !DisplayController.isTransientTaskbar(mScrimView.getContext());
+ boolean isTransient = mActivity.isTransientTaskbar();
+ final boolean isPersistentTaskBarVisible = mTaskbarVisible && !isTransient;
final boolean manageMenuExpanded =
(mSysUiStateFlags & SYSUI_STATE_BUBBLES_MANAGE_MENU_EXPANDED) != 0;
if (isPersistentTaskBarVisible && manageMenuExpanded) {
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
index f87c21e..fa35a03 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarSpringOnStashController.java
@@ -27,7 +27,6 @@
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.SpringAnimationBuilder;
import com.android.launcher3.taskbar.TaskbarControllers.LoggableTaskbarController;
-import com.android.launcher3.util.DisplayController;
import java.io.PrintWriter;
@@ -47,7 +46,7 @@
public TaskbarSpringOnStashController(TaskbarActivityContext context) {
mContext = context;
- mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+ mIsTransientTaskbar = context.isTransientTaskbar();
mStartVelocityPxPerS = context.getResources()
.getDimension(R.dimen.transient_taskbar_stash_spring_velocity_dp_per_s);
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
index 95724ad..5284edd 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashController.java
@@ -346,7 +346,7 @@
StashedHandleViewController.ALPHA_INDEX_STASHED);
mTaskbarStashedHandleHintScale = stashedHandleController.getStashedHandleHintScale();
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ boolean isTransientTaskbar = mActivity.isTransientTaskbar();
boolean isInSetup = !mActivity.isUserSetupComplete() || setupUIVisible;
boolean isStashedInAppAuto =
isTransientTaskbar && !mTaskbarSharedState.getTaskbarWasPinned();
@@ -367,9 +367,7 @@
// Hide the background while stashed so it doesn't show on fast swipes home
boolean shouldHideTaskbarBackground = mActivity.isPhoneMode() ||
- (enableScalingRevealHomeAnimation()
- && DisplayController.isTransientTaskbar(mActivity)
- && isStashed());
+ (enableScalingRevealHomeAnimation() && isTransientTaskbar && isStashed());
mTaskbarBackgroundAlphaForStash.setValue(shouldHideTaskbarBackground ? 0 : 1);
@@ -414,8 +412,7 @@
if (DisplayController.isPinnedTaskbar(mActivity)) {
return PINNED_TASKBAR_TRANSITION_DURATION;
}
- return DisplayController.isTransientTaskbar(mActivity)
- ? TRANSIENT_TASKBAR_STASH_DURATION
+ return mActivity.isTransientTaskbar() ? TRANSIENT_TASKBAR_STASH_DURATION
: TASKBAR_STASH_DURATION;
}
@@ -509,8 +506,8 @@
* @see android.view.WindowInsets.Type#systemBars()
*/
public int getContentHeightToReportToApps() {
- if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode()
- || DisplayController.isTransientTaskbar(mActivity))) {
+ boolean isTransient = mActivity.isTransientTaskbar();
+ if (mActivity.isUserSetupComplete() && (mActivity.isPhoneGestureNavMode() || isTransient)) {
return getStashedHeight();
}
@@ -577,8 +574,7 @@
*/
public void updateAndAnimateTransientTaskbar(boolean stash, boolean shouldBubblesFollow,
boolean delayTaskbarBackground) {
- if (!DisplayController.isTransientTaskbar(mActivity)
- || mActivity.isBubbleBarOnPhone()) {
+ if (!mActivity.isTransientTaskbar() || mActivity.isBubbleBarOnPhone()) {
return;
}
@@ -646,7 +642,7 @@
/** Toggles the Taskbar's stash state. */
public void toggleTaskbarStash() {
- if (!DisplayController.isTransientTaskbar(mActivity) || !hasAnyFlag(FLAGS_IN_APP)) return;
+ if (!mActivity.isTransientTaskbar() || !hasAnyFlag(FLAGS_IN_APP)) return;
updateAndAnimateTransientTaskbar(!hasAnyFlag(FLAG_STASHED_IN_APP_AUTO));
}
@@ -697,8 +693,7 @@
mAnimator = new AnimatorSet();
addJankMonitorListener(
mAnimator, /* expanding= */ !isStashed, /* tag= */ jankTag);
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
- final float stashTranslation = mActivity.isPhoneMode() || isTransientTaskbar
+ final float stashTranslation = mActivity.isPhoneMode() || mActivity.isTransientTaskbar()
? 0
: (mUnstashedHeight - mStashedHeight);
@@ -722,7 +717,7 @@
return;
}
- if (isTransientTaskbar) {
+ if (mActivity.isTransientTaskbar()) {
createTransientAnimToIsStashed(mAnimator, isStashed, duration,
shouldDelayBackground, animationType);
} else {
@@ -1144,7 +1139,7 @@
boolean stashForBubbles = hasAnyFlag(FLAG_IN_OVERVIEW)
&& hasAnyFlag(systemUiStateFlags, SYSUI_STATE_BUBBLES_EXPANDED)
- && DisplayController.isTransientTaskbar(mActivity);
+ && mActivity.isTransientTaskbar();
updateStateForFlag(FLAG_STASHED_SYSUI,
hasAnyFlag(systemUiStateFlags, SYSUI_STATE_SCREEN_PINNING) || stashForBubbles);
updateStateForFlag(FLAG_STASHED_DEVICE_LOCKED,
@@ -1174,7 +1169,7 @@
* <p>Do not stash if a system gesture is started.
*/
private boolean shouldStashForIme() {
- if (DisplayController.isTransientTaskbar(mActivity)) {
+ if (mActivity.isTransientTaskbar()) {
return false;
}
// Do not stash if in small screen, with 3 button nav, and in landscape.
@@ -1270,7 +1265,7 @@
*/
public void setUpTaskbarSystemAction(boolean visible) {
UI_HELPER_EXECUTOR.execute(() -> {
- if (!visible || !DisplayController.isTransientTaskbar(mActivity)
+ if (!visible || !mActivity.isTransientTaskbar()
|| mActivity.isPhoneMode()) {
mAccessibilityManager.unregisterSystemAction(SYSTEM_ACTION_ID_TASKBAR);
mIsTaskbarSystemActionRegistered = false;
@@ -1321,7 +1316,7 @@
* If false, attempts to re/start the timeout
*/
public void updateTaskbarTimeout(boolean isAutohideSuspended) {
- if (!DisplayController.isTransientTaskbar(mActivity)) {
+ if (!mActivity.isTransientTaskbar()) {
return;
}
if (isAutohideSuspended) {
@@ -1335,9 +1330,7 @@
* Attempts to start timer to auto hide the taskbar based on time.
*/
private void tryStartTaskbarTimeout() {
- if (!DisplayController.isTransientTaskbar(mActivity)
- || mIsStashed
- || mEnableBlockingTimeoutDuringTests) {
+ if (!mActivity.isTransientTaskbar() || mIsStashed || mEnableBlockingTimeoutDuringTests) {
return;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
index deaf024..df10d24 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarStashViaTouchController.kt
@@ -23,7 +23,6 @@
import com.android.launcher3.touch.SingleAxisSwipeDetector
import com.android.launcher3.touch.SingleAxisSwipeDetector.DIRECTION_NEGATIVE
import com.android.launcher3.touch.SingleAxisSwipeDetector.VERTICAL
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.TouchController
import com.android.quickstep.inputconsumers.TaskbarUnstashInputConsumer
@@ -39,7 +38,7 @@
class TaskbarStashViaTouchController(val controllers: TaskbarControllers) : TouchController {
private val activity: TaskbarActivityContext = controllers.taskbarActivityContext
- private val enabled = DisplayController.isTransientTaskbar(activity)
+ private val enabled = activity.isTransientTaskbar
private val swipeDownDetector: SingleAxisSwipeDetector
private val translationCallback = controllers.taskbarTranslationController.transitionCallback
/** Interpolator to apply resistance as user swipes down to the bottom of the screen. */
@@ -67,7 +66,7 @@
val gestureHeight: Int =
ResourceUtils.getNavbarSize(
ResourceUtils.NAVBAR_BOTTOM_GESTURE_SIZE,
- activity.resources
+ activity.resources,
)
gestureHeightYThreshold = (activity.deviceProfile.heightPx - gestureHeight).toFloat()
}
@@ -89,7 +88,7 @@
maxTouchDisplacement,
0f,
maxVisualDisplacement,
- displacementInterpolator
+ displacementInterpolator,
)
)
return false
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
index 5a5d6d0..13fb296 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarTranslationController.java
@@ -29,7 +29,6 @@
import com.android.app.animation.Interpolators;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.SpringAnimationBuilder;
-import com.android.launcher3.util.DisplayController;
import java.io.PrintWriter;
@@ -63,7 +62,7 @@
public TaskbarTranslationController(TaskbarActivityContext context) {
mContext = context;
- mIsTransientTaskbar = DisplayController.isTransientTaskbar(mContext);
+ mIsTransientTaskbar = mContext.isTransientTaskbar();
mCallback = new TransitionCallback();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
index ea0b81e..061a5a1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarUIController.java
@@ -37,7 +37,6 @@
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.popup.SystemShortcut;
import com.android.launcher3.taskbar.bubbles.BubbleBarController;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.SplitConfigurationOptions;
import com.android.quickstep.util.SplitTask;
import com.android.quickstep.views.RecentsView;
@@ -116,9 +115,8 @@
*/
public void hideOverlayWindow() {
mControllers.keyboardQuickSwitchController.closeQuickSwitchView();
-
- if (!DisplayController.isTransientTaskbar(mControllers.taskbarActivityContext)
- || mControllers.taskbarAllAppsController.isOpen()) {
+ boolean isTransientTaskbar = mControllers.taskbarActivityContext.isTransientTaskbar();
+ if (!isTransientTaskbar || mControllers.taskbarAllAppsController.isOpen()) {
mControllers.taskbarOverlayController.hideWindow();
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
index 07b77c9..a6e05c9 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarView.java
@@ -61,7 +61,6 @@
import com.android.launcher3.taskbar.customization.TaskbarAllAppsButtonContainer;
import com.android.launcher3.taskbar.customization.TaskbarDividerContainer;
import com.android.launcher3.uioverrides.PredictedAppIcon;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.quickstep.util.GroupTask;
@@ -184,8 +183,9 @@
setWillNotDraw(false);
mAllAppsButtonContainer = new TaskbarAllAppsButtonContainer(context);
- mAllAppsButtonTranslationOffset = (int) getResources().getDimension(
- mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(isTransientTaskbar()));
+ mAllAppsButtonTranslationOffset = (int) getResources().getDimension(
+ mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
+ mActivityContext.isTransientTaskbar()));
if (enableTaskbarPinning() || enableRecentsInTaskbar()) {
mTaskbarDividerContainer = new TaskbarDividerContainer(context);
@@ -200,10 +200,7 @@
// TODO: Disable touch events on QSB otherwise it can crash.
mQsb = LayoutInflater.from(context).inflate(R.layout.search_container_hotseat, this, false);
- mNumStaticViews =
- ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() && !mActivityContext.isPhoneMode()
- ? addStaticViews()
- : 0;
+ mNumStaticViews = ENABLE_TASKBAR_RECENTS_LAYOUT_TRANSITION.isTrue() ? addStaticViews() : 0;
}
/**
@@ -243,7 +240,7 @@
enableTaskbarPinning() && !mActivityContext.isThreeButtonNav();
availableWidth -= iconSize - (int) getResources().getDimension(
mAllAppsButtonContainer.getAllAppsButtonTranslationXOffset(
- forceTransientTaskbarSize || isTransientTaskbar()));
+ forceTransientTaskbarSize || mActivityContext.isTransientTaskbar()));
++additionalIcons;
return Math.floorDiv(availableWidth, iconSize) + additionalIcons;
@@ -478,7 +475,7 @@
}
// Recents divider takes priority.
- if (!mAddedDividerForRecents && !mActivityContext.areDesktopTasksVisible()) {
+ if (!mAddedDividerForRecents && !mActivityContext.isInDesktopMode()) {
updateAllAppsDivider();
}
}
@@ -1101,11 +1098,6 @@
// Ignore, we just implement Insettable to draw behind system insets.
}
- private boolean isTransientTaskbar() {
- return DisplayController.isTransientTaskbar(mActivityContext)
- && !mActivityContext.isPhoneMode();
- }
-
public boolean areIconsVisible() {
// Consider the overall visibility
return getVisibility() == VISIBLE;
diff --git a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
index a80e2c4..605171a 100644
--- a/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/TaskbarViewController.java
@@ -267,9 +267,8 @@
: mActivity.getDeviceProfile().taskbarHeight;
mTaskbarIconScaleForStash.updateValue(1f);
- float pinningValue = DisplayController.isTransientTaskbar(mActivity)
- ? PINNING_TRANSIENT
- : PINNING_PERSISTENT;
+ float pinningValue =
+ mActivity.isTransientTaskbar() ? PINNING_TRANSIENT : PINNING_PERSISTENT;
mTaskbarIconScaleForPinning.updateValue(pinningValue);
mTaskbarIconTranslationYForPinning.updateValue(pinningValue);
mTaskbarIconTranslationXForPinning.updateValue(pinningValue);
@@ -936,7 +935,7 @@
mOnControllerPreCreateCallback.run();
DeviceProfile taskbarDp = mActivity.getDeviceProfile();
Rect hotseatPadding = launcherDp.getHotseatLayoutPadding(mActivity);
- boolean isTransientTaskbar = DisplayController.isTransientTaskbar(mActivity);
+ boolean isTransientTaskbar = mActivity.isTransientTaskbar();
float scaleUp = ((float) launcherDp.iconSizePx) / taskbarDp.taskbarIconSize;
int borderSpacing = launcherDp.hotseatBorderSpace;
diff --git a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
index c380c8d..c7d42b1 100644
--- a/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/VoiceInteractionWindowController.kt
@@ -22,7 +22,6 @@
import android.view.ViewTreeObserver.InternalInsetsInfo.TOUCHABLE_INSETS_REGION
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.views.BaseDragLayer
import com.android.systemui.animation.ViewRootSync
import java.io.PrintWriter
@@ -41,8 +40,7 @@
class VoiceInteractionWindowController(val context: TaskbarActivityContext) :
TaskbarControllers.LoggableTaskbarController, TaskbarControllers.BackgroundRendererController {
- private val isSeparateBackgroundEnabled =
- !DisplayController.isTransientTaskbar(context) && !context.isPhoneMode
+ private val isSeparateBackgroundEnabled = !context.isTransientTaskbar && !context.isPhoneMode
private val taskbarBackgroundRenderer = TaskbarBackgroundRenderer(context)
private val nonTouchableInsetsComputer =
ViewTreeObserver.OnComputeInternalInsetsListener {
@@ -97,7 +95,7 @@
separateWindowLayoutParams =
context.createDefaultWindowLayoutParams(
TYPE_APPLICATION_OVERLAY,
- TEMP_BACKGROUND_WINDOW_TITLE
+ TEMP_BACKGROUND_WINDOW_TITLE,
)
separateWindowLayoutParams?.isSystemApplicationOverlay = true
}
@@ -163,7 +161,7 @@
// First add the temporary window, then hide the overlapping taskbar background.
context.addWindowView(
separateWindowForTaskbarBackground,
- separateWindowLayoutParams
+ separateWindowLayoutParams,
);
{ controllers.taskbarDragLayerController.setIsBackgroundDrawnElsewhere(true) }
} else {
@@ -179,7 +177,7 @@
ViewRootSync.synchronizeNextDraw(
separateWindowForTaskbarBackground!!,
context.dragLayer,
- onWindowsSynchronized
+ onWindowsSynchronized,
)
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
index 52f7176..f1ccd39 100644
--- a/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/allapps/TaskbarAllAppsViewController.java
@@ -31,7 +31,6 @@
import com.android.launcher3.taskbar.TaskbarStashController;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayContext;
import com.android.launcher3.taskbar.overlay.TaskbarOverlayController;
-import com.android.launcher3.util.DisplayController;
import com.android.systemui.shared.system.InteractionJankMonitorWrapper;
import java.util.Optional;
@@ -90,7 +89,7 @@
}
private void setUpTaskbarStashing() {
- if (DisplayController.isTransientTaskbar(mContext)) {
+ if (mContext.isTransientTaskbar()) {
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, true);
mTaskbarStashController.applyState();
}
@@ -103,7 +102,7 @@
AbstractFloatingView.closeOpenContainer(
mContext, AbstractFloatingView.TYPE_ACTION_POPUP);
- if (DisplayController.isTransientTaskbar(mContext)) {
+ if (mContext.isTransientTaskbar()) {
mTaskbarStashController.updateStateForFlag(FLAG_STASHED_IN_TASKBAR_ALL_APPS, false);
mTaskbarStashController.applyState();
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
index 383f4d2..24e7d99 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarLocationDropTarget.kt
@@ -60,8 +60,6 @@
override fun onDrop(dragObject: DropTarget.DragObject, options: DragOptions) {
val itemInfo = dragObject.dragInfo ?: return
- // TODO(b/397459664) : fix task bar icon animation after drop
- // TODO(b/397459664) : update bubble bar location
bubbleBarDragListener.onLauncherItemDroppedOverBubbleBarDragZone(
bubbleBarLocation,
itemInfo,
@@ -77,8 +75,6 @@
}
override fun onDragExit(dragObject: DropTarget.DragObject) {
- // TODO(b/397459664) : fix the issue for no bubbles, when moving task bar icon out of
- // the bubble bar drag zone drag ends and swipes gesture swipes the overview
if (!isShowingDropTarget) return
isShowingDropTarget = false
bubbleBarDragListener.onLauncherItemDraggedOutsideBubbleBarDropZone()
diff --git a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
index 277dbbf..5b1d3fb 100644
--- a/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/bubbles/BubbleBarViewController.java
@@ -41,15 +41,16 @@
import androidx.annotation.Nullable;
import com.android.app.animation.Interpolators;
+import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.R;
import com.android.launcher3.anim.AnimatedFloat;
import com.android.launcher3.anim.RoundedRectRevealOutlineProvider;
+import com.android.launcher3.dragndrop.DragController;
import com.android.launcher3.model.data.ItemInfo;
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.taskbar.TaskbarActivityContext;
import com.android.launcher3.taskbar.TaskbarControllers;
-import com.android.launcher3.taskbar.TaskbarDragController;
import com.android.launcher3.taskbar.TaskbarInsetsController;
import com.android.launcher3.taskbar.TaskbarSharedState;
import com.android.launcher3.taskbar.TaskbarStashController;
@@ -144,6 +145,7 @@
@Override
public void onLauncherItemDroppedOverBubbleBarDragZone(@NonNull BubbleBarLocation location,
@NonNull ItemInfo itemInfo) {
+ AbstractFloatingView.closeAllOpenViews(mActivity);
if (itemInfo instanceof WorkspaceItemInfo) {
ShortcutInfo shortcutInfo = ((WorkspaceItemInfo) itemInfo).getDeepShortcutInfo();
if (shortcutInfo != null) {
@@ -193,13 +195,13 @@
private boolean mShouldShowEducation;
public boolean mOverflowAdded;
private boolean mIsLocationUpdatedForDropTarget = false;
+ private boolean mWasStashedBeforeEnteringBubbleDragZone = false;
private BubbleBarViewAnimator mBubbleBarViewAnimator;
private final FrameLayout mBubbleBarContainer;
private BubbleBarFlyoutController mBubbleBarFlyoutController;
private BubbleBarPinController mBubbleBarPinController;
private TaskbarSharedState mTaskbarSharedState;
- private TaskbarDragController mTaskbarDragController;
private final BubbleBarLocationDropTarget mBubbleBarLeftDropTarget;
private final BubbleBarLocationDropTarget mBubbleBarRightDropTarget;
private final TimeSource mTimeSource = System::currentTimeMillis;
@@ -236,7 +238,6 @@
/** Initializes controller. */
public void init(TaskbarControllers controllers, BubbleControllers bubbleControllers,
TaskbarViewPropertiesProvider taskbarViewPropertiesProvider) {
- mTaskbarDragController = controllers.taskbarDragController;
mTaskbarSharedState = controllers.getSharedState();
mBubbleStashController = bubbleControllers.bubbleStashController;
mBubbleBarController = bubbleControllers.bubbleBarController;
@@ -273,7 +274,7 @@
mBoundsChangeListener.onBoundsChanged();
}
});
- float pinningValue = DisplayController.isTransientTaskbar(mActivity)
+ float pinningValue = mActivity.isTransientTaskbar()
? PINNING_TRANSIENT
: PINNING_PERSISTENT;
mBubbleBarPinning.updateValue(pinningValue);
@@ -338,8 +339,18 @@
mBubbleBarController.updateBubbleBarLocation(location, source);
}
};
- mTaskbarDragController.addDropTarget(mBubbleBarLeftDropTarget);
- mTaskbarDragController.addDropTarget(mBubbleBarRightDropTarget);
+ }
+
+ /** Adds bubble bar locations drop zones to the drag controller. */
+ public void addBubbleBarDropTargets(DragController<?> dragController) {
+ dragController.addDropTarget(mBubbleBarLeftDropTarget);
+ dragController.addDropTarget(mBubbleBarRightDropTarget);
+ }
+
+ /** Removes bubble bar locations drop zones to the drag controller. */
+ public void removeBubbleBarDropTargets(DragController<?> dragController) {
+ dragController.removeDropTarget(mBubbleBarLeftDropTarget);
+ dragController.removeDropTarget(mBubbleBarRightDropTarget);
}
/** Returns animated float property responsible for pinning transition animation. */
@@ -617,14 +628,30 @@
* updated.
*/
public void onDragItemOverBubbleBarDragZone(@NonNull BubbleBarLocation bubbleBarLocation) {
+ Log.w("BBAnimation", "onDragItemOverBubbleBarDragZone()");
mBarView.showDropTarget(/* isDropTarget = */ true);
boolean isRtl = mBarView.isLayoutRtl();
mIsLocationUpdatedForDropTarget = getBubbleBarLocation().isOnLeft(isRtl)
!= bubbleBarLocation.isOnLeft(isRtl);
- if (mIsLocationUpdatedForDropTarget) {
- animateBubbleBarLocation(bubbleBarLocation);
- }
- if (!hasBubbles()) {
+ mWasStashedBeforeEnteringBubbleDragZone = hasBubbles()
+ && mBubbleStashController.isStashed();
+ if (mWasStashedBeforeEnteringBubbleDragZone) {
+ if (mIsLocationUpdatedForDropTarget) {
+ // bubble bar is stashed an location updated
+ //TODO(b/399678274) add animation
+ mBubbleStashController.showBubbleBarImmediate();
+ animateBubbleBarLocation(bubbleBarLocation);
+ } else {
+ // bubble bar is stashed and location the same - just un-stash
+ mBubbleStashController.showBubbleBar(/* expandBubbles = */ false);
+ }
+ } else if (hasBubbles()) {
+ if (mIsLocationUpdatedForDropTarget) {
+ // bubble bar has bubbles and location is changed - animate bar to the opposite side
+ animateBubbleBarLocation(bubbleBarLocation);
+ }
+ } else {
+ // bubble bar has no bubbles flow just show the empty drop target
mBubbleBarPinController.showDropTarget(bubbleBarLocation);
}
}
@@ -644,12 +671,27 @@
* mode and reset the value returned from {@link #isLocationUpdatedForDropTarget()} to false.
*/
public void onItemDraggedOutsideBubbleBarDropZone() {
+ Log.w("BBAnimation", "onItemDraggedOutsideBubbleBarDropZone()");
mBarView.showDropTarget(/* isDropTarget = */ false);
- if (mIsLocationUpdatedForDropTarget) {
- animateBubbleBarLocation(getBubbleBarLocation());
+ if (mWasStashedBeforeEnteringBubbleDragZone) {
+ if (mIsLocationUpdatedForDropTarget) {
+ // bubble bar was stashed and location updated
+ //TODO(b/399678274) add animation
+ animateBubbleBarLocation(getBubbleBarLocation());
+ mBubbleStashController.stashBubbleBarImmediate();
+ } else {
+ // bubble bar was stashed and location the same - just stash it back
+ mBubbleStashController.stashBubbleBar();
+ }
+ } else if (hasBubbles()) {
+ if (mIsLocationUpdatedForDropTarget) {
+ // bubble bar has bubbles and location was changed - return to the original location
+ animateBubbleBarLocation(getBubbleBarLocation());
+ }
}
mBubbleBarPinController.hideDropTarget();
mIsLocationUpdatedForDropTarget = false;
+ mWasStashedBeforeEnteringBubbleDragZone = false;
}
/**
@@ -657,9 +699,11 @@
* The controller will hide the drop target if there are no bubbles and exit drop target mode.
*/
public void onItemDroppedInBubbleBarDragZone() {
+ Log.w("BBAnimation", "onItemDroppedInBubbleBarDragZone()");
mBarView.showDropTarget(/* isDropTarget = */ false);
mBubbleBarPinController.hideDropTarget();
mIsLocationUpdatedForDropTarget = false;
+ mWasStashedBeforeEnteringBubbleDragZone = false;
}
/**
@@ -1356,8 +1400,6 @@
/** Called when the controller is destroyed. */
public void onDestroy() {
adjustTaskbarAndHotseatToBubbleBarState(/*isBubbleBarExpanded = */false);
- mTaskbarDragController.removeDropTarget(mBubbleBarLeftDropTarget);
- mTaskbarDragController.removeDropTarget(mBubbleBarRightDropTarget);
}
/**
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
index 4932654..82b1295 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarAllAppsButtonContainer.kt
@@ -32,7 +32,6 @@
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarViewCallbacks
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
@@ -69,7 +68,7 @@
)
backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
setIconDrawable(drawable)
- if (!DisplayController.isTransientTaskbar(context)) {
+ if (activityContext.isTransientTaskbar) {
setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
}
setForegroundTint(activityContext.getColor(R.color.all_apps_button_color))
@@ -106,7 +105,7 @@
@DimenRes
fun getAllAppsButtonTranslationXOffset(isTransientTaskbar: Boolean): Int {
- return if (isTransientTaskbar) {
+ return if (isTransientTaskbar && activityContext.isTransientTaskbar) {
R.dimen.transient_taskbar_all_apps_button_translation_x_offset
} else {
R.dimen.taskbar_all_apps_search_button_translation_x_offset
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
index d5f72d5..060ce46 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarDividerContainer.kt
@@ -26,7 +26,6 @@
import com.android.launcher3.Utilities.dpToPx
import com.android.launcher3.taskbar.TaskbarActivityContext
import com.android.launcher3.taskbar.TaskbarViewCallbacks
-import com.android.launcher3.util.DisplayController
import com.android.launcher3.views.ActivityContext
import com.android.launcher3.views.IconButtonView
@@ -52,7 +51,7 @@
backgroundTintList = ColorStateList.valueOf(TRANSPARENT)
val drawable = resources.getDrawable(R.drawable.taskbar_divider_button)
setIconDrawable(drawable)
- if (!DisplayController.isTransientTaskbar(context)) {
+ if (!activityContext.isTransientTaskbar) {
setPadding(dpToPx(activityContext.taskbarSpecsEvaluator.taskbarIconPadding.toFloat()))
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
index f130d29..a14c5a4 100644
--- a/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
+++ b/quickstep/src/com/android/launcher3/taskbar/customization/TaskbarFeatureEvaluator.kt
@@ -19,7 +19,6 @@
import com.android.launcher3.Flags.enableRecentsInTaskbar
import com.android.launcher3.config.FeatureFlags.enableTaskbarPinning
import com.android.launcher3.taskbar.TaskbarActivityContext
-import com.android.launcher3.util.DisplayController
/** Evaluates all the features taskbar can have. */
class TaskbarFeatureEvaluator
@@ -36,7 +35,7 @@
get() = enableTaskbarPinning() || isRecentsEnabled
val isTransient: Boolean
- get() = DisplayController.isTransientTaskbar(taskbarActivityContext)
+ get() = taskbarActivityContext.isTransientTaskbar
val isLandscape: Boolean
get() = taskbarActivityContext.deviceProfile.isLandscape
diff --git a/quickstep/src/com/android/launcher3/taskbar/navbutton/NearestTouchFrame.java b/quickstep/src/com/android/launcher3/taskbar/navbutton/NearestTouchFrame.java
index bbf08bf..844f1af 100644
--- a/quickstep/src/com/android/launcher3/taskbar/navbutton/NearestTouchFrame.java
+++ b/quickstep/src/com/android/launcher3/taskbar/navbutton/NearestTouchFrame.java
@@ -194,6 +194,7 @@
event.offsetLocation(mTouchingChild.getWidth() / 2 - x,
mTouchingChild.getHeight() / 2 - y);
return mTouchingChild.getVisibility() == VISIBLE
+ && mTouchingChild.isAttachedToWindow()
&& mTouchingChild.dispatchTouchEvent(event);
}
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
index 55bb0f9..dd91d17 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayContext.java
@@ -55,7 +55,7 @@
Context windowContext,
TaskbarActivityContext taskbarContext,
TaskbarControllers controllers) {
- super(windowContext);
+ super(windowContext, taskbarContext.isPrimaryDisplay());
mTaskbarContext = taskbarContext;
mOverlayController = controllers.taskbarOverlayController;
mDragController = new TaskbarDragController(this);
@@ -67,6 +67,11 @@
onViewCreated();
}
+ /** Called when the controller is destroyed. */
+ public void onDestroy() {
+ mDragController.onDestroy();
+ }
+
public @Nullable TaskbarSearchSessionController getSearchSessionController() {
return mSearchSessionController;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
index 79cb748..8b01e99 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayController.java
@@ -150,9 +150,13 @@
/** Destroys the controller and any overlay window if present. */
public void onDestroy() {
TaskStackChangeListeners.getInstance().unregisterTaskStackListener(mTaskStackListener);
- Optional.ofNullable(mOverlayContext)
- .map(c -> c.getSystemService(WindowManager.class))
- .ifPresent(m -> m.removeViewImmediate(mOverlayContext.getDragLayer()));
+ Optional.ofNullable(mOverlayContext).ifPresent(c -> {
+ c.onDestroy();
+ WindowManager wm = c.getSystemService(WindowManager.class);
+ if (wm != null) {
+ wm.removeViewImmediate(mOverlayContext.getDragLayer());
+ }
+ });
mOverlayContext = null;
}
diff --git a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
index 669850c..41694ec 100644
--- a/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
+++ b/quickstep/src/com/android/launcher3/taskbar/overlay/TaskbarOverlayDragLayer.java
@@ -34,7 +34,6 @@
import com.android.launcher3.AbstractFloatingView;
import com.android.launcher3.testing.TestLogging;
import com.android.launcher3.testing.shared.TestProtocol;
-import com.android.launcher3.util.DisplayController;
import com.android.launcher3.util.TouchController;
import com.android.launcher3.views.BaseDragLayer;
@@ -147,7 +146,7 @@
* 2) Sets tappableInsets bottom inset to 0.
*/
private WindowInsets updateInsetsDueToStashing(WindowInsets oldInsets) {
- if (!DisplayController.isTransientTaskbar(mContainer)) {
+ if (!mContainer.isTransientTaskbar()) {
return oldInsets;
}
WindowInsets.Builder updatedInsetsBuilder = new WindowInsets.Builder(oldInsets);
diff --git a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
index 914855b..4280baf 100644
--- a/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/DesktopSystemShortcut.kt
@@ -82,6 +82,7 @@
taskKey.numActivities,
taskKey.isTopActivityNoDisplay,
taskKey.isActivityStackTransparent,
+ taskKey.userId,
) -> null
!taskContainer.task.isDockable -> null
diff --git a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
index f97cf9c..3b823f5 100644
--- a/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
+++ b/quickstep/src/com/android/quickstep/ExternalDisplaySystemShortcut.kt
@@ -80,6 +80,7 @@
taskKey.numActivities,
taskKey.isTopActivityNoDisplay,
taskKey.isActivityStackTransparent,
+ taskKey.userId,
) -> null
else -> {
diff --git a/quickstep/src/com/android/quickstep/GestureState.java b/quickstep/src/com/android/quickstep/GestureState.java
index 74aa8e2..c4ba2d5 100644
--- a/quickstep/src/com/android/quickstep/GestureState.java
+++ b/quickstep/src/com/android/quickstep/GestureState.java
@@ -38,10 +38,10 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
-import com.android.launcher3.Flags;
import com.android.launcher3.statemanager.BaseState;
import com.android.launcher3.statemanager.StatefulContainer;
import com.android.quickstep.TopTaskTracker.CachedTaskInfo;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureErrorDetector;
import com.android.quickstep.util.ActiveGestureLog;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
@@ -323,8 +323,7 @@
*/
public boolean useSyntheticRecentsTransition() {
return mRunningTask.isHomeTask()
- && (Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow());
+ && RecentsWindowFlags.Companion.getEnableOverviewInWindow();
}
/**
diff --git a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
index 783ec2c..2ff42cd 100644
--- a/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
+++ b/quickstep/src/com/android/quickstep/LauncherBackAnimationController.java
@@ -220,7 +220,7 @@
mHandler.post(() -> {
LauncherBackAnimationController controller = mControllerRef.get();
if (controller != null) {
- controller.startBack(backEvent);
+ controller.initBackMotion(backEvent);
mProgressAnimator.onBackStarted(backEvent, event -> {
float backProgress = event.getProgress();
controller.mBackProgress =
@@ -270,6 +270,7 @@
}
}
controller.mAnimationFinishedCallback = finishedCallback;
+ controller.startBack();
}
@Override
@@ -294,34 +295,32 @@
mBackCallback = null;
}
- private void startBack(BackMotionEvent backEvent) {
+ private void initBackMotion(BackMotionEvent backEvent) {
// in case we're still animating an onBackCancelled event, let's remove the finish-
// callback from the progress animator to prevent calling finishAnimation() before
// restarting a new animation
- // Side note: startBack is never called during the post-commit phase if the back gesture
- // was committed (not cancelled). BackAnimationController prevents that. Therefore we
- // don't have to handle that case.
+ // Side note: initBackMotion is never called during the post-commit phase if the back
+ // gesture was committed (not cancelled). BackAnimationController prevents that. Therefore
+ // we don't have to handle that case.
mProgressAnimator.removeOnBackCancelledFinishCallback();
mBackInProgress = true;
- RemoteAnimationTarget appTarget = backEvent.getDepartingAnimationTarget();
-
- if (appTarget == null || appTarget.leash == null || !appTarget.leash.isValid()) {
+ mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
+ }
+ private void startBack() {
+ if (mBackTarget == null) {
return;
}
mTransaction
- .show(appTarget.leash)
+ .show(mBackTarget.leash)
.setAnimationTransaction();
- mBackTarget = appTarget;
- mInitialTouchPos.set(backEvent.getTouchX(), backEvent.getTouchY());
-
- mStartRect.set(appTarget.windowConfiguration.getMaxBounds());
+ mStartRect.set(mBackTarget.windowConfiguration.getMaxBounds());
// inset bottom in case of taskbar being present
if (!predictiveBackThreeButtonNav() || mLauncher.getDeviceProfile().isTaskbarPresent
|| DisplayController.getNavigationMode(mLauncher) == NavigationMode.NO_BUTTON) {
- mStartRect.inset(0, 0, 0, appTarget.contentInsets.bottom);
+ mStartRect.inset(0, 0, 0, mBackTarget.contentInsets.bottom);
}
mLauncherTargetView = mQuickstepTransitionManager.findLauncherView(
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
index 984f390..bf87291 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.kt
@@ -30,9 +30,7 @@
import androidx.annotation.VisibleForTesting
import com.android.internal.jank.Cuj
import com.android.launcher3.Flags.enableAltTabKqsOnConnectedDisplays
-import com.android.launcher3.Flags.enableFallbackOverviewInWindow
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
-import com.android.launcher3.Flags.enableLauncherOverviewInWindow
import com.android.launcher3.Flags.enableOverviewCommandHelperTimeout
import com.android.launcher3.PagedView
import com.android.launcher3.logger.LauncherAtom
@@ -53,6 +51,7 @@
import com.android.quickstep.OverviewCommandHelper.CommandType.SHOW
import com.android.quickstep.OverviewCommandHelper.CommandType.TOGGLE
import com.android.quickstep.fallback.window.RecentsDisplayModel
+import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
import com.android.quickstep.util.ActiveGestureLog
import com.android.quickstep.util.ActiveGestureProtoLogProxy
import com.android.quickstep.views.RecentsView
@@ -299,7 +298,7 @@
val focusedDisplayId = focusState.focusedDisplayId
val focusedDisplayUIController: TaskbarUIController? =
- if (RecentsDisplayModel.enableOverviewInWindow()) {
+ if (enableOverviewInWindow) {
Log.d(
TAG,
"Querying RecentsDisplayModel for TaskbarUIController for display: $focusedDisplayId",
@@ -392,9 +391,7 @@
return false
}
- val recentsInWindowFlagSet =
- enableFallbackOverviewInWindow() || enableLauncherOverviewInWindow()
- if (!recentsInWindowFlagSet) {
+ if (!enableOverviewInWindow) {
containerInterface.getCreatedContainer()?.rootView?.let { view ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
@@ -425,7 +422,7 @@
transitionInfo: TransitionInfo?,
) {
Log.d(TAG, "recents animation started: $command")
- if (recentsInWindowFlagSet) {
+ if (enableOverviewInWindow) {
containerInterface.getCreatedContainer()?.rootView?.let { view ->
InteractionJankMonitorWrapper.begin(view, Cuj.CUJ_LAUNCHER_QUICK_SWITCH)
}
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index 7eacef3..7d3a1da 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -24,6 +24,7 @@
import static com.android.launcher3.Flags.enableOverviewOnConnectedDisplays;
import static com.android.launcher3.config.FeatureFlags.SEPARATE_RECENTS_ACTIVITY;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
+import static com.android.quickstep.fallback.window.RecentsWindowFlags.enableLauncherOverviewInWindow;
import static com.android.systemui.shared.system.PackageManagerWrapper.ACTION_PREFERRED_ACTIVITY_CHANGED;
import android.content.ActivityNotFoundException;
@@ -41,7 +42,6 @@
import androidx.annotation.Nullable;
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.dagger.LauncherAppComponent;
@@ -50,6 +50,7 @@
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.SimpleBroadcastReceiver;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.system.PackageManagerWrapper;
@@ -180,7 +181,7 @@
mDefaultDisplayContainerInterface.onAssistantVisibilityChanged(0.f);
}
- if (SEPARATE_RECENTS_ACTIVITY.get() || Flags.enableLauncherOverviewInWindow()) {
+ if (SEPARATE_RECENTS_ACTIVITY.get() || enableLauncherOverviewInWindow.isTrue()) {
mIsDefaultHome = false;
if (defaultHome == null) {
defaultHome = mMyHomeIntent.getComponent();
@@ -203,7 +204,7 @@
unregisterOtherHomeAppUpdateReceiver();
} else {
// The default home app is a different launcher. Use the fallback Overview instead.
- if (Flags.enableLauncherOverviewInWindow() || Flags.enableFallbackOverviewInWindow()) {
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
mDefaultDisplayContainerInterface =
mRecentsDisplayModel.getFallbackWindowInterface(DEFAULT_DISPLAY);
} else {
diff --git a/quickstep/src/com/android/quickstep/RecentTasksList.java b/quickstep/src/com/android/quickstep/RecentTasksList.java
index ac88e5a..cc5b2da 100644
--- a/quickstep/src/com/android/quickstep/RecentTasksList.java
+++ b/quickstep/src/com/android/quickstep/RecentTasksList.java
@@ -38,8 +38,10 @@
import androidx.annotation.VisibleForTesting;
import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.LooperExecutor;
import com.android.launcher3.util.SplitConfigurationOptions;
+import com.android.launcher3.util.window.WindowManagerProxy;
import com.android.quickstep.util.DesktopTask;
import com.android.quickstep.util.ExternalDisplaysKt;
import com.android.quickstep.util.GroupTask;
@@ -70,7 +72,10 @@
/**
* Manages the recent task list from the system, caching it as necessary.
*/
-public class RecentTasksList {
+// TODO: b/401602554 - Consider letting [DesktopTasksController] notify [RecentTasksController] of
+// desk changes to trigger [IRecentTasksListener.onRecentTasksChanged()], instead of implementing
+// [DesktopVisibilityListener].
+public class RecentTasksList implements WindowManagerProxy.DesktopVisibilityListener {
private static final TaskLoadResult INVALID_RESULT = new TaskLoadResult(-1, false, 0);
@@ -78,6 +83,7 @@
private final KeyguardManager mKeyguardManager;
private final LooperExecutor mMainThreadExecutor;
private final SystemUiProxy mSysUiProxy;
+ private final DesktopVisibilityController mDesktopVisibilityController;
// The list change id, increments as the task list changes in the system
private int mChangeId;
@@ -95,13 +101,16 @@
public RecentTasksList(Context context, LooperExecutor mainThreadExecutor,
KeyguardManager keyguardManager, SystemUiProxy sysUiProxy,
- TopTaskTracker topTaskTracker) {
+ TopTaskTracker topTaskTracker,
+ DesktopVisibilityController desktopVisibilityController,
+ DaggerSingletonTracker tracker) {
mContext = context;
mMainThreadExecutor = mainThreadExecutor;
mKeyguardManager = keyguardManager;
mChangeId = 1;
mSysUiProxy = sysUiProxy;
- sysUiProxy.registerRecentTasksListener(new IRecentTasksListener.Stub() {
+ mDesktopVisibilityController = desktopVisibilityController;
+ final IRecentTasksListener recentTasksListener = new IRecentTasksListener.Stub() {
@Override
public void onRecentTasksChanged() throws RemoteException {
mMainThreadExecutor.execute(RecentTasksList.this::onRecentTasksChanged);
@@ -147,7 +156,19 @@
topTaskTracker.onVisibleTasksChanged(visibleTasks);
});
}
- });
+ };
+
+ mSysUiProxy.registerRecentTasksListener(recentTasksListener);
+ tracker.addCloseable(
+ () -> mSysUiProxy.unregisterRecentTasksListener(recentTasksListener));
+
+ if (DesktopModeStatus.enableMultipleDesktops(mContext)) {
+ mDesktopVisibilityController.registerDesktopVisibilityListener(
+ this);
+ tracker.addCloseable(
+ () -> mDesktopVisibilityController.unregisterDesktopVisibilityListener(this));
+ }
+
// 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
@@ -286,6 +307,27 @@
return mRunningTasks;
}
+ @Override
+ public void onDeskAdded(int displayId, int deskId) {
+ onRecentTasksChanged();
+ }
+
+ @Override
+ public void onDeskRemoved(int displayId, int deskId) {
+ onRecentTasksChanged();
+ }
+
+ @Override
+ public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
+ // Should desk activation changes lead to the invalidation of the loaded tasks? The cases
+ // are:
+ // - Switching from one active desk to another.
+ // - Switching from out of a desk session into an active desk.
+ // - Switching from an active desk to a non-desk session.
+ // These changes don't affect the list of desks, nor their contents, so let's ignore them
+ // for now.
+ }
+
private void onRunningTaskAppeared(RunningTaskInfo taskInfo) {
// Make sure this task is not already in the list
for (RunningTaskInfo existingTask : mRunningTasks) {
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
index d7152b5..ecde37b 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationCallbacks.java
@@ -34,9 +34,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.util.Preconditions;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.systemui.shared.recents.model.ThumbnailData;
import com.android.systemui.shared.system.RecentsAnimationControllerCompat;
@@ -113,8 +113,8 @@
boolean isOpeningHome = Arrays.stream(appTargets).filter(app -> app.mode == MODE_OPENING
&& app.windowConfiguration.getActivityType() == ACTIVITY_TYPE_HOME)
.count() > 0;
- if (appCount == 0 && (!(Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow()) || isOpeningHome)) {
+ if (appCount == 0 && (!RecentsWindowFlags.Companion.getEnableOverviewInWindow()
+ || isOpeningHome)) {
ActiveGestureProtoLogProxy.logOnRecentsAnimationStartCancelled();
// Edge case, if there are no closing app targets, then Launcher has nothing to handle
notifyAnimationCanceled();
diff --git a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
index cf7e499..0deb1ca 100644
--- a/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
+++ b/quickstep/src/com/android/quickstep/RecentsAnimationTargets.java
@@ -59,6 +59,10 @@
if (!DesktopModeStatus.canEnterDesktopMode(context)) {
return false;
}
+ // TODO: b/400866688 - Check if we need to update this such that for an empty desk, we
+ // receive a list of apps that contain only the Launcher and the `DesktopWallpaperActivity`
+ // and both are fullscreen windowing mode. A desk can also have transparent modals and
+ // immersive apps which may not have a "freeform" windowing mode.
for (RemoteAnimationTarget target : apps) {
if (target.windowConfiguration.getWindowingMode() == WINDOWING_MODE_FREEFORM) {
return true;
diff --git a/quickstep/src/com/android/quickstep/RecentsFilterState.java b/quickstep/src/com/android/quickstep/RecentsFilterState.java
index c4b0f25..1808a97 100644
--- a/quickstep/src/com/android/quickstep/RecentsFilterState.java
+++ b/quickstep/src/com/android/quickstep/RecentsFilterState.java
@@ -18,6 +18,7 @@
import androidx.annotation.Nullable;
+import com.android.quickstep.util.DesksUtils;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
import com.android.systemui.shared.recents.model.Task;
@@ -117,37 +118,43 @@
* Returns a predicate for filtering out GroupTasks by package name.
*
* @param packageName package name to filter GroupTasks by
- * if null, Predicate filters out desktop tasks with no non-minimized tasks.
+ * if null, Predicate filters out desktop tasks with no non-minimized tasks,
+ * unless the multiple desks feature is enabled, which allows empty desks.
*/
public static Predicate<GroupTask> getFilter(@Nullable String packageName) {
if (packageName == null) {
- return getEmptyDesktopTaskFilter();
+ return getDesktopTaskFilter();
}
return (groupTask) -> (groupTask.containsPackage(packageName)
- && !isDestopTaskWithMinimizedTasksOnly(groupTask));
+ && shouldKeepGroupTask(groupTask));
}
/**
- * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks.
+ * Returns a predicate that filters out desk tasks that contain no non-minimized desktop tasks,
+ * unless the multiple desks feature is enabled, which allows empty desks.
*/
- public static Predicate<GroupTask> getEmptyDesktopTaskFilter() {
- return (groupTask -> !isDestopTaskWithMinimizedTasksOnly(groupTask));
+ public static Predicate<GroupTask> getDesktopTaskFilter() {
+ return (groupTask -> shouldKeepGroupTask(groupTask));
}
/**
- * Whether the provided task is a desktop task with no non-minimized tasks - returns true if the
- * desktop task has no tasks at all.
+ * Returns true if the given `groupTask` should be kept, and false if it should be filtered out.
+ * Desks will be filtered out if they are empty unless the multiple desks feature is enabled.
*
* @param groupTask The group task to check.
*/
- static boolean isDestopTaskWithMinimizedTasksOnly(GroupTask groupTask) {
+ private static boolean shouldKeepGroupTask(GroupTask groupTask) {
if (groupTask.taskViewType != TaskViewType.DESKTOP) {
- return false;
+ return true;
}
+
+ if (DesksUtils.areMultiDesksFlagsEnabled()) {
+ return true;
+ }
+
return groupTask.getTasks().stream()
- .filter(task -> !task.isMinimized)
- .toList().isEmpty();
+ .anyMatch(task -> !task.isMinimized);
}
/**
diff --git a/quickstep/src/com/android/quickstep/RecentsModel.java b/quickstep/src/com/android/quickstep/RecentsModel.java
index 1d83d42..e1adf3d 100644
--- a/quickstep/src/com/android/quickstep/RecentsModel.java
+++ b/quickstep/src/com/android/quickstep/RecentsModel.java
@@ -43,6 +43,7 @@
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.graphics.ThemeManager.ThemeChangeListener;
import com.android.launcher3.icons.IconProvider;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
import com.android.launcher3.util.DaggerSingletonObject;
import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.DisplayController;
@@ -61,6 +62,8 @@
import com.android.systemui.shared.system.TaskStackChangeListener;
import com.android.systemui.shared.system.TaskStackChangeListeners;
+import dagger.Lazy;
+
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.List;
@@ -72,8 +75,6 @@
import javax.inject.Inject;
-import dagger.Lazy;
-
/**
* Singleton class to load and manage recents model.
*/
@@ -104,12 +105,14 @@
DisplayController displayController,
LockedUserState lockedUserState,
Lazy<ThemeManager> themeManagerLazy,
+ DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker
) {
// Lazily inject the ThemeManager and access themeManager once the device is
// unlocked. See b/393248495 for details.
this(context, new IconProvider(context), systemUiProxy, topTaskTracker,
- displayController, lockedUserState,themeManagerLazy, tracker);
+ displayController, lockedUserState, themeManagerLazy, desktopVisibilityController,
+ tracker);
}
@SuppressLint("VisibleForTests")
@@ -120,6 +123,7 @@
DisplayController displayController,
LockedUserState lockedUserState,
Lazy<ThemeManager> themeManagerLazy,
+ DesktopVisibilityController desktopVisibilityController,
DaggerSingletonTracker tracker) {
this(context,
new RecentTasksList(
@@ -127,7 +131,7 @@
MAIN_EXECUTOR,
context.getSystemService(KeyguardManager.class),
systemUiProxy,
- topTaskTracker),
+ topTaskTracker, desktopVisibilityController, tracker),
new TaskIconCache(context, RECENTS_MODEL_EXECUTOR, iconProvider, displayController),
new TaskThumbnailCache(context, RECENTS_MODEL_EXECUTOR),
iconProvider,
@@ -205,7 +209,7 @@
@Override
public int getTasks(@Nullable Consumer<List<GroupTask>> callback) {
return mTaskList.getTasks(false /* loadKeysOnly */, callback,
- RecentsFilterState.getEmptyDesktopTaskFilter());
+ RecentsFilterState.getDesktopTaskFilter());
}
/**
diff --git a/quickstep/src/com/android/quickstep/SystemUiProxy.kt b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
index d6f6540..506f85d 100644
--- a/quickstep/src/com/android/quickstep/SystemUiProxy.kt
+++ b/quickstep/src/com/android/quickstep/SystemUiProxy.kt
@@ -1096,18 +1096,26 @@
// Desktop Mode
//
/** Calls shell to create a new desk (if possible) on the display whose ID is `displayId`. */
- fun createDesktop(displayId: Int) =
+ fun createDesk(displayId: Int) =
executeWithErrorLog({ "Failed call createDesk" }) { desktopMode?.createDesk(displayId) }
/**
* Calls shell to activate the desk whose ID is `deskId` on whatever display it exists on. This
* will bring all tasks on this desk to the front.
*/
- fun activateDesktop(deskId: Int, transition: RemoteTransition?) =
+ fun activateDesk(deskId: Int, transition: RemoteTransition?) =
executeWithErrorLog({ "Failed call activateDesk" }) {
desktopMode?.activateDesk(deskId, transition)
}
+ /** Calls shell to remove the desk whose ID is `deskId`. */
+ fun removeDesk(deskId: Int) =
+ executeWithErrorLog({ "Failed call removeDesk" }) { desktopMode?.removeDesk(deskId) }
+
+ /** Calls shell to remove all the available desks on all displays. */
+ fun removeAllDesks() =
+ executeWithErrorLog({ "Failed call removeAllDesks" }) { desktopMode?.removeAllDesks() }
+
/** Call shell to show all apps active on the desktop */
fun showDesktopApps(displayId: Int, transition: RemoteTransition?) =
executeWithErrorLog({ "Failed call showDesktopApps" }) {
@@ -1159,9 +1167,9 @@
}
/** Call shell to remove the desktop that is on given `displayId` */
- fun removeDesktop(displayId: Int) =
- executeWithErrorLog({ "Failed call removeDesktop" }) {
- desktopMode?.removeDesktop(displayId)
+ fun removeDefaultDeskInDisplay(displayId: Int) =
+ executeWithErrorLog({ "Failed call removeDefaultDeskInDisplay" }) {
+ desktopMode?.removeDefaultDeskInDisplay(displayId)
}
/** Call shell to move a task with given `taskId` to external display. */
diff --git a/quickstep/src/com/android/quickstep/TaskAnimationManager.java b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
index 64a8c25..1c7f23c 100644
--- a/quickstep/src/com/android/quickstep/TaskAnimationManager.java
+++ b/quickstep/src/com/android/quickstep/TaskAnimationManager.java
@@ -42,12 +42,12 @@
import androidx.annotation.UiThread;
import com.android.internal.util.ArrayUtils;
-import com.android.launcher3.Flags;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.taskbar.TaskbarUIController;
import com.android.launcher3.util.DisplayController;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowManager;
import com.android.quickstep.util.ActiveGestureProtoLogProxy;
import com.android.quickstep.util.SystemUiFlagUtils;
@@ -63,7 +63,6 @@
public class TaskAnimationManager implements RecentsAnimationCallbacks.RecentsAnimationListener {
public static final boolean SHELL_TRANSITIONS_ROTATION =
SystemProperties.getBoolean("persist.wm.debug.shell_transit_rotate", false);
-
private final Context mCtx;
private RecentsAnimationController mController;
private RecentsAnimationCallbacks mCallbacks;
@@ -313,8 +312,7 @@
}
if(containerInterface.getCreatedContainer() instanceof RecentsWindowManager
- && (Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow())) {
+ && RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
mRecentsAnimationStartPending = getSystemUiProxy().startRecentsActivity(intent, options,
mCallbacks, gestureState.useSyntheticRecentsTransition());
RecentsDisplayModel.getINSTANCE().get(mCtx)
diff --git a/quickstep/src/com/android/quickstep/TouchInteractionService.java b/quickstep/src/com/android/quickstep/TouchInteractionService.java
index ba662c4..741ae7d 100644
--- a/quickstep/src/com/android/quickstep/TouchInteractionService.java
+++ b/quickstep/src/com/android/quickstep/TouchInteractionService.java
@@ -94,6 +94,7 @@
import com.android.quickstep.OverviewComponentObserver.OverviewChangeListener;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.fallback.window.RecentsWindowSwipeHandler;
import com.android.quickstep.inputconsumers.BubbleBarInputConsumer;
import com.android.quickstep.inputconsumers.OneHandedModeInputConsumer;
@@ -1081,10 +1082,9 @@
}
public AbsSwipeUpHandler.Factory getSwipeUpHandlerFactory() {
- boolean recentsInWindow =
- Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow();
return mOverviewComponentObserver.isHomeAndOverviewSame()
- ? mLauncherSwipeHandlerFactory : (recentsInWindow
+ ? mLauncherSwipeHandlerFactory
+ : (RecentsWindowFlags.Companion.getEnableOverviewInWindow()
? mRecentsWindowSwipeHandlerFactory : mFallbackSwipeHandlerFactory);
}
diff --git a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
index d8662f2..8ec97ed 100644
--- a/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
+++ b/quickstep/src/com/android/quickstep/fallback/FallbackRecentsView.java
@@ -30,7 +30,6 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
-import com.android.launcher3.Flags;
import com.android.launcher3.anim.AnimatorPlaybackController;
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.desktop.DesktopRecentsTransitionController;
@@ -46,6 +45,7 @@
import com.android.quickstep.GestureState;
import com.android.quickstep.RemoteTargetGluer.RemoteTargetHandle;
import com.android.quickstep.fallback.window.RecentsDisplayModel;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.util.SingleTask;
import com.android.quickstep.util.SplitSelectStateController;
@@ -80,9 +80,10 @@
@Override
public BaseContainerInterface<RecentsState, ?> getContainerInterface(int displayId) {
- return (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow())
+ return RecentsWindowFlags.Companion.getEnableOverviewInWindow()
? RecentsDisplayModel.getINSTANCE().get(mContext)
- .getFallbackWindowInterface(displayId) : FallbackActivityInterface.INSTANCE;
+ .getFallbackWindowInterface(displayId)
+ : FallbackActivityInterface.INSTANCE;
}
@Override
@@ -290,8 +291,7 @@
}
// disabling this so app icons aren't drawn on top of recent tasks.
- if (isOverlayEnabled && !(Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow())) {
+ if (isOverlayEnabled && !RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
runActionOnRemoteHandles(remoteTargetHandle ->
remoteTargetHandle.getTaskViewSimulator().setDrawsBelowRecents(true));
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
index 58c6c50..12dc177 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsDisplayModel.kt
@@ -19,7 +19,6 @@
import android.content.Context
import android.view.Display
import androidx.core.util.valueIterator
-import com.android.launcher3.Flags
import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.util.DaggerSingletonObject
@@ -29,6 +28,7 @@
import com.android.quickstep.FallbackWindowInterface
import com.android.quickstep.dagger.QuickstepBaseAppComponent
import com.android.quickstep.fallback.window.RecentsDisplayModel.RecentsDisplayResource
+import com.android.quickstep.fallback.window.RecentsWindowFlags.Companion.enableOverviewInWindow
import java.io.PrintWriter
import javax.inject.Inject
@@ -50,14 +50,10 @@
DaggerSingletonObject<RecentsDisplayModel>(
QuickstepBaseAppComponent::getRecentsDisplayModel
)
-
- @JvmStatic
- fun enableOverviewInWindow() =
- Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()
}
init {
- if (enableOverviewInWindow()) {
+ if (enableOverviewInWindow) {
registerDisplayListener()
tracker.addCloseable { destroy() }
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
index 333571c..d70d7eb 100644
--- a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowContext.kt
@@ -18,7 +18,7 @@
import android.content.Context
import android.graphics.PixelFormat
-import android.view.Display.DEFAULT_DISPLAY
+import android.view.Display
import android.view.ViewGroup
import android.view.WindowManager
import android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
@@ -53,7 +53,7 @@
fun initDeviceProfile() {
deviceProfile =
- if (displayId == DEFAULT_DISPLAY)
+ if (displayId == Display.DEFAULT_DISPLAY)
InvariantDeviceProfile.INSTANCE[this].getDeviceProfile(this)
else InvariantDeviceProfile.INSTANCE[this].createDeviceProfileForSecondaryDisplay(this)
}
diff --git a/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
new file mode 100644
index 0000000..9953154
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/fallback/window/RecentsWindowFlags.kt
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.fallback.window
+
+import android.window.DesktopModeFlags.DesktopModeFlag
+import com.android.launcher3.Flags
+
+class RecentsWindowFlags {
+ companion object {
+ @JvmField
+ val enableLauncherOverviewInWindow: DesktopModeFlag =
+ DesktopModeFlag(Flags::enableLauncherOverviewInWindow, false)
+
+ @JvmField
+ val enableFallbackOverviewInWindow: DesktopModeFlag =
+ DesktopModeFlag(Flags::enableFallbackOverviewInWindow, false)
+
+ @JvmStatic
+ val enableOverviewInWindow
+ get() = enableLauncherOverviewInWindow.isTrue || enableFallbackOverviewInWindow.isTrue
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 7cae5b8..c8cf58c 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -42,7 +42,6 @@
import androidx.annotation.UiThread;
-import com.android.launcher3.Flags;
import com.android.launcher3.R;
import com.android.launcher3.Utilities;
import com.android.launcher3.testing.TestLogging;
@@ -59,6 +58,7 @@
import com.android.quickstep.RecentsAnimationTargets;
import com.android.quickstep.RotationTouchHelper;
import com.android.quickstep.TaskAnimationManager;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.util.CachedEventDispatcher;
import com.android.quickstep.util.MotionPauseDetector;
import com.android.quickstep.util.NavBarPosition;
@@ -438,10 +438,8 @@
notifyGestureStarted(true /*isLikelyToStartNewTask*/);
} else {
// todo differentiate intent based on if we are on home or in app for overview in window
- boolean useHomeIntentForWindow = Flags.enableFallbackOverviewInWindow()
- || Flags.enableLauncherOverviewInWindow();
- Intent intent = new Intent(useHomeIntentForWindow ? mInteractionHandler.getHomeIntent()
- : mInteractionHandler.getLaunchIntent());
+ Intent intent = new Intent(RecentsWindowFlags.Companion.getEnableOverviewInWindow()
+ ? mInteractionHandler.getHomeIntent() : mInteractionHandler.getLaunchIntent());
intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
mInteractionHandler);
diff --git a/quickstep/src/com/android/quickstep/util/DesksUtils.kt b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
new file mode 100644
index 0000000..ccfdbb9
--- /dev/null
+++ b/quickstep/src/com/android/quickstep/util/DesksUtils.kt
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.quickstep.util
+
+import android.content.Context
+import android.window.DesktopExperienceFlags
+import com.android.systemui.shared.recents.model.Task
+
+class DesksUtils {
+ companion object {
+ @JvmStatic
+ fun areMultiDesksFlagsEnabled() =
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_BACKEND.isTrue() &&
+ DesktopExperienceFlags.ENABLE_MULTIPLE_DESKTOPS_FRONTEND.isTrue()
+
+ /** Returns true if this [task] contains the [DesktopWallpaperActivity]. */
+ @JvmStatic
+ fun isDesktopWallpaperTask(context: Context, task: Task): Boolean {
+ val sysUiPackage =
+ context.getResources().getString(com.android.internal.R.string.config_systemUi)
+ val component = task.key.component
+ if (component != null) {
+ return component.className.contains("DesktopWallpaperActivity") &&
+ component.packageName.contains(sysUiPackage)
+ }
+ return false
+ }
+ }
+}
diff --git a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
index 1d035e9..27657b4 100644
--- a/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/DesktopTaskView.kt
@@ -35,6 +35,7 @@
import com.android.launcher3.Flags.enableOverviewIconMenu
import com.android.launcher3.Flags.enableRefactorTaskThumbnail
import com.android.launcher3.R
+import com.android.launcher3.statehandlers.DesktopVisibilityController
import com.android.launcher3.testing.TestLogging
import com.android.launcher3.testing.shared.TestProtocol
import com.android.launcher3.util.RunnableList
@@ -68,6 +69,8 @@
type = TaskViewType.DESKTOP,
thumbnailFullscreenParams = DesktopFullscreenDrawParams(context),
) {
+ var deskId = DesktopVisibilityController.INACTIVE_DESK_ID
+
private val contentViewFullscreenParams = FullscreenDrawParams(context)
private val taskContentViewPool =
@@ -281,6 +284,7 @@
orientedState: RecentsOrientedState,
taskOverlayFactory: TaskOverlayFactory,
) {
+ deskId = desktopTask.deskId
// TODO(b/370495260): Minimized tasks should not be filtered with desktop exploded view
// support.
// Minimized tasks should not be shown in Overview.
@@ -332,12 +336,18 @@
override fun onRecycle() {
super.onRecycle()
+ deskId = DesktopVisibilityController.INACTIVE_DESK_ID
explodeProgress = 0.0f
viewModel = null
visibility = VISIBLE
taskContainers.forEach { removeAndRecycleThumbnailView(it) }
}
+ override fun setOrientationState(orientationState: RecentsOrientedState) {
+ super.setOrientationState(orientationState)
+ iconView.setIconOrientation(orientationState, isGridTask)
+ }
+
@SuppressLint("RtlHardcoded")
override fun updateTaskSize(lastComputedTaskSize: Rect, lastComputedGridTaskSize: Rect) {
super.updateTaskSize(lastComputedTaskSize, lastComputedGridTaskSize)
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index c282e77..1f3fbf9 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -56,6 +56,7 @@
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_CLEAR_ALL;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_DISMISS_SWIPE_UP;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_TASK_LAUNCH_SWIPE_DOWN;
+import static com.android.launcher3.statehandlers.DesktopVisibilityController.INACTIVE_DESK_ID;
import static com.android.launcher3.testing.shared.TestProtocol.DISMISS_ANIMATION_ENDS_MESSAGE;
import static com.android.launcher3.touch.PagedOrientationHandler.CANVAS_TRANSLATE;
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
@@ -64,6 +65,7 @@
import static com.android.launcher3.util.SystemUiController.UI_STATE_FULLSCREEN_TASK;
import static com.android.quickstep.BaseContainerInterface.getTaskDimension;
import static com.android.quickstep.TaskUtils.checkCurrentOrManagedUserId;
+import static com.android.quickstep.util.DesksUtils.areMultiDesksFlagsEnabled;
import static com.android.quickstep.util.LogUtils.splitFailureMessage;
import static com.android.quickstep.views.ClearAllButton.DISMISS_ALPHA;
import static com.android.quickstep.views.OverviewActionsView.HIDDEN_ACTIONS_IN_MENU;
@@ -200,6 +202,7 @@
import com.android.quickstep.TaskViewUtils;
import com.android.quickstep.TopTaskTracker;
import com.android.quickstep.ViewUtils;
+import com.android.quickstep.fallback.window.RecentsWindowFlags;
import com.android.quickstep.orientation.RecentsPagedOrientationHandler;
import com.android.quickstep.recents.data.RecentTasksRepository;
import com.android.quickstep.recents.data.RecentsDeviceProfileRepository;
@@ -253,11 +256,11 @@
import java.util.HashSet;
import java.util.List;
import java.util.Map;
+import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Consumer;
import java.util.stream.Collectors;
-
/**
* A list of recent tasks.
*
@@ -1924,12 +1927,16 @@
return;
}
- int[] currentTaskIds;
TaskView currentTaskView = getTaskViewAt(mCurrentPage);
- if (currentTaskView != null) {
+ int[] currentTaskIds = null;
+ // Track the current DesktopTaskView through [deskId] as a desk can be empty without any
+ // tasks.
+ int currentTaskViewDeskId = INACTIVE_DESK_ID;
+ if (areMultiDesksFlagsEnabled()
+ && currentTaskView instanceof DesktopTaskView desktopTaskView) {
+ currentTaskViewDeskId = desktopTaskView.getDeskId();
+ } else if (currentTaskView != null) {
currentTaskIds = currentTaskView.getTaskIds();
- } else {
- currentTaskIds = new int[0];
}
// Unload existing visible task data
@@ -1941,9 +1948,19 @@
// Save running task ID if it exists before rebinding all taskViews, otherwise the task from
// the runningTaskView currently bound could get assigned to another TaskView
- int[] runningTaskIds = getTaskIdsForTaskViewId(mRunningTaskViewId);
- int[] focusedTaskIds = getTaskIdsForTaskViewId(mFocusedTaskViewId);
+ TaskView runningTaskView = getRunningTaskView();
+ int[] runningTaskIds = null;
+ // Track the running TaskView through [deskId] as a desk can be empty without any tasks.
+ int runningTaskViewDeskId = INACTIVE_DESK_ID;
+ if (areMultiDesksFlagsEnabled()
+ && runningTaskView instanceof DesktopTaskView desktopTaskView) {
+ runningTaskViewDeskId = desktopTaskView.getDeskId();
+ } else if (runningTaskView != null) {
+ runningTaskIds = runningTaskView.getTaskIds();
+ }
+
+ int[] focusedTaskIds = getTaskIdsForTaskViewId(mFocusedTaskViewId);
// Reset the focused task to avoiding initializing TaskViews layout as focused task during
// binding. The focused task view will be updated after all the TaskViews are bound.
setFocusedTaskViewId(INVALID_TASK_ID);
@@ -2050,24 +2067,24 @@
updateTaskSize();
updateChildTaskOrientations();
- TaskView newRunningTaskView = null;
- if (hasAllValidTaskIds(runningTaskIds)) {
+ TaskView newRunningTaskView = mUtils.getDesktopTaskViewForDeskId(runningTaskViewDeskId);
+ if (newRunningTaskView == null) {
// Update mRunningTaskViewId to be the new TaskView that was assigned by binding
// the full list of tasks to taskViews
newRunningTaskView = getTaskViewByTaskIds(runningTaskIds);
- if (newRunningTaskView != null) {
- setRunningTaskViewId(newRunningTaskView.getTaskViewId());
+ }
+ if (newRunningTaskView != null) {
+ setRunningTaskViewId(newRunningTaskView.getTaskViewId());
+ } else {
+ if (mActiveGestureRunningTasks != null) {
+ // This will update mRunningTaskViewId and create a stub view if necessary.
+ // We try to avoid this because it can cause a scroll jump, but it is needed
+ // for cases where the running task isn't included in this load plan (e.g. if
+ // the current running task is excludedFromRecents.)
+ showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan");
+ newRunningTaskView = getRunningTaskView();
} else {
- if (mActiveGestureRunningTasks != null) {
- // This will update mRunningTaskViewId and create a stub view if necessary.
- // We try to avoid this because it can cause a scroll jump, but it is needed
- // for cases where the running task isn't included in this load plan (e.g. if
- // the current running task is excludedFromRecents.)
- showCurrentTask(mActiveGestureRunningTasks, "applyLoadPlan");
- newRunningTaskView = getRunningTaskView();
- } else {
- setRunningTaskViewId(INVALID_TASK_ID);
- }
+ setRunningTaskViewId(INVALID_TASK_ID);
}
}
@@ -2075,11 +2092,12 @@
if (mNextPage != INVALID_PAGE) {
// Restore mCurrentPage but don't call setCurrentPage() as that clobbers the scroll.
mCurrentPage = previousCurrentPage;
- if (hasAllValidTaskIds(currentTaskIds)) {
+ currentTaskView = mUtils.getDesktopTaskViewForDeskId(currentTaskViewDeskId);
+ if (currentTaskView == null) {
currentTaskView = getTaskViewByTaskIds(currentTaskIds);
- if (currentTaskView != null) {
- targetPage = indexOfChild(currentTaskView);
- }
+ }
+ if (currentTaskView != null) {
+ targetPage = indexOfChild(currentTaskView);
}
} else if (previousFocusedPage != INVALID_PAGE) {
targetPage = previousFocusedPage;
@@ -2826,9 +2844,13 @@
/**
* Called when a gesture from an app is starting.
*/
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity` from being
+ // considered in Overview.
public void onGestureAnimationStart(Task[] runningTasks) {
Log.d(TAG, "onGestureAnimationStart - runningTasks: " + Arrays.toString(runningTasks));
mActiveGestureRunningTasks = runningTasks;
+
+
// This needs to be called before the other states are set since it can create the task view
if (mOrientationState.setGestureActive(true)) {
reapplyActiveRotation();
@@ -3062,6 +3084,21 @@
}
/**
+ * Creates a `DesktopTaskView` for the currently active desk on this display, which contains the
+ * gievn `runningTasks`.
+ */
+ private DesktopTaskView createDesktopTaskViewForActiveDesk(Task[] runningTasks) {
+ final int activeDeskId = mUtils.getActiveDeskIdOnThisDisplay();
+ final var desktopTaskView = (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
+
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
+ desktopTaskView.bind(
+ new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
+ mOrientationState, mTaskOverlayFactory);
+ return desktopTaskView;
+ }
+
+ /**
* Creates a task view (if necessary) to represent the task with the {@param runningTaskId}.
*
* All subsequent calls to reload will keep the task as the first item until {@link #reset()}
@@ -3075,20 +3112,14 @@
}
int runningTaskViewId = -1;
- boolean needGroupTaskView = runningTasks.length > 1;
- boolean needDesktopTask = hasDesktopTask(runningTasks);
if (shouldAddStubTaskView(runningTasks)) {
boolean wasEmpty = getChildCount() == 0;
// Add an empty view for now until the task plan is loaded and applied
final TaskView taskView;
+ final boolean needGroupTaskView = runningTasks.length > 1;
+ final boolean needDesktopTask = hasDesktopTask(runningTasks);
if (needDesktopTask) {
- final int activeDeskId =
- DesktopVisibilityController.INSTANCE.get(mContext).getActiveDeskId(
- mContainer.getDisplay().getDisplayId());
- taskView = getTaskViewFromPool(TaskViewType.DESKTOP);
- ((DesktopTaskView) taskView).bind(
- new DesktopTask(activeDeskId, Arrays.asList(runningTasks)),
- mOrientationState, mTaskOverlayFactory);
+ taskView = createDesktopTaskViewForActiveDesk(runningTasks);
} else if (needGroupTaskView) {
taskView = getTaskViewFromPool(TaskViewType.GROUPED);
// When we create a placeholder task view mSplitBoundsConfig will be null, but with
@@ -3114,8 +3145,11 @@
measure(makeMeasureSpec(getMeasuredWidth(), EXACTLY),
makeMeasureSpec(getMeasuredHeight(), EXACTLY));
layout(getLeft(), getTop(), getRight(), getBottom());
- } else if (getTaskViewByTaskId(runningTasks[0].key.id) != null) {
- runningTaskViewId = getTaskViewByTaskId(runningTasks[0].key.id).getTaskViewId();
+ } else {
+ var runningTaskView = getTaskViewByTaskId(runningTasks[0].key.id);
+ if (runningTaskView != null) {
+ runningTaskViewId = runningTaskView.getTaskViewId();
+ }
}
boolean runningTaskTileHidden = mRunningTaskTileHidden;
@@ -3154,6 +3188,10 @@
return true;
}
}
+
+ // A running empty desk will have a single running app for the `DesktopWallpaperActivity`.
+ // TODO: b/401582344 - Implement a way to exclude the `DesktopWallpaperActivity`.
+
return false;
}
@@ -4525,24 +4563,33 @@
return lastVisibleTaskView;
}
- private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
- UI_HELPER_EXECUTOR
- .getHandler()
- .post(
- () -> {
- if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()
- && dismissedTaskView instanceof DesktopTaskView) {
- // TODO: b/362720497 - Use the api with desktop id instead.
- SystemUiProxy.INSTANCE
+ private void removeTaskInternal(@NonNull TaskView dismissedTaskView) {
+ UI_HELPER_EXECUTOR
+ .getHandler()
+ .post(
+ () -> {
+ if (dismissedTaskView instanceof DesktopTaskView desktopTaskView) {
+ removeDesktopTaskView(desktopTaskView);
+ } else {
+ for (int taskId : dismissedTaskView.getTaskIds()) {
+ ActivityManagerWrapper.getInstance().removeTask(taskId);
+ }
+ }
+ });
+ }
+
+ private void removeDesktopTaskView(DesktopTaskView desktopTaskView) {
+ if (areMultiDesksFlagsEnabled()) {
+ SystemUiProxy.INSTANCE
.get(getContext())
- .removeDesktop(mContainer.getDisplay().getDisplayId());
- } else {
- for (int taskId : dismissedTaskView.getTaskIds()) {
- ActivityManagerWrapper.getInstance().removeTask(taskId);
- }
- }
- });
- }
+ .removeDesk(desktopTaskView.getDeskId());
+ } else if (DesktopModeFlags.ENABLE_DESKTOP_WINDOWING_BACK_NAVIGATION.isTrue()) {
+ SystemUiProxy.INSTANCE
+ .get(getContext())
+ .removeDefaultDeskInDisplay(
+ mContainer.getDisplay().getDisplayId());
+ }
+ }
protected void onDismissAnimationEnds() {
AccessibilityManagerCompat.sendTestProtocolEventToTest(getContext(),
@@ -4562,6 +4609,12 @@
mPendingAnimation = anim;
mPendingAnimation.addEndListener(isSuccess -> {
if (isSuccess) {
+ // Remove desktops first, since desks can be empty (so they have no recent tasks),
+ // and closing all tasks on a desk doesn't always necessarily mean that the desk
+ // will be removed. So, there are no guarantees that the below call to
+ // `ActivityManagerWrapper::removeAllRecentTasks()` will be enough.
+ SystemUiProxy.INSTANCE.get(getContext()).removeAllDesks();
+
// Remove all the task views now
finishRecentsAnimation(true /* toRecents */, false /* shouldPip */, () -> {
UI_HELPER_EXECUTOR.getHandler().post(
@@ -4691,7 +4744,7 @@
private void createDesk(View view) {
SystemUiProxy.INSTANCE
.get(getContext())
- .createDesktop(mContainer.getDisplay().getDisplayId());
+ .createDesk(mContainer.getDisplay().getDisplayId());
}
@Override
@@ -6026,7 +6079,7 @@
// mSyncTransactionApplier doesn't get transferred over
runActionOnRemoteHandles(remoteTargetHandle -> {
final TransformParams params = remoteTargetHandle.getTransformParams();
- if (Flags.enableFallbackOverviewInWindow() || Flags.enableLauncherOverviewInWindow()) {
+ if (RecentsWindowFlags.Companion.getEnableOverviewInWindow()) {
params.setHomeBuilderProxy((builder, app, transformParams) -> {
mTmpMatrix.setScale(
1f, 1f, app.localBounds.exactCenterX(), app.localBounds.exactCenterY());
@@ -6924,6 +6977,58 @@
// TODO: b/389209338 - update the AddDesktopButton's visibility on this.
}
+ @Override
+ public void onDeskAdded(int displayId, int deskId) {
+ // Ignore desk changes that don't belong to this display.
+ if (displayId != mContainer.getDisplay().getDisplayId()) {
+ return;
+ }
+
+ if (mUtils.getDesktopTaskViewForDeskId(deskId) != null) {
+ Log.e(TAG, "A task view for this desk has already been added.");
+ return;
+ }
+
+ // We assume that a newly added desk is always empty and gets added to the left of the
+ // `AddNewDesktopButton`.
+ DesktopTaskView desktopTaskView =
+ (DesktopTaskView) getTaskViewFromPool(TaskViewType.DESKTOP);
+ desktopTaskView.bind(new DesktopTask(deskId, new ArrayList<>()),
+ mOrientationState, mTaskOverlayFactory);
+
+ Objects.requireNonNull(mAddDesktopButton);
+ final int insertionIndex = 1 + indexOfChild(mAddDesktopButton);
+ addView(desktopTaskView, insertionIndex);
+
+ updateTaskSize();
+ updateChildTaskOrientations();
+
+ // TODO: b/401002178 - Recalculate the new current page such that the addition of the new
+ // desk does not result in a change in the current scroll page.
+ }
+
+ @Override
+ public void onDeskRemoved(int displayId, int deskId) {
+ // Ignore desk changes that don't belong to this display.
+ if (displayId != mContainer.getDisplay().getDisplayId()) {
+ return;
+ }
+
+ // We need to distinguish between desk removals that are triggered from outside of overview
+ // vs. the ones that were initiated from overview by dismissing the corresponding desktop
+ // task view.
+ var taskView = mUtils.getDesktopTaskViewForDeskId(deskId);
+ if (taskView != null) {
+ dismissTaskView(taskView, true, true);
+ }
+ }
+
+ @Override
+ public void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {
+ // TODO: b/400870600 - We may need to add code here to special case when an empty desk gets
+ // activated, since `RemoteDesktopLaunchTransitionRunner` doesn't always get triggered.
+ }
+
/** Get the color used for foreground scrimming the RecentsView for sharing. */
public static int getForegroundScrimDimColor(Context context) {
return context.getColor(R.color.overview_foreground_scrim_color);
diff --git a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
index 1c37986..29601ef 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
+++ b/quickstep/src/com/android/quickstep/views/RecentsViewUtils.kt
@@ -22,7 +22,11 @@
import androidx.core.view.children
import com.android.launcher3.Flags.enableLargeDesktopWindowingTile
import com.android.launcher3.Flags.enableSeparateExternalDisplayTasks
+import com.android.launcher3.statehandlers.DesktopVisibilityController
+import com.android.launcher3.statehandlers.DesktopVisibilityController.Companion.INACTIVE_DESK_ID
import com.android.launcher3.util.IntArray
+import com.android.quickstep.util.DesksUtils.Companion.areMultiDesksFlagsEnabled
+import com.android.quickstep.util.DesktopTask
import com.android.quickstep.util.GroupTask
import com.android.quickstep.util.isExternalDisplay
import com.android.quickstep.views.RecentsView.RUNNING_TASK_ATTACH_ALPHA
@@ -52,7 +56,12 @@
* @return Sorted list of GroupTasks to be used in the RecentsView.
*/
fun sortDesktopTasksToFront(tasks: List<GroupTask>): List<GroupTask> {
- val (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+ var (desktopTasks, otherTasks) = tasks.partition { it.taskViewType == TaskViewType.DESKTOP }
+ if (areMultiDesksFlagsEnabled()) {
+ // Desk IDs of newer desks are larger than those of older desks, hence we can use them
+ // to sort desks from old to new.
+ desktopTasks = desktopTasks.sortedBy { (it as DesktopTask).deskId }
+ }
return otherTasks + desktopTasks
}
@@ -114,6 +123,22 @@
it.isLargeTile && !(recentsView.isSplitSelectionActive && it is DesktopTaskView)
}
+ /**
+ * Returns the [DesktopTaskView] that matches the given [deskId], or null if it doesn't exist.
+ */
+ fun getDesktopTaskViewForDeskId(deskId: Int): DesktopTaskView? {
+ if (deskId == INACTIVE_DESK_ID) {
+ return null
+ }
+ return taskViews.firstOrNull { it is DesktopTaskView && it.deskId == deskId }
+ as? DesktopTaskView
+ }
+
+ /** Returns the active desk ID of the display that contains the [recentsView] instance. */
+ fun getActiveDeskIdOnThisDisplay(): Int =
+ DesktopVisibilityController.INSTANCE.get(recentsView.context)
+ .getActiveDeskId(recentsView.mContainer.display.displayId)
+
/** Returns the expected focus task. */
fun getFirstNonDesktopTaskView(): TaskView? =
if (enableLargeDesktopWindowingTile()) taskViews.firstOrNull { it !is DesktopTaskView }
@@ -140,6 +165,17 @@
private fun getDeviceProfile() = (recentsView.mContainer as RecentsViewContainer).deviceProfile
fun getRunningTaskExpectedIndex(runningTaskView: TaskView): Int {
+ if (areMultiDesksFlagsEnabled() && runningTaskView is DesktopTaskView) {
+ // Use the [deskId] to keep desks in the order of their creation, as a newer desk
+ // always has a larger [deskId] than the older desks.
+ val desktopTaskView =
+ taskViews.firstOrNull {
+ it is DesktopTaskView &&
+ it.deskId != INACTIVE_DESK_ID &&
+ it.deskId <= runningTaskView.deskId
+ }
+ if (desktopTaskView != null) return recentsView.indexOfChild(desktopTaskView)
+ }
val firstTaskViewIndex = recentsView.indexOfChild(getFirstTaskView())
return if (getDeviceProfile().isTablet) {
var index = firstTaskViewIndex
diff --git a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
index 7c762f4..6bc0666 100644
--- a/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskMenuView.kt
@@ -22,7 +22,6 @@
import android.content.Context
import android.graphics.Outline
import android.graphics.Rect
-import android.graphics.drawable.GradientDrawable
import android.graphics.drawable.ShapeDrawable
import android.graphics.drawable.shapes.RectShape
import android.util.AttributeSet
@@ -165,7 +164,18 @@
recentsViewContainer.layoutInflater.inflate(R.layout.task_view_menu_option, this, false)
as LinearLayout
if (enableOverviewIconMenu()) {
- (menuOptionView.background as GradientDrawable).cornerRadius = 0f
+ menuOptionView.background =
+ ResourcesCompat.getDrawable(
+ resources,
+ R.drawable.app_chip_menu_item_bg,
+ context.theme,
+ )
+ menuOptionView.foreground =
+ ResourcesCompat.getDrawable(
+ resources,
+ R.drawable.app_chip_menu_item_fg,
+ context.theme,
+ )
}
menuOption.setIconAndLabelFor(
menuOptionView.findViewById(R.id.icon),
@@ -366,13 +376,10 @@
deviceProfile = recentsViewContainer.deviceProfile,
taskMenuX = translationX,
taskMenuY =
- when {
- !enableOverviewIconMenu() -> translationY
- // Bottom menu can translate up to show more options. So we use the min
- // translation allowed to calculate its max height.
- taskView.isOnGridBottomRow() -> minMenuTop
- else -> menuTranslationYBeforeOpen
- },
+ // Bottom menu can translate up to show more options. So we use the min
+ // translation allowed to calculate its max height.
+ if (enableOverviewIconMenu() && taskView.isOnGridBottomRow()) minMenuTop
+ else translationY,
)
private fun setOnClosingStartCallback(onClosingStartCallback: Runnable?) {
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.kt b/quickstep/src/com/android/quickstep/views/TaskView.kt
index b7f1d1d..29793ad 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.kt
+++ b/quickstep/src/com/android/quickstep/views/TaskView.kt
@@ -33,7 +33,6 @@
import android.view.Display
import android.view.MotionEvent
import android.view.View
-import android.view.View.OnClickListener
import android.view.ViewGroup
import android.view.ViewStub
import android.view.accessibility.AccessibilityNodeInfo
diff --git a/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java b/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
index c319cb1..cb7254f 100644
--- a/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/launcher3/util/StateManagerProtoLogProxy.java
@@ -16,22 +16,25 @@
package com.android.launcher3.util;
-import static com.android.launcher3.Flags.enableStateManagerProtoLog;
import static com.android.quickstep.util.QuickstepProtoLogGroup.LAUNCHER_STATE_MANAGER;
import static com.android.quickstep.util.QuickstepProtoLogGroup.isProtoLogInitialized;
+import android.window.DesktopModeFlags.DesktopModeFlag;
+
import androidx.annotation.NonNull;
import com.android.internal.protolog.ProtoLog;
+import com.android.launcher3.Flags;
/**
* Proxy class used for StateManager ProtoLog support.
*/
public class StateManagerProtoLogProxy {
-
+ private static final DesktopModeFlag ENABLE_STATE_MANAGER_PROTO_LOG =
+ new DesktopModeFlag(Flags::enableStateManagerProtoLog, true);
public static void logGoToState(
@NonNull Object fromState, @NonNull Object toState, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER,
"StateManager.goToState: fromState: %s, toState: %s, partial trace:\n%s",
fromState,
@@ -41,7 +44,7 @@
public static void logCreateAtomicAnimation(
@NonNull Object fromState, @NonNull Object toState, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.createAtomicAnimation: "
+ "fromState: %s, toState: %s, partial trace:\n%s",
fromState,
@@ -50,17 +53,17 @@
}
public static void logOnStateTransitionStart(@NonNull Object state) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.onStateTransitionStart: state: %s", state);
}
public static void logOnStateTransitionEnd(@NonNull Object state) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER, "StateManager.onStateTransitionEnd: state: %s", state);
}
public static void logCancelAnimation(boolean animationOngoing, @NonNull String trace) {
- if (!enableStateManagerProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_STATE_MANAGER_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(LAUNCHER_STATE_MANAGER,
"StateManager.cancelAnimation: animation ongoing: %b, partial trace:\n%s",
animationOngoing,
diff --git a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
index 2c9ae33..99888fb 100644
--- a/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
+++ b/quickstep/src_protolog/com/android/quickstep/util/RecentsWindowProtoLogProxy.java
@@ -16,14 +16,16 @@
package com.android.quickstep.util;
-import static com.android.launcher3.Flags.enableRecentsWindowProtoLog;
import static com.android.quickstep.util.QuickstepProtoLogGroup.RECENTS_WINDOW;
import static com.android.quickstep.util.QuickstepProtoLogGroup.isProtoLogInitialized;
+import android.window.DesktopModeFlags;
+
import androidx.annotation.NonNull;
import com.android.internal.protolog.ProtoLog;
import com.android.internal.protolog.common.IProtoLogGroup;
+import com.android.launcher3.Flags;
/**
* Proxy class used for Recents Window ProtoLog support.
@@ -35,19 +37,20 @@
* method. Or, if an existing entry needs to be modified, simply update it here.
*/
public class RecentsWindowProtoLogProxy {
-
+ private static final DesktopModeFlags.DesktopModeFlag ENABLE_RECENTS_WINDOW_PROTO_LOG =
+ new DesktopModeFlags.DesktopModeFlag(Flags::enableRecentsWindowProtoLog, true);
public static void logOnStateSetStart(@NonNull String stateName) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "onStateSetStart: %s", stateName);
}
public static void logOnStateSetEnd(@NonNull String stateName) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "onStateSetEnd: %s", stateName);
}
public static void logStartRecentsWindow(boolean isShown, boolean windowViewIsNull) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW,
"Starting recents window: isShow= %b, windowViewIsNull=%b",
isShown,
@@ -55,7 +58,7 @@
}
public static void logCleanup(boolean isShown) {
- if (!enableRecentsWindowProtoLog() || !isProtoLogInitialized()) return;
+ if (!ENABLE_RECENTS_WINDOW_PROTO_LOG.isTrue() || !isProtoLogInitialized()) return;
ProtoLog.d(RECENTS_WINDOW, "Cleaning up recents window: isShow= %b", isShown);
}
}
diff --git a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
index 09c62aa..1c7af14 100644
--- a/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
+++ b/quickstep/tests/multivalentTests/src/com/android/launcher3/model/QuickstepModelDelegateTest.kt
@@ -64,7 +64,7 @@
underTest.mHotseatState.predictor = hotseatPredictor
underTest.mWidgetsRecommendationState.predictor = widgetRecommendationPredictor
underTest.mModel = modelHelper.model
- underTest.mDataModel = BgDataModel()
+ underTest.mDataModel = BgDataModel(WidgetsModel(modelHelper.sandboxContext))
}
@After
diff --git a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
index 70bf6b4..9722e9d 100644
--- a/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
+++ b/quickstep/tests/multivalentTests/src/com/android/quickstep/RecentTasksListTest.java
@@ -49,6 +49,8 @@
import androidx.test.filters.SmallTest;
import com.android.internal.R;
+import com.android.launcher3.statehandlers.DesktopVisibilityController;
+import com.android.launcher3.util.DaggerSingletonTracker;
import com.android.launcher3.util.LooperExecutor;
import com.android.quickstep.util.GroupTask;
import com.android.quickstep.views.TaskViewType;
@@ -102,7 +104,9 @@
.thenReturn(true);
mRecentTasksList = new RecentTasksList(mContext, mockMainThreadExecutor,
- mockKeyguardManager, mSystemUiProxy, mTopTaskTracker);
+ mockKeyguardManager, mSystemUiProxy, mTopTaskTracker,
+ mock(DesktopVisibilityController.class),
+ mock(DaggerSingletonTracker.class));
}
@Test
diff --git a/res/values/strings.xml b/res/values/strings.xml
index cc740a5..3c1a9c6 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -323,6 +323,8 @@
<string name="edit_home_screen">Edit Home Screen</string>
<!-- Text for settings button [CHAR LIMIT=20]-->
<string name="settings_button_text">Home settings</string>
+ <!-- Text for app menu button [CHAR LIMIT=30]-->
+ <string name="all_apps_home_screen">Apps</string>
<!-- Message shown when a feature is disabled by the administrator -->
<string name="msg_disabled_by_admin">Disabled by your admin</string>
@@ -469,6 +471,12 @@
<!-- Accessibility action to show quick actions menu for an icon. [CHAR_LIMIT=30] -->
<string name="action_deep_shortcut">Shortcut Menu</string>
+ <!-- Accessibility name for the app widget resize frame. -->
+ <string name="widget_frame_name">Widget Resize Frame for <xliff:g id="string" example="Clock">%1$s</xliff:g></string>
+
+ <!-- Accessibility action to close the widget resize frame. [CHAR_LIMIT=30] -->
+ <string name="action_close">Close</string>
+
<!-- Accessibility action to dismiss a notification in the shortcuts menu for an icon. [CHAR_LIMIT=30] -->
<string name="action_dismiss_notification">Dismiss</string>
diff --git a/src/com/android/launcher3/AppWidgetResizeFrame.java b/src/com/android/launcher3/AppWidgetResizeFrame.java
index b51e850..213d88f 100644
--- a/src/com/android/launcher3/AppWidgetResizeFrame.java
+++ b/src/com/android/launcher3/AppWidgetResizeFrame.java
@@ -216,6 +216,13 @@
AppWidgetResizeFrame frame = (AppWidgetResizeFrame) launcher.getLayoutInflater()
.inflate(R.layout.app_widget_resize_frame, dl, false);
frame.setupForWidget(widget, cellLayout, dl);
+ // Save widget item info as tag on resize frame; so that, the accessibility delegate can
+ // attach actions that typically happen on widget (e.g. resize, move) also on the resize
+ // frame.
+ frame.setTag(widget.getTag());
+ frame.setAccessibilityDelegate(launcher.getAccessibilityDelegate());
+ frame.setContentDescription(launcher.asContext().getString(R.string.widget_frame_name,
+ widget.getContentDescription()));
((DragLayer.LayoutParams) frame.getLayoutParams()).customPosition = true;
dl.addView(frame);
@@ -235,6 +242,13 @@
}
}
+ /**
+ * Retrieves the view where accessibility actions happen.
+ */
+ public View getViewForAccessibility() {
+ return mWidgetView;
+ }
+
private void setupForWidget(LauncherAppWidgetHostView widgetView, CellLayout cellLayout,
DragLayer dragLayer) {
mCellLayout = cellLayout;
diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java
index 783e82c..30e3a2b 100644
--- a/src/com/android/launcher3/BubbleTextView.java
+++ b/src/com/android/launcher3/BubbleTextView.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.BubbleTextView.RunningAppState.MINIMIZED;
import static com.android.launcher3.Flags.enableContrastTiles;
import static com.android.launcher3.Flags.enableCursorHoverStates;
+import static com.android.launcher3.allapps.AlphabeticalAppsList.PRIVATE_SPACE_PACKAGE;
import static com.android.launcher3.graphics.PreloadIconDrawable.newPendingIcon;
import static com.android.launcher3.icons.BitmapInfo.FLAG_NO_BADGE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_SKIP_USER_BADGE;
@@ -104,6 +105,7 @@
import java.text.NumberFormat;
import java.util.HashMap;
import java.util.Locale;
+import java.util.Objects;
/**
* TextView that draws a bubble behind the text. We cannot use a LineBackgroundSpan
@@ -589,7 +591,9 @@
}
private void setNonPendingIcon(ItemInfoWithIcon info) {
- int flags = shouldUseTheme() ? FLAG_THEMED : info.bitmap.creationFlags;
+ // Set nonPendingIcon acts as a restart which should refresh the flag state when applicable.
+ int flags = Objects.equals(info.getTargetPackage(), PRIVATE_SPACE_PACKAGE)
+ ? info.bitmap.creationFlags : shouldUseTheme() ? FLAG_THEMED : 0;
// Remove badge on icons smaller than 48dp.
if (mHideBadge || mDisplay == DISPLAY_SEARCH_RESULT_SMALL) {
flags |= FLAG_NO_BADGE;
diff --git a/src/com/android/launcher3/LauncherModel.kt b/src/com/android/launcher3/LauncherModel.kt
index add0ad8..02d70ae 100644
--- a/src/com/android/launcher3/LauncherModel.kt
+++ b/src/com/android/launcher3/LauncherModel.kt
@@ -28,11 +28,12 @@
import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.AddWorkspaceItemsTask
import com.android.launcher3.model.AllAppsList
-import com.android.launcher3.model.BaseLauncherBinder
+import com.android.launcher3.model.BaseLauncherBinder.BaseLauncherBinderFactory
import com.android.launcher3.model.BgDataModel
import com.android.launcher3.model.CacheDataUpdatedTask
import com.android.launcher3.model.ItemInstallQueue
import com.android.launcher3.model.LoaderTask
+import com.android.launcher3.model.LoaderTask.LoaderTaskFactory
import com.android.launcher3.model.ModelDbController
import com.android.launcher3.model.ModelDelegate
import com.android.launcher3.model.ModelInitializer
@@ -43,6 +44,8 @@
import com.android.launcher3.model.ReloadStringCacheTask
import com.android.launcher3.model.ShortcutsChangedTask
import com.android.launcher3.model.UserLockStateChangedTask
+import com.android.launcher3.model.UserManagerState
+import com.android.launcher3.model.WorkspaceItemSpaceFinder
import com.android.launcher3.model.data.ItemInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
import com.android.launcher3.pm.UserCache
@@ -70,28 +73,23 @@
@Inject
constructor(
@ApplicationContext private val context: Context,
- private val appProvider: Provider<LauncherAppState>,
+ private val taskControllerProvider: Provider<ModelTaskController>,
private val iconCache: IconCache,
private val prefs: LauncherPrefs,
private val installQueue: ItemInstallQueue,
- appFilter: AppFilter,
@Named("ICONS_DB") dbFileName: String?,
initializer: ModelInitializer,
lifecycle: DaggerSingletonTracker,
val modelDelegate: ModelDelegate,
+ private val mBgAllAppsList: AllAppsList,
+ private val mBgDataModel: BgDataModel,
+ private val loaderFactory: LoaderTaskFactory,
+ private val binderFactory: BaseLauncherBinderFactory,
+ private val spaceFinderFactory: Provider<WorkspaceItemSpaceFinder>,
) {
private val mCallbacksList = ArrayList<BgDataModel.Callbacks>(1)
- // < only access in worker thread >
- private val mBgAllAppsList = AllAppsList(iconCache, appFilter)
-
- /**
- * All the static data should be accessed on the background thread, A lock should be acquired on
- * this object when accessing any data from this model.
- */
- private val mBgDataModel = BgDataModel()
-
val modelDbController = ModelDbController(context)
private val mLock = Any()
@@ -139,7 +137,7 @@
/** Adds the provided items to the workspace. */
fun addAndBindAddedWorkspaceItems(itemList: List<Pair<ItemInfo?, Any?>?>) {
callbacks.forEach { it.preAddApps() }
- enqueueModelUpdateTask(AddWorkspaceItemsTask(itemList))
+ enqueueModelUpdateTask(AddWorkspaceItemsTask(itemList, spaceFinderFactory.get()))
}
fun getWriter(
@@ -299,13 +297,7 @@
// Clear any pending bind-runnables from the synchronized load process.
callbacksList.forEach { MAIN_EXECUTOR.execute(it::clearPendingBinds) }
- val launcherBinder =
- BaseLauncherBinder(
- appProvider.get(),
- mBgDataModel,
- mBgAllAppsList,
- callbacksList,
- )
+ val launcherBinder = binderFactory.createBinder(callbacksList)
if (bindDirectly) {
// Divide the set of loaded items into those that we are binding synchronously,
// and everything else that is to be bound normally (asynchronously).
@@ -317,14 +309,7 @@
launcherBinder.bindWidgets()
return true
} else {
- val task =
- LoaderTask(
- appProvider.get(),
- mBgAllAppsList,
- mBgDataModel,
- this.modelDelegate,
- launcherBinder,
- )
+ val task = loaderFactory.newLoaderTask(launcherBinder, UserManagerState())
mLoaderTask = task
// Always post the loader task, instead of running directly
@@ -425,7 +410,7 @@
/** Called when the labels for the widgets has updated in the icon cache. */
fun onWidgetLabelsUpdated(updatedPackages: HashSet<String?>, user: UserHandle) {
enqueueModelUpdateTask { taskController, dataModel, _ ->
- dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user, appProvider.get())
+ dataModel.widgetsModel.onPackageIconsUpdated(updatedPackages, user)
taskController.bindUpdatedWidgets(dataModel)
}
}
@@ -439,17 +424,7 @@
// Loader has not yet run.
return@execute
}
- task.execute(
- ModelTaskController(
- appProvider.get(),
- mBgDataModel,
- mBgAllAppsList,
- this,
- MAIN_EXECUTOR,
- ),
- mBgDataModel,
- mBgAllAppsList,
- )
+ task.execute(taskControllerProvider.get(), mBgDataModel, mBgAllAppsList)
}
}
@@ -476,7 +451,7 @@
fun refreshAndBindWidgetsAndShortcuts(packageUser: PackageUserKey?) {
enqueueModelUpdateTask { taskController, dataModel, _ ->
- dataModel.widgetsModel.update(taskController.app, packageUser)
+ dataModel.widgetsModel.update(packageUser)
taskController.bindUpdatedWidgets(dataModel)
}
}
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index cb3a0bc..d6ae3a6 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -18,6 +18,7 @@
import static com.android.launcher3.BuildConfig.WIDGET_ON_FIRST_SCREEN;
import static com.android.launcher3.Flags.enableSmartspaceAsAWidget;
+import static com.android.launcher3.graphics.ShapeDelegate.DEFAULT_PATH_SIZE;
import static com.android.launcher3.icons.BitmapInfo.FLAG_THEMED;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_BOTTOM_OR_RIGHT;
import static com.android.launcher3.util.SplitConfigurationOptions.STAGE_POSITION_TOP_OR_LEFT;
@@ -39,6 +40,7 @@
import android.graphics.LightingColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
+import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.Rect;
@@ -98,6 +100,7 @@
import java.util.Collections;
import java.util.List;
import java.util.Locale;
+import java.util.Objects;
import java.util.function.Predicate;
/**
@@ -274,8 +277,12 @@
*/
public static void mapCoordInSelfToDescendant(View descendant, View root, float[] coord) {
sMatrix.reset();
+ //TODO(b/307488755) when implemented this check should be removed
+ if (!Objects.equals(descendant.getWindowId(), root.getWindowId())) {
+ return;
+ }
View v = descendant;
- while(v != root) {
+ while (v != root) {
sMatrix.postTranslate(-v.getScrollX(), -v.getScrollY());
sMatrix.postConcat(v.getMatrix());
sMatrix.postTranslate(v.getLeft(), v.getTop());
@@ -632,7 +639,7 @@
Drawable badge = null;
if ((info instanceof ItemInfoWithIcon iiwi) && !iiwi.getMatchingLookupFlag().useLowRes()) {
- badge = iiwi.bitmap.getBadgeDrawable(context, useTheme);
+ badge = iiwi.bitmap.getBadgeDrawable(context, useTheme, getIconShapeOrNull(context));
}
if (info instanceof PendingAddShortcutInfo) {
@@ -659,8 +666,11 @@
// Only fetch badge if the icon is on workspace
if (info.id != ItemInfo.NO_ID && badge == null) {
badge = appState.getIconCache().getShortcutInfoBadge(si).newIcon(
- context, ThemeManager.INSTANCE.get(context).isIconThemeEnabled()
- ? FLAG_THEMED : 0);
+ context,
+ ThemeManager.INSTANCE.get(context).isIconThemeEnabled()
+ ? FLAG_THEMED : 0,
+ getIconShapeOrNull(context)
+ );
}
}
} else if (info.itemType == LauncherSettings.Favorites.ITEM_TYPE_FOLDER) {
@@ -706,10 +716,11 @@
if (badge == null) {
badge = BitmapInfo.LOW_RES_INFO.withFlags(
- UserCache.INSTANCE.get(context)
- .getUserInfo(info.user)
- .applyBitmapInfoFlags(FlagOp.NO_OP))
- .getBadgeDrawable(context, useTheme);
+ UserCache.INSTANCE.get(context)
+ .getUserInfo(info.user)
+ .applyBitmapInfoFlags(FlagOp.NO_OP)
+ )
+ .getBadgeDrawable(context, useTheme, getIconShapeOrNull(context));
if (badge == null) {
badge = new ColorDrawable(Color.TRANSPARENT);
}
@@ -939,4 +950,18 @@
}
return null;
}
+
+ /**
+ * Returns current icon shape to use for badges if flag is on, otherwise null.
+ */
+ @Nullable
+ public static Path getIconShapeOrNull(Context context) {
+ if (Flags.enableLauncherIconShapes()) {
+ return ThemeManager.INSTANCE.get(context)
+ .getIconShape()
+ .getPath(DEFAULT_PATH_SIZE);
+ } else {
+ return null;
+ }
+ }
}
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 78b53a9..cd91f8e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -25,6 +25,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.AbstractFloatingView;
+import com.android.launcher3.AppWidgetResizeFrame;
import com.android.launcher3.BubbleTextView;
import com.android.launcher3.ButtonDropTarget;
import com.android.launcher3.CellLayout;
@@ -82,6 +83,7 @@
protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
protected static final int RESIZE = R.id.action_resize;
public static final int DEEP_SHORTCUTS = R.id.action_deep_shortcuts;
+ public static final int CLOSE = R.id.action_close;
public LauncherAccessibilityDelegate(Launcher launcher) {
super(launcher);
@@ -104,6 +106,8 @@
RESIZE, R.string.action_resize, KeyEvent.KEYCODE_R));
mActions.put(DEEP_SHORTCUTS, new LauncherAction(DEEP_SHORTCUTS,
R.string.action_deep_shortcut, KeyEvent.KEYCODE_S));
+ mActions.put(CLOSE, new LauncherAction(CLOSE,
+ R.string.action_close, KeyEvent.KEYCODE_X));
}
private static boolean isNotInShortcutMenu(@Nullable View view) {
@@ -137,6 +141,10 @@
}
}
+ if (host instanceof AppWidgetResizeFrame) {
+ out.add(mActions.get(CLOSE));
+ }
+
if (supportAddToWorkSpace(item)) {
out.add(mActions.get(ADD_TO_WORKSPACE));
}
@@ -183,22 +191,28 @@
}
return dragCondition != null;
} else if (action == MOVE) {
- return beginAccessibleDrag(host, item, fromKeyboard);
+ final View itemView = (host instanceof AppWidgetResizeFrame)
+ ? ((AppWidgetResizeFrame) host).getViewForAccessibility()
+ : host;
+ return beginAccessibleDrag(itemView, item, fromKeyboard);
} else if (action == ADD_TO_WORKSPACE) {
return addToWorkspace(item, true /*accessibility*/, null /*finishCallback*/);
} else if (action == MOVE_TO_WORKSPACE) {
return moveToWorkspace(item);
} else if (action == RESIZE) {
+ final View itemView = (host instanceof AppWidgetResizeFrame)
+ ? ((AppWidgetResizeFrame) host).getViewForAccessibility()
+ : host;
final LauncherAppWidgetInfo info = (LauncherAppWidgetInfo) item;
- List<OptionItem> actions = getSupportedResizeActions(host, info);
+ List<OptionItem> actions = getSupportedResizeActions(itemView, info);
Rect pos = new Rect();
- mContext.getDragLayer().getDescendantRectRelativeToSelf(host, pos);
+ mContext.getDragLayer().getDescendantRectRelativeToSelf(itemView, pos);
ArrowPopup popup = OptionsPopupView.show(mContext, new RectF(pos), actions, false);
popup.requestFocus();
popup.addOnCloseCallback(() -> {
- host.requestFocus();
- host.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
- host.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
+ itemView.requestFocus();
+ itemView.sendAccessibilityEvent(TYPE_VIEW_FOCUSED);
+ itemView.performAccessibilityAction(ACTION_ACCESSIBILITY_FOCUS, null);
AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
});
@@ -208,6 +222,11 @@
: (host instanceof BubbleTextHolder
? ((BubbleTextHolder) host).getBubbleText() : null);
return btv != null && PopupContainerWithArrow.showForIcon(btv) != null;
+ } else if (action == CLOSE) {
+ if (host instanceof AppWidgetResizeFrame) {
+ AbstractFloatingView.closeOpenViews(mContext, /* animate= */ false,
+ AbstractFloatingView.TYPE_WIDGET_RESIZE_FRAME);
+ }
} else {
for (ButtonDropTarget dropTarget : mContext.getDropTargetBar().getDropTargets()) {
if (dropTarget.supportsAccessibilityDrop(item, host)
@@ -222,6 +241,10 @@
private List<OptionItem> getSupportedResizeActions(View host, LauncherAppWidgetInfo info) {
List<OptionItem> actions = new ArrayList<>();
+ if (host instanceof AppWidgetResizeFrame) {
+ return getSupportedResizeActions(
+ ((AppWidgetResizeFrame) host).getViewForAccessibility(), info);
+ }
AppWidgetProviderInfo providerInfo = ((LauncherAppWidgetHostView) host).getAppWidgetInfo();
if (providerInfo == null) {
return actions;
diff --git a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
index 5f632fe..870c891 100644
--- a/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
+++ b/src/com/android/launcher3/allapps/AlphabeticalAppsList.java
@@ -64,7 +64,7 @@
AllAppsStore.OnUpdateListener {
public static final String TAG = "AlphabeticalAppsList";
- private static final String PRIVATE_SPACE_PACKAGE = "com.android.privatespace";
+ public static final String PRIVATE_SPACE_PACKAGE = "com.android.privatespace";
private final WorkProfileManager mWorkProviderManager;
diff --git a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
index 06643d3..c499097 100644
--- a/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
+++ b/src/com/android/launcher3/dagger/LauncherBaseAppComponent.java
@@ -28,6 +28,7 @@
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.LauncherIcons.IconPool;
import com.android.launcher3.model.ItemInstallQueue;
+import com.android.launcher3.model.LoaderCursor.LoaderCursorFactory;
import com.android.launcher3.model.WidgetsFilterDataProvider;
import com.android.launcher3.pm.InstallSessionHelper;
import com.android.launcher3.pm.UserCache;
@@ -87,6 +88,8 @@
GridCustomizationsProxy getGridCustomizationsProxy();
WidgetsFilterDataProvider getWidgetsFilterDataProvider();
+ LoaderCursorFactory getLoaderCursorFactory();
+
/** Builder for LauncherBaseAppComponent. */
interface Builder {
@BindsInstance Builder appContext(@ApplicationContext Context context);
diff --git a/src/com/android/launcher3/debug/TestEventEmitter.java b/src/com/android/launcher3/debug/TestEventEmitter.java
index ed3b4bb..db69bc2 100644
--- a/src/com/android/launcher3/debug/TestEventEmitter.java
+++ b/src/com/android/launcher3/debug/TestEventEmitter.java
@@ -34,7 +34,9 @@
RESIZE_FRAME_SHOWING("RESIZE_FRAME_SHOWING"),
WORKSPACE_FINISH_LOADING("WORKSPACE_FINISH_LOADING"),
SPRING_LOADED_STATE_STARTED("SPRING_LOADED_STATE_STARTED"),
- SPRING_LOADED_STATE_COMPLETED("SPRING_LOADED_STATE_COMPLETED");
+ SPRING_LOADED_STATE_COMPLETED("SPRING_LOADED_STATE_COMPLETED"),
+
+ LAUNCHER_STATE_COMPLETED("LAUNCHER_STATE_COMPLETED");
TestEvent(String event) {
}
diff --git a/src/com/android/launcher3/dragndrop/DragController.java b/src/com/android/launcher3/dragndrop/DragController.java
index 613b430..4127dd1 100644
--- a/src/com/android/launcher3/dragndrop/DragController.java
+++ b/src/com/android/launcher3/dragndrop/DragController.java
@@ -540,7 +540,7 @@
accepted = true;
}
}
- final View dropTargetAsView = dropTarget instanceof View ? (View) dropTarget : null;
+ final View dropTargetAsView = dropTarget.getDropView();
dispatchDropComplete(dropTargetAsView, accepted);
}
diff --git a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
index b80238c..e4e4b90 100644
--- a/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
+++ b/src/com/android/launcher3/graphics/LauncherPreviewRenderer.java
@@ -80,8 +80,10 @@
import com.android.launcher3.dagger.LauncherAppModule;
import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.model.BaseLauncherBinder.BaseLauncherBinderFactory;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
+import com.android.launcher3.model.LoaderTask.LoaderTaskFactory;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.CollectionInfo;
import com.android.launcher3.model.data.FolderInfo;
@@ -605,6 +607,10 @@
@Component(modules = LauncherAppModule.class)
public interface PreviewAppComponent extends LauncherAppComponent {
+ LoaderTaskFactory getLoaderTaskFactory();
+ BaseLauncherBinderFactory getBaseLauncherBinderFactory();
+ BgDataModel getDataModel();
+
/** Builder for NexusLauncherAppComponent. */
@Component.Builder
interface Builder extends LauncherAppComponent.Builder {
diff --git a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
index d425f03..5a9b9c2 100644
--- a/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
+++ b/src/com/android/launcher3/graphics/PreviewSurfaceRenderer.java
@@ -58,19 +58,21 @@
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.Workspace;
+import com.android.launcher3.dagger.LauncherComponentProvider;
+import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewAppComponent;
import com.android.launcher3.graphics.LauncherPreviewRenderer.PreviewContext;
-import com.android.launcher3.model.BaseLauncherBinder;
import com.android.launcher3.model.BgDataModel;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.LoaderTask;
import com.android.launcher3.model.ModelDbController;
+import com.android.launcher3.model.UserManagerState;
import com.android.launcher3.util.ComponentKey;
import com.android.launcher3.util.RunnableList;
import com.android.launcher3.util.Themes;
import com.android.launcher3.widget.LocalColorExtractor;
import com.android.systemui.shared.Flags;
-import java.util.ArrayList;
+import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;
@@ -338,38 +340,32 @@
// Start the migration
PreviewContext previewContext =
new PreviewContext(inflationContext, mGridName, mShapeKey);
+ PreviewAppComponent appComponent =
+ (PreviewAppComponent) LauncherComponentProvider.get(previewContext);
- BgDataModel bgModel = new BgDataModel();
- new LoaderTask(
- LauncherAppState.getInstance(previewContext),
- /* bgAllAppsList= */ null,
- bgModel,
- LauncherAppState.getInstance(previewContext).getModel().getModelDelegate(),
- new BaseLauncherBinder(LauncherAppState.getInstance(previewContext), bgModel,
- /* bgAllAppsList= */ null, new Callbacks[0])) {
+ LoaderTask task = appComponent.getLoaderTaskFactory().newLoaderTask(
+ appComponent.getBaseLauncherBinderFactory().createBinder(new Callbacks[0]),
+ new UserManagerState());
- @Override
- public void run() {
- InvariantDeviceProfile idp = LauncherAppState.getIDP(previewContext);
- DeviceProfile deviceProfile = idp.getDeviceProfile(previewContext);
- String query =
- LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
- + " or " + LauncherSettings.Favorites.CONTAINER + " = "
- + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
- if (deviceProfile.isTwoPanels) {
- query += " or " + LauncherSettings.Favorites.SCREEN + " = "
- + Workspace.SECOND_SCREEN_ID;
- }
- loadWorkspace(new ArrayList<>(), query, null, null);
+ InvariantDeviceProfile idp = appComponent.getIDP();
+ DeviceProfile deviceProfile = idp.getDeviceProfile(previewContext);
+ String query =
+ LauncherSettings.Favorites.SCREEN + " = " + Workspace.FIRST_SCREEN_ID
+ + " or " + LauncherSettings.Favorites.CONTAINER + " = "
+ + LauncherSettings.Favorites.CONTAINER_HOTSEAT;
+ if (deviceProfile.isTwoPanels) {
+ query += " or " + LauncherSettings.Favorites.SCREEN + " = "
+ + Workspace.SECOND_SCREEN_ID;
+ }
- final SparseArray<Size> spanInfo = getLoadedLauncherWidgetInfo();
- MAIN_EXECUTOR.execute(() -> {
- renderView(previewContext, mBgDataModel, mWidgetProvidersMap, spanInfo,
- idp);
- mLifeCycleTracker.add(previewContext::onDestroy);
- });
- }
- }.run();
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap = new HashMap<>();
+ task.loadWorkspaceForPreview(query, widgetProviderInfoMap);
+ final SparseArray<Size> spanInfo = getLoadedLauncherWidgetInfo();
+ MAIN_EXECUTOR.execute(() -> {
+ renderView(previewContext, appComponent.getDataModel(), widgetProviderInfoMap,
+ spanInfo, idp);
+ mLifeCycleTracker.add(previewContext::onDestroy);
+ });
} else {
LauncherAppState.getInstance(inflationContext).getModel().loadAsync(dataModel -> {
if (dataModel != null) {
@@ -420,7 +416,6 @@
view.setTranslationY((mHeight - scale * view.getHeight()) / 2);
}
-
if (!Flags.newCustomizationPickerUi()) {
view.setAlpha(mSkipAnimations ? 1 : 0);
view.animate().alpha(1)
@@ -477,5 +472,4 @@
MAIN_EXECUTOR.execute(mLifecycleTracker::executeAllAndDestroy);
}
}
-
}
diff --git a/src/com/android/launcher3/graphics/ShapeDelegate.kt b/src/com/android/launcher3/graphics/ShapeDelegate.kt
index 9033eac..7c04292 100644
--- a/src/com/android/launcher3/graphics/ShapeDelegate.kt
+++ b/src/com/android/launcher3/graphics/ShapeDelegate.kt
@@ -203,7 +203,11 @@
start =
poly.transformed(
Matrix().apply {
- setRectToRect(RectF(0f, 0f, 100f, 100f), RectF(startRect), FILL)
+ setRectToRect(
+ RectF(0f, 0f, DEFAULT_PATH_SIZE, DEFAULT_PATH_SIZE),
+ RectF(startRect),
+ FILL,
+ )
}
),
end =
@@ -281,7 +285,10 @@
PathParser.createPathFromPathData(shapeStr).apply {
transform(
Matrix().apply {
- setScale(AREA_CALC_SIZE / 100f, AREA_CALC_SIZE / 100f)
+ setScale(
+ AREA_CALC_SIZE / DEFAULT_PATH_SIZE,
+ AREA_CALC_SIZE / DEFAULT_PATH_SIZE,
+ )
}
)
}
diff --git a/src/com/android/launcher3/logging/StatsLogManager.java b/src/com/android/launcher3/logging/StatsLogManager.java
index 74d5098..4715132 100644
--- a/src/com/android/launcher3/logging/StatsLogManager.java
+++ b/src/com/android/launcher3/logging/StatsLogManager.java
@@ -166,6 +166,9 @@
@UiEvent(doc = "User tapped or long pressed on settings icon inside launcher settings.")
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS(463),
+ @UiEvent(doc = "User tapped or long pressed on apps icon inside launcher settings.")
+ LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS(2204),
+
@UiEvent(doc = "User tapped or long pressed on widget tray icon inside launcher settings.")
LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS(464),
diff --git a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
index ddbbdc7..dfba4bb 100644
--- a/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
+++ b/src/com/android/launcher3/model/AddWorkspaceItemsTask.java
@@ -66,13 +66,6 @@
/**
* @param itemList items to add on the workspace
- */
- public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList) {
- this(itemList, new WorkspaceItemSpaceFinder());
- }
-
- /**
- * @param itemList items to add on the workspace
* @param itemSpaceFinder inject WorkspaceItemSpaceFinder dependency for testing
*/
public AddWorkspaceItemsTask(@NonNull final List<Pair<ItemInfo, Object>> itemList,
@@ -91,7 +84,7 @@
final ArrayList<ItemInfo> addedItemsFinal = new ArrayList<>();
final IntArray addedWorkspaceScreensFinal = new IntArray();
- final Context context = taskController.getApp().getContext();
+ final Context context = taskController.getContext();
synchronized (dataModel) {
IntArray workspaceScreens = dataModel.collectWorkspaceScreens();
@@ -133,7 +126,7 @@
for (ItemInfo item : filteredItems) {
// Find appropriate space for the item.
- int[] coords = mItemSpaceFinder.findSpaceForItem(taskController.getApp(), dataModel,
+ int[] coords = mItemSpaceFinder.findSpaceForItem(
workspaceScreens, addedWorkspaceScreensFinal, item.spanX, item.spanY);
int screenId = coords[0];
@@ -192,7 +185,7 @@
continue;
}
- IconCache cache = taskController.getApp().getIconCache();
+ IconCache cache = taskController.getIconCache();
WorkspaceItemInfo wii = (WorkspaceItemInfo) itemInfo;
wii.title = "";
wii.bitmap = cache.getDefaultIcon(item.user);
diff --git a/src/com/android/launcher3/model/AllAppsList.java b/src/com/android/launcher3/model/AllAppsList.java
index 98f9afd..2311239 100644
--- a/src/com/android/launcher3/model/AllAppsList.java
+++ b/src/com/android/launcher3/model/AllAppsList.java
@@ -34,6 +34,7 @@
import com.android.launcher3.AppFilter;
import com.android.launcher3.compat.AlphabeticIndexCompat;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.data.AppInfo;
@@ -53,11 +54,13 @@
import java.util.function.Consumer;
import java.util.function.Predicate;
+import javax.inject.Inject;
+
/**
* Stores the list of all applications for the all apps view.
*/
-@SuppressWarnings("NewApi")
+@LauncherAppSingleton
public class AllAppsList {
private static final String TAG = "AllAppsList";
@@ -70,10 +73,10 @@
public final ArrayList<AppInfo> data = new ArrayList<>(DEFAULT_APPLICATIONS_NUMBER);
@NonNull
- private IconCache mIconCache;
+ private final IconCache mIconCache;
@NonNull
- private AppFilter mAppFilter;
+ private final AppFilter mAppFilter;
private boolean mDataChanged = false;
private Consumer<AppInfo> mRemoveListener = NO_OP_CONSUMER;
@@ -92,6 +95,7 @@
/**
* Boring constructor.
*/
+ @Inject
public AllAppsList(IconCache iconCache, AppFilter appFilter) {
mIconCache = iconCache;
mAppFilter = appFilter;
diff --git a/src/com/android/launcher3/model/BaseLauncherBinder.java b/src/com/android/launcher3/model/BaseLauncherBinder.java
index 262bf67..c4bbae4 100644
--- a/src/com/android/launcher3/model/BaseLauncherBinder.java
+++ b/src/com/android/launcher3/model/BaseLauncherBinder.java
@@ -26,6 +26,7 @@
import static com.android.launcher3.util.Executors.MAIN_EXECUTOR;
import static com.android.launcher3.util.Executors.MODEL_EXECUTOR;
+import android.content.Context;
import android.os.Trace;
import android.util.Log;
import android.util.Pair;
@@ -34,11 +35,12 @@
import androidx.annotation.NonNull;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherModel.CallbackTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.celllayout.CellPosMapper;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.model.BgDataModel.Callbacks;
import com.android.launcher3.model.BgDataModel.FixedContainerItems;
import com.android.launcher3.model.data.AppInfo;
@@ -55,6 +57,10 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
@@ -76,7 +82,9 @@
protected final LooperExecutor mUiExecutor;
- protected final LauncherAppState mApp;
+ private final Context mContext;
+ private final InvariantDeviceProfile mIDP;
+ private final LauncherModel mModel;
protected final BgDataModel mBgDataModel;
private final AllAppsList mBgAllAppsList;
@@ -84,10 +92,18 @@
private int mMyBindingId;
- public BaseLauncherBinder(LauncherAppState app, BgDataModel dataModel,
- AllAppsList allAppsList, Callbacks[] callbacksList) {
+ @AssistedInject
+ public BaseLauncherBinder(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ BgDataModel dataModel,
+ AllAppsList allAppsList,
+ @Assisted Callbacks[] callbacksList) {
mUiExecutor = MAIN_EXECUTOR;
- mApp = app;
+ mContext = context;
+ mIDP = idp;
+ mModel = model;
mBgDataModel = dataModel;
mBgAllAppsList = allAppsList;
mCallbacksList = callbacksList;
@@ -110,15 +126,14 @@
mBgDataModel.extraItems.forEach(extraItems::add);
if (incrementBindId) {
mBgDataModel.lastBindId++;
- mBgDataModel.lastLoadId = mApp.getModel().getLastLoadId();
+ mBgDataModel.lastLoadId = mModel.getLastLoadId();
}
mMyBindingId = mBgDataModel.lastBindId;
workspaceItemCount = mBgDataModel.itemsIdMap.size();
}
for (Callbacks cb : mCallbacksList) {
- new UnifiedWorkspaceBinder(cb, mUiExecutor, mApp, mBgDataModel, mMyBindingId,
- itemsIdMap, extraItems, orderedScreenIds)
+ new UnifiedWorkspaceBinder(cb, itemsIdMap, extraItems, orderedScreenIds)
.bind(isBindSync, workspaceItemCount);
}
} finally {
@@ -162,9 +177,8 @@
if (!WIDGETS_ENABLED) {
return;
}
- List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mApp.getContext())
+ List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mContext)
.build(mBgDataModel.widgetsModel.getWidgetsByPackageItemForPicker());
-
executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor);
}
@@ -181,10 +195,9 @@
/**
* Sorts the set of items by hotseat, workspace (spatially from top to bottom, left to right)
*/
- protected void sortWorkspaceItemsSpatially(InvariantDeviceProfile profile,
- ArrayList<ItemInfo> workspaceItems) {
- final int screenCols = profile.numColumns;
- final int screenCellCount = profile.numColumns * profile.numRows;
+ protected void sortWorkspaceItemsSpatially(ArrayList<ItemInfo> workspaceItems) {
+ final int screenCols = mIDP.numColumns;
+ final int screenCellCount = mIDP.numColumns * mIDP.numRows;
Collections.sort(workspaceItems, (lhs, rhs) -> {
if (lhs.container == rhs.container) {
// Within containers, order by their spatial position in that container
@@ -240,30 +253,18 @@
private class UnifiedWorkspaceBinder {
- private final Executor mUiExecutor;
private final Callbacks mCallbacks;
- private final LauncherAppState mApp;
- private final BgDataModel mBgDataModel;
-
- private final int mMyBindingId;
private final IntSparseArrayMap<ItemInfo> mItemIdMap;
private final IntArray mOrderedScreenIds;
private final ArrayList<FixedContainerItems> mExtraItems;
- UnifiedWorkspaceBinder(Callbacks callbacks,
- Executor uiExecutor,
- LauncherAppState app,
- BgDataModel bgDataModel,
- int myBindingId,
+ UnifiedWorkspaceBinder(
+ Callbacks callbacks,
IntSparseArrayMap<ItemInfo> itemIdMap,
ArrayList<FixedContainerItems> extraItems,
IntArray orderedScreenIds) {
mCallbacks = callbacks;
- mUiExecutor = uiExecutor;
- mApp = app;
- mBgDataModel = bgDataModel;
- mMyBindingId = myBindingId;
mItemIdMap = itemIdMap;
mExtraItems = extraItems;
mOrderedScreenIds = orderedScreenIds;
@@ -289,9 +290,8 @@
(WIDGET_FILTER.test(item) ? otherAppWidgets : otherWorkspaceItems).add(item);
}
});
- final InvariantDeviceProfile idp = mApp.getInvariantDeviceProfile();
- sortWorkspaceItemsSpatially(idp, currentWorkspaceItems);
- sortWorkspaceItemsSpatially(idp, otherWorkspaceItems);
+ sortWorkspaceItemsSpatially(currentWorkspaceItems);
+ sortWorkspaceItemsSpatially(otherWorkspaceItems);
// Tell the workspace that we're about to start binding items
executeCallbacksTask(c -> {
@@ -352,7 +352,7 @@
executeCallbacksTask(c -> c.bindStringCache(cacheClone), pendingExecutor);
executeCallbacksTask(c -> c.finishBindingItems(currentScreenIds), pendingExecutor);
- pendingExecutor.execute(() -> ItemInstallQueue.INSTANCE.get(mApp.getContext())
+ pendingExecutor.execute(() -> ItemInstallQueue.INSTANCE.get(mContext)
.resumeModelPush(FLAG_LOADER_RUNNING));
}
@@ -367,8 +367,8 @@
return;
}
- ModelWriter writer = mApp.getModel()
- .getWriter(false /* verifyChanges */, CellPosMapper.DEFAULT, null);
+ ModelWriter writer = mModel.getWriter(
+ false /* verifyChanges */, CellPosMapper.DEFAULT, null);
List<Pair<ItemInfo, View>> bindItems = items.stream()
.map(i -> Pair.create(i, inflater.inflateItem(i, writer, null)))
.collect(Collectors.toList());
@@ -398,4 +398,9 @@
});
}
}
+
+ @AssistedFactory
+ public interface BaseLauncherBinderFactory {
+ BaseLauncherBinder createBinder(Callbacks[] callbacks);
+ }
}
diff --git a/src/com/android/launcher3/model/BgDataModel.java b/src/com/android/launcher3/model/BgDataModel.java
index d9eccaf..ea9f36c 100644
--- a/src/com/android/launcher3/model/BgDataModel.java
+++ b/src/com/android/launcher3/model/BgDataModel.java
@@ -46,6 +46,7 @@
import com.android.launcher3.BuildConfig;
import com.android.launcher3.Workspace;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.LauncherAppSingleton;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.CollectionInfo;
@@ -79,9 +80,15 @@
import java.util.stream.Collectors;
import java.util.stream.Stream;
+import javax.inject.Inject;
+
/**
* All the data stored in-memory and managed by the LauncherModel
+ *
+ * All the static data should be accessed on the background thread, A lock should be acquired on
+ * this object when accessing any data from this model.
*/
+@LauncherAppSingleton
public class BgDataModel {
private static final String TAG = "BgDataModel";
@@ -105,7 +112,7 @@
/**
* Entire list of widgets.
*/
- public final WidgetsModel widgetsModel = new WidgetsModel();
+ public final WidgetsModel widgetsModel;
/**
* Cache for strings used in launcher
@@ -124,6 +131,11 @@
public boolean isFirstPagePinnedItemEnabled = QSB_ON_FIRST_SCREEN
&& !enableSmartspaceRemovalToggle();
+ @Inject
+ public BgDataModel(WidgetsModel widgetsModel) {
+ this.widgetsModel = widgetsModel;
+ }
+
/**
* Clears all the data
*/
diff --git a/src/com/android/launcher3/model/CacheDataUpdatedTask.java b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
index 48934e2..f740b49 100644
--- a/src/com/android/launcher3/model/CacheDataUpdatedTask.java
+++ b/src/com/android/launcher3/model/CacheDataUpdatedTask.java
@@ -59,7 +59,7 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- IconCache iconCache = taskController.getApp().getIconCache();
+ IconCache iconCache = taskController.getIconCache();
ArrayList<ItemInfo> updatedItems = new ArrayList<>();
synchronized (dataModel) {
diff --git a/src/com/android/launcher3/model/FirstScreenBroadcast.java b/src/com/android/launcher3/model/FirstScreenBroadcast.java
index f443f81..829e0fe 100644
--- a/src/com/android/launcher3/model/FirstScreenBroadcast.java
+++ b/src/com/android/launcher3/model/FirstScreenBroadcast.java
@@ -45,9 +45,9 @@
import java.util.ArrayList;
import java.util.Collections;
-import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
+import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@@ -76,9 +76,9 @@
private static final String VERIFICATION_TOKEN_EXTRA = "verificationToken";
- private final HashMap<PackageUserKey, SessionInfo> mSessionInfoForPackage;
+ private final Map<PackageUserKey, SessionInfo> mSessionInfoForPackage;
- public FirstScreenBroadcast(HashMap<PackageUserKey, SessionInfo> sessionInfoForPackage) {
+ public FirstScreenBroadcast(Map<PackageUserKey, SessionInfo> sessionInfoForPackage) {
mSessionInfoForPackage = sessionInfoForPackage;
}
diff --git a/src/com/android/launcher3/model/LoaderCursor.java b/src/com/android/launcher3/model/LoaderCursor.java
index bd8c36b..77d0d76 100644
--- a/src/com/android/launcher3/model/LoaderCursor.java
+++ b/src/com/android/launcher3/model/LoaderCursor.java
@@ -45,13 +45,14 @@
import com.android.launcher3.Flags;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.Workspace;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.logging.FileLog;
import com.android.launcher3.model.data.AppInfo;
@@ -70,6 +71,10 @@
import com.android.launcher3.util.PackageManagerHelper;
import com.android.launcher3.util.UserIconInfo;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.net.URISyntaxException;
import java.security.InvalidParameterException;
@@ -82,8 +87,8 @@
private final LongSparseArray<UserHandle> allUsers;
- private final LauncherAppState mApp;
private final Context mContext;
+ private final LauncherModel mModel;
private final PackageManagerHelper mPmHelper;
private final IconCache mIconCache;
private final InvariantDeviceProfile mIDP;
@@ -130,17 +135,24 @@
public int itemType;
public int restoreFlag;
- public LoaderCursor(Cursor cursor, LauncherAppState app, UserManagerState userManagerState,
- PackageManagerHelper pmHelper,
- @Nullable LauncherRestoreEventLogger restoreEventLogger) {
+ @AssistedInject
+ public LoaderCursor(
+ @Assisted Cursor cursor,
+ @Assisted UserManagerState userManagerState,
+ @Assisted @Nullable LauncherRestoreEventLogger restoreEventLogger,
+ @ApplicationContext Context context,
+ IconCache iconCache,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ PackageManagerHelper pmHelper) {
super(cursor);
-
- mApp = app;
- allUsers = userManagerState.allUsers;
- mContext = app.getContext();
- mIconCache = app.getIconCache();
+ mContext = context;
+ mIconCache = iconCache;
+ mIDP = idp;
+ mModel = model;
mPmHelper = pmHelper;
- mIDP = app.getInvariantDeviceProfile();
+
+ allUsers = userManagerState.allUsers;
mRestoreEventLogger = restoreEventLogger;
// Init column indices
@@ -432,7 +444,7 @@
*/
public ContentWriter updater() {
return new ContentWriter(mContext, new ContentWriter.CommitParams(
- mApp.getModel().getModelDbController(),
+ mModel.getModelDbController(),
BaseColumns._ID + "= ?", new String[]{Integer.toString(id)}));
}
@@ -454,7 +466,7 @@
public boolean commitDeleted() {
if (mItemsToRemove.size() > 0) {
// Remove dead items
- mApp.getModel().getModelDbController().delete(TABLE_NAME,
+ mModel.getModelDbController().delete(TABLE_NAME,
Utilities.createDbSelectionQuery(Favorites._ID, mItemsToRemove), null);
return true;
}
@@ -480,7 +492,7 @@
// Update restored items that no longer require special handling
ContentValues values = new ContentValues();
values.put(Favorites.RESTORED, 0);
- mApp.getModel().getModelDbController().update(TABLE_NAME, values,
+ mModel.getModelDbController().update(TABLE_NAME, values,
Utilities.createDbSelectionQuery(Favorites._ID, mRestoredRows), null);
}
if (mRestoreEventLogger != null) {
@@ -645,4 +657,12 @@
return false;
}
}
+
+ @AssistedFactory
+ public interface LoaderCursorFactory {
+
+ LoaderCursor createLoaderCursor(Cursor cursor,
+ UserManagerState userManagerState,
+ @Nullable LauncherRestoreEventLogger restoreEventLogger);
+ }
}
diff --git a/src/com/android/launcher3/model/LoaderTask.java b/src/com/android/launcher3/model/LoaderTask.java
index fb1ebaf..73af6a2 100644
--- a/src/com/android/launcher3/model/LoaderTask.java
+++ b/src/com/android/launcher3/model/LoaderTask.java
@@ -53,7 +53,6 @@
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
-import android.util.ArrayMap;
import android.util.Log;
import android.util.LongSparseArray;
@@ -63,13 +62,14 @@
import androidx.annotation.WorkerThread;
import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.InvariantDeviceProfile;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherPrefs;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.Utilities;
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.folder.Folder;
import com.android.launcher3.folder.FolderGridOrganizer;
import com.android.launcher3.folder.FolderNameInfos;
@@ -82,6 +82,7 @@
import com.android.launcher3.icons.cache.IconCacheUpdateHandler;
import com.android.launcher3.icons.cache.LauncherActivityCachingLogic;
import com.android.launcher3.logging.FileLog;
+import com.android.launcher3.model.LoaderCursor.LoaderCursorFactory;
import com.android.launcher3.model.data.AppInfo;
import com.android.launcher3.model.data.AppPairInfo;
import com.android.launcher3.model.data.FolderInfo;
@@ -106,6 +107,10 @@
import com.android.launcher3.util.TraceHelper;
import com.android.launcher3.widget.WidgetInflater;
+import dagger.assisted.Assisted;
+import dagger.assisted.AssistedFactory;
+import dagger.assisted.AssistedInject;
+
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
@@ -117,6 +122,8 @@
import java.util.Set;
import java.util.concurrent.CancellationException;
+import javax.inject.Named;
+
/**
* Runnable for the thread that loads the contents of the launcher:
* - workspace icons
@@ -131,10 +138,14 @@
private static final boolean DEBUG = true;
- @NonNull
- protected final LauncherAppState mApp;
+ private final Context mContext;
+ private final LauncherModel mModel;
+ private final InvariantDeviceProfile mIDP;
+ private final boolean mIsSafeModeEnabled;
private final AllAppsList mBgAllAppsList;
protected final BgDataModel mBgDataModel;
+ private final LoaderCursorFactory mLoaderCursorFactory;
+
private final ModelDelegate mModelDelegate;
private boolean mIsRestoreFromBackup;
@@ -152,7 +163,6 @@
private final IconCache mIconCache;
private final UserManagerState mUserManagerState;
- protected final Map<ComponentKey, AppWidgetProviderInfo> mWidgetProvidersMap = new ArrayMap<>();
private Map<ShortcutKey, ShortcutInfo> mShortcutKeyToPinnedShortcuts;
private HashMap<PackageUserKey, SessionInfo> mInstallingPkgsCached;
@@ -164,26 +174,36 @@
private boolean mItemsDeleted = false;
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());
- }
-
- @VisibleForTesting
- LoaderTask(@NonNull LauncherAppState app, AllAppsList bgAllAppsList, BgDataModel bgModel,
- ModelDelegate modelDelegate, @NonNull BaseLauncherBinder launcherBinder,
- UserManagerState userManagerState) {
- mApp = app;
+ @AssistedInject
+ LoaderTask(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ LauncherModel model,
+ UserCache userCache,
+ PackageManagerHelper pmHelper,
+ InstallSessionHelper sessionHelper,
+ IconCache iconCache,
+ AllAppsList bgAllAppsList,
+ BgDataModel bgModel,
+ LoaderCursorFactory loaderCursorFactory,
+ @Named("SAFE_MODE") boolean isSafeModeEnabled,
+ @Assisted @NonNull BaseLauncherBinder launcherBinder,
+ @Assisted UserManagerState userManagerState) {
+ mContext = context;
+ mIDP = idp;
+ mModel = model;
+ mIsSafeModeEnabled = isSafeModeEnabled;
mBgAllAppsList = bgAllAppsList;
mBgDataModel = bgModel;
- mModelDelegate = modelDelegate;
+ mModelDelegate = model.getModelDelegate();
mLauncherBinder = launcherBinder;
- mLauncherApps = mApp.getContext().getSystemService(LauncherApps.class);
- mUserManager = mApp.getContext().getSystemService(UserManager.class);
- mUserCache = UserCache.INSTANCE.get(mApp.getContext());
- mPmHelper = PackageManagerHelper.INSTANCE.get(mApp.getContext());
- mSessionHelper = InstallSessionHelper.INSTANCE.get(mApp.getContext());
- mIconCache = mApp.getIconCache();
+ mLoaderCursorFactory = loaderCursorFactory;
+ mLauncherApps = mContext.getSystemService(LauncherApps.class);
+ mUserManager = mContext.getSystemService(UserManager.class);
+ mUserCache = userCache;
+ mPmHelper = pmHelper;
+ mSessionHelper = sessionHelper;
+ mIconCache = iconCache;
mUserManagerState = userManagerState;
mInstallingPkgsCached = null;
}
@@ -215,7 +235,7 @@
.filter(currentScreenContentFilter(firstScreens))
.toList();
final int disableArchivingLauncherBroadcast = Settings.Secure.getInt(
- mApp.getContext().getContentResolver(),
+ mContext.getContentResolver(),
"disable_launcher_broadcast_installed_apps",
/* default */ 0);
boolean shouldAttachArchivingExtras = mIsRestoreFromBackup
@@ -230,10 +250,10 @@
mBgDataModel.itemsIdMap.stream().filter(WIDGET_FILTER).toList()
);
logASplit("Sending first screen broadcast with additional archiving Extras");
- FirstScreenBroadcastHelper.sendBroadcastsForModels(mApp.getContext(), broadcastModels);
+ FirstScreenBroadcastHelper.sendBroadcastsForModels(mContext, broadcastModels);
} else {
logASplit("Sending first screen broadcast");
- mFirstScreenBroadcast.sendBroadcasts(mApp.getContext(), firstScreenItems);
+ mFirstScreenBroadcast.sendBroadcasts(mContext, firstScreenItems);
}
}
@@ -249,16 +269,14 @@
MODEL_EXECUTOR.elevatePriority(CALLER_LOADER_TASK);
LoaderMemoryLogger memoryLogger = new LoaderMemoryLogger();
mIsRestoreFromBackup =
- LauncherPrefs.get(mApp.getContext()).get(IS_FIRST_LOAD_AFTER_RESTORE);
+ LauncherPrefs.get(mContext).get(IS_FIRST_LOAD_AFTER_RESTORE);
LauncherRestoreEventLogger restoreEventLogger = null;
if (enableLauncherBrMetricsFixed()) {
- restoreEventLogger = LauncherRestoreEventLogger.Companion
- .newInstance(mApp.getContext());
+ restoreEventLogger = LauncherRestoreEventLogger.Companion.newInstance(mContext);
}
- try (LauncherModel.LoaderTransaction transaction = mApp.getModel().beginLoader(this)) {
-
+ try (LauncherModel.LoaderTransaction transaction = mModel.beginLoader(this)) {
List<CacheableShortcutInfo> allShortcuts = new ArrayList<>();
- loadWorkspace(allShortcuts, "", memoryLogger, restoreEventLogger);
+ loadWorkspace(allShortcuts, "", new HashMap<>(), memoryLogger, restoreEventLogger);
// Sanitize data re-syncs widgets/shortcuts based on the workspace loaded from db.
// sanitizeData should not be invoked if the workspace is loaded from a db different
@@ -267,7 +285,7 @@
// TODO(b/384731096): Write Unit Test to make sure sanitizeWidgetsShortcutsAndPackages
// actually re-pins shortcuts that are in model but not in ShortcutManager, if possible
// after a simulated restore.
- if (Objects.equals(mApp.getInvariantDeviceProfile().dbFile, mDbName)) {
+ if (Objects.equals(mIDP.dbFile, mDbName)) {
verifyNotStopped();
sanitizeFolders(mItemsDeleted);
sanitizeAppPairs();
@@ -307,13 +325,13 @@
setIgnorePackages(updateHandler);
updateHandler.updateIcons(allActivityList,
LauncherActivityCachingLogic.INSTANCE,
- mApp.getModel()::onPackageIconsUpdated);
+ mModel::onPackageIconsUpdated);
logASplit("update AllApps icon cache finished");
verifyNotStopped();
logASplit("saving all shortcuts in icon cache");
updateHandler.updateIcons(allShortcuts, CacheableShortcutCachingLogic.INSTANCE,
- mApp.getModel()::onPackageIconsUpdated);
+ mModel::onPackageIconsUpdated);
// Take a break
waitForIdle();
@@ -342,14 +360,14 @@
// fourth step
WidgetsModel widgetsModel = mBgDataModel.widgetsModel;
- List<CachedObject> allWidgetsList = widgetsModel.update(mApp, /*packageUser=*/null);
+ List<CachedObject> allWidgetsList = widgetsModel.update(/*packageUser=*/null);
logASplit("load widgets finished");
verifyNotStopped();
mLauncherBinder.bindWidgets();
logASplit("bindWidgets finished");
verifyNotStopped();
- LauncherPrefs prefs = LauncherPrefs.get(mApp.getContext());
+ LauncherPrefs prefs = LauncherPrefs.get(mContext);
if (enableSmartspaceAsAWidget() && prefs.get(SHOULD_SHOW_SMARTSPACE)) {
mLauncherBinder.bindSmartspaceWidget();
@@ -366,7 +384,7 @@
logASplit("saving all widgets in icon cache");
updateHandler.updateIcons(allWidgetsList,
CachedObjectCachingLogic.INSTANCE,
- mApp.getModel()::onWidgetLabelsUpdated);
+ mModel::onWidgetLabelsUpdated);
// fifth step
loadFolderNames();
@@ -380,7 +398,7 @@
memoryLogger.clearLogs();
if (mIsRestoreFromBackup) {
mIsRestoreFromBackup = false;
- LauncherPrefs.get(mApp.getContext()).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
+ LauncherPrefs.get(mContext).putSync(IS_FIRST_LOAD_AFTER_RESTORE.to(false));
if (restoreEventLogger != null) {
restoreEventLogger.reportLauncherRestoreResults();
}
@@ -402,35 +420,42 @@
this.notify();
}
- protected void loadWorkspace(
+ public void loadWorkspaceForPreview(String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap) {
+ loadWorkspace(new ArrayList<>(), selection, widgetProviderInfoMap, null, null);
+ }
+
+ private void loadWorkspace(
List<CacheableShortcutInfo> allDeepShortcuts,
String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
@Nullable LoaderMemoryLogger memoryLogger,
@Nullable LauncherRestoreEventLogger restoreEventLogger
) {
Trace.beginSection("LoadWorkspace");
try {
- loadWorkspaceImpl(allDeepShortcuts, selection, memoryLogger, restoreEventLogger);
+ loadWorkspaceImpl(allDeepShortcuts, selection, widgetProviderInfoMap,
+ memoryLogger, restoreEventLogger);
} finally {
Trace.endSection();
}
logASplit("loadWorkspace finished");
mBgDataModel.isFirstPagePinnedItemEnabled = FeatureFlags.QSB_ON_FIRST_SCREEN
- && (!enableSmartspaceRemovalToggle() || LauncherPrefs.getPrefs(
- mApp.getContext()).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
+ && (!enableSmartspaceRemovalToggle()
+ || LauncherPrefs.getPrefs(mContext).getBoolean(SMARTSPACE_ON_HOME_SCREEN, true));
}
private void loadWorkspaceImpl(
List<CacheableShortcutInfo> allDeepShortcuts,
String selection,
+ Map<ComponentKey, AppWidgetProviderInfo> widgetProviderInfoMap,
@Nullable LoaderMemoryLogger memoryLogger,
@Nullable LauncherRestoreEventLogger restoreEventLogger) {
- final Context context = mApp.getContext();
final boolean isSdCardReady = Utilities.isBootCompleted();
- final WidgetInflater widgetInflater = new WidgetInflater(context);
+ final WidgetInflater widgetInflater = new WidgetInflater(mContext, mIsSafeModeEnabled);
- ModelDbController dbController = mApp.getModel().getModelDbController();
+ ModelDbController dbController = mModel.getModelDbController();
if (Flags.gridMigrationRefactor()) {
try {
dbController.attemptMigrateDb(restoreEventLogger, mModelDelegate);
@@ -452,28 +477,29 @@
if (Flags.enableSupportForArchiving()) {
mInstallingPkgsCached = installingPkgs;
}
- installingPkgs.forEach(mApp.getIconCache()::updateSessionCache);
+ installingPkgs.forEach(mIconCache::updateSessionCache);
FileLog.d(TAG, "loadWorkspace: Packages with active install/update sessions: "
+ installingPkgs.keySet().stream().map(info -> info.mPackageName).toList());
mFirstScreenBroadcast = new FirstScreenBroadcast(installingPkgs);
mShortcutKeyToPinnedShortcuts = new HashMap<>();
- final LoaderCursor c = new LoaderCursor(
+ final LoaderCursor c = mLoaderCursorFactory.createLoaderCursor(
dbController.query(TABLE_NAME, null, selection, null, null),
- mApp, mUserManagerState, mPmHelper,
+ mUserManagerState,
mIsRestoreFromBackup ? restoreEventLogger : null);
final Bundle extras = c.getExtras();
mDbName = extras == null ? null : extras.getString(ModelDbController.EXTRA_DB_NAME);
try {
final LongSparseArray<Boolean> unlockedUsers = new LongSparseArray<>();
- queryPinnedShortcutsForUnlockedUsers(context, unlockedUsers);
+ queryPinnedShortcutsForUnlockedUsers(mContext, unlockedUsers);
mWorkspaceIconRequestInfos = new ArrayList<>();
WorkspaceItemProcessor itemProcessor = new WorkspaceItemProcessor(c, memoryLogger,
mUserCache, mUserManagerState, mLauncherApps, mPendingPackages,
- mShortcutKeyToPinnedShortcuts, mApp, mBgDataModel,
- mWidgetProvidersMap, installingPkgs, isSdCardReady,
+ mShortcutKeyToPinnedShortcuts, mContext, mIDP, mIconCache,
+ mIsSafeModeEnabled, mBgDataModel,
+ widgetProviderInfoMap, installingPkgs, isSdCardReady,
widgetInflater, mPmHelper, mWorkspaceIconRequestInfos, unlockedUsers,
allDeepShortcuts);
@@ -577,7 +603,7 @@
@WorkerThread
private void processFolderItems() {
// Sort the folder items, update ranks, and make sure all preview items are high res.
- List<FolderGridOrganizer> verifiers = mApp.getInvariantDeviceProfile().supportedProfiles
+ List<FolderGridOrganizer> verifiers = mIDP.supportedProfiles
.stream().map(FolderGridOrganizer::createFolderGridOrganizer).toList();
for (ItemInfo itemInfo : mBgDataModel.itemsIdMap) {
if (!(itemInfo instanceof FolderInfo folder)) {
@@ -617,7 +643,7 @@
if (mIconCache.isDefaultIcon(wai.bitmap, wai.user)) {
logASplit("tryLoadWorkspaceIconsInBulk: default icon found for "
+ wai.getTargetComponent() + ", will attempt to load from iconBlob");
- iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+ iconRequestInfo.loadIconFromDbBlob(mContext);
}
}
} finally {
@@ -649,7 +675,7 @@
private void sanitizeFolders(boolean itemsDeleted) {
if (itemsDeleted) {
// Remove any empty folder
- IntArray deletedFolderIds = mApp.getModel().getModelDbController().deleteEmptyFolders();
+ IntArray deletedFolderIds = mModel.getModelDbController().deleteEmptyFolders();
synchronized (mBgDataModel) {
for (int folderId : deletedFolderIds) {
mBgDataModel.itemsIdMap.remove(folderId);
@@ -660,8 +686,8 @@
/** Cleans up app pairs if they don't have the right number of member apps (2). */
private void sanitizeAppPairs() {
- IntArray deletedAppPairIds = mApp.getModel().getModelDbController().deleteBadAppPairs();
- IntArray deletedAppIds = mApp.getModel().getModelDbController().deleteUnparentedApps();
+ IntArray deletedAppPairIds = mModel.getModelDbController().deleteBadAppPairs();
+ IntArray deletedAppIds = mModel.getModelDbController().deleteUnparentedApps();
IntArray deleted = new IntArray();
deleted.addAll(deletedAppPairIds);
@@ -675,17 +701,15 @@
}
private void sanitizeWidgetsShortcutsAndPackages() {
- Context context = mApp.getContext();
-
// Remove any ghost widgets
- mApp.getModel().getModelDbController().removeGhostWidgets();
+ mModel.getModelDbController().removeGhostWidgets();
// Update pinned state of model shortcuts
- mBgDataModel.updateShortcutPinnedState(context);
+ mBgDataModel.updateShortcutPinnedState(mContext);
if (!Utilities.isBootCompleted() && !mPendingPackages.isEmpty()) {
- context.registerReceiver(
- new SdCardAvailableReceiver(mApp, mPendingPackages),
+ mContext.registerReceiver(
+ new SdCardAvailableReceiver(mContext, mModel, mPendingPackages),
new IntentFilter(Intent.ACTION_BOOT_COMPLETED),
null,
MODEL_EXECUTOR.getHandler());
@@ -722,7 +746,7 @@
for (int i = 0; i < apps.size(); i++) {
LauncherActivityInfo app = apps.get(i);
AppInfo appInfo = new AppInfo(app, mUserCache.getUserInfo(user),
- ApiWrapper.INSTANCE.get(mApp.getContext()), mPmHelper, quietMode);
+ ApiWrapper.INSTANCE.get(mContext), mPmHelper, quietMode);
if (Flags.enableSupportForArchiving() && app.getApplicationInfo().isArchived) {
// For archived apps, include progress info in case there is a pending
// install session post restart of device.
@@ -751,7 +775,7 @@
for (PackageInstaller.SessionInfo info :
mSessionHelper.getAllVerifiedSessions()) {
AppInfo promiseAppInfo = mBgAllAppsList.addPromiseApp(
- mApp.getContext(),
+ mContext,
PackageInstallInfo.fromInstallingState(info),
false);
@@ -776,7 +800,7 @@
+ appInfo.getTargetComponent()
+ ", will attempt to load from iconBlob: "
+ Arrays.toString(iconRequestInfo.iconBlob));
- iconRequestInfo.loadIconFromDbBlob(mApp.getContext());
+ iconRequestInfo.loadIconFromDbBlob(mContext);
}
}
}
@@ -794,9 +818,9 @@
mUserManagerState.isAnyProfileQuietModeEnabled());
}
mBgAllAppsList.setFlags(FLAG_HAS_SHORTCUT_PERMISSION,
- hasShortcutsPermission(mApp.getContext()));
+ hasShortcutsPermission(mContext));
mBgAllAppsList.setFlags(FLAG_QUIET_MODE_CHANGE_PERMISSION,
- mApp.getContext().checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
+ mContext.checkSelfPermission("android.permission.MODIFY_QUIET_MODE")
== PackageManager.PERMISSION_GRANTED);
mBgAllAppsList.getAndResetChangeFlag();
@@ -827,7 +851,7 @@
workspaceIconRequest.get().iconBlob,
false /* useLowResIcon= */
);
- if (!iconRequestInfo.loadIconFromDbBlob(mApp.getContext())) {
+ if (!iconRequestInfo.loadIconFromDbBlob(mContext)) {
Log.d(TAG, "AppInfo Icon failed to load from blob, using cache.");
mIconCache.getTitleAndIcon(
appInfo,
@@ -853,7 +877,7 @@
if (mBgAllAppsList.hasShortcutHostPermission()) {
for (UserHandle user : mUserCache.getUserProfiles()) {
if (mUserManager.isUserUnlocked(user)) {
- List<ShortcutInfo> shortcuts = new ShortcutRequest(mApp.getContext(), user)
+ List<ShortcutInfo> shortcuts = new ShortcutRequest(mContext, user)
.query(ShortcutRequest.ALL);
allShortcuts.addAll(shortcuts);
mBgDataModel.updateDeepShortcutCounts(null, user, shortcuts);
@@ -864,7 +888,7 @@
}
private void loadFolderNames() {
- FolderNameProvider provider = FolderNameProvider.newInstance(mApp.getContext(),
+ FolderNameProvider provider = FolderNameProvider.newInstance(mContext,
mBgAllAppsList.data, FolderNameProvider.getCollectionForSuggestions(mBgDataModel));
synchronized (mBgDataModel) {
@@ -874,7 +898,7 @@
.forEach(info -> {
FolderInfo fi = (FolderInfo) info;
FolderNameInfos suggestionInfos = new FolderNameInfos();
- provider.getSuggestedFolderName(mApp.getContext(), fi.getAppContents(),
+ provider.getSuggestedFolderName(mContext, fi.getAppContents(),
suggestionInfos);
fi.suggestedFolderNames = suggestionInfos;
});
@@ -891,4 +915,10 @@
Log.d(TAG, label);
}
}
+
+ @AssistedFactory
+ public interface LoaderTaskFactory {
+
+ LoaderTask newLoaderTask(BaseLauncherBinder binder, UserManagerState userState);
+ }
}
diff --git a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
index 7ba2dad..8b6c369 100644
--- a/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
+++ b/src/com/android/launcher3/model/ModelLauncherCallbacks.kt
@@ -114,7 +114,7 @@
override fun onUpdateSessionDisplay(key: PackageUserKey, info: SessionInfo) {
/** Updates the icons and label of all pending icons for the provided package name. */
taskExecutor.accept { controller, _, _ ->
- controller.app.iconCache.updateSessionCache(key, info)
+ controller.iconCache.updateSessionCache(key, info)
}
taskExecutor.accept(
CacheDataUpdatedTask(
@@ -128,7 +128,7 @@
override fun onInstallSessionCreated(sessionInfo: PackageInstallInfo) {
if (FeatureFlags.PROMISE_APPS_IN_ALL_APPS.get()) {
taskExecutor.accept { taskController, _, apps ->
- apps.addPromiseApp(taskController.app.context, sessionInfo)
+ apps.addPromiseApp(taskController.context, sessionInfo)
taskController.bindApplicationsIfNeeded()
}
}
diff --git a/src/com/android/launcher3/model/ModelTaskController.kt b/src/com/android/launcher3/model/ModelTaskController.kt
index 5566482..f17ca32 100644
--- a/src/com/android/launcher3/model/ModelTaskController.kt
+++ b/src/com/android/launcher3/model/ModelTaskController.kt
@@ -16,27 +16,34 @@
package com.android.launcher3.model
-import com.android.launcher3.LauncherAppState
+import android.content.Context
import com.android.launcher3.LauncherModel
import com.android.launcher3.LauncherModel.CallbackTask
import com.android.launcher3.celllayout.CellPosMapper
+import com.android.launcher3.dagger.ApplicationContext
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.BgDataModel.FixedContainerItems
import com.android.launcher3.model.data.ItemInfo
+import com.android.launcher3.util.Executors.MAIN_EXECUTOR
import com.android.launcher3.util.PackageUserKey
import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder
import java.util.Objects
-import java.util.concurrent.Executor
import java.util.function.Predicate
+import javax.inject.Inject
/** Class with utility methods and properties for running a LauncherModel Task */
-class ModelTaskController(
- val app: LauncherAppState,
+class ModelTaskController
+@Inject
+constructor(
+ @ApplicationContext val context: Context,
+ val iconCache: IconCache,
val dataModel: BgDataModel,
val allAppsList: AllAppsList,
- private val model: LauncherModel,
- private val uiExecutor: Executor,
+ val model: LauncherModel,
) {
+ private val uiExecutor = MAIN_EXECUTOR
+
/** Schedules a {@param task} to be executed on the current callbacks. */
fun scheduleCallbackTask(task: CallbackTask) {
for (cb in model.callbacks) {
@@ -78,7 +85,7 @@
fun bindUpdatedWidgets(dataModel: BgDataModel) {
val allWidgets =
- WidgetsListBaseEntriesBuilder(app.context)
+ WidgetsListBaseEntriesBuilder(context)
.build(dataModel.widgetsModel.widgetsByPackageItemForPicker)
scheduleCallbackTask { it.bindAllWidgets(allWidgets) }
}
diff --git a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
index a216042..a3561ed 100644
--- a/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
+++ b/src/com/android/launcher3/model/PackageInstallStateChangedTask.java
@@ -52,11 +52,11 @@
try {
// For instant apps we do not get package-add. Use setting events to update
// any pinned icons.
- Context context = taskController.getApp().getContext();
+ Context context = taskController.getContext();
ApplicationInfo ai = context
.getPackageManager().getApplicationInfo(mInstallInfo.packageName, 0);
if (InstantAppResolver.newInstance(context).isInstantApp(ai)) {
- taskController.getApp().getModel().newModelCallbacks()
+ taskController.getModel().newModelCallbacks()
.onPackageAdded(ai.packageName, mInstallInfo.user);
}
} catch (PackageManager.NameNotFoundException e) {
diff --git a/src/com/android/launcher3/model/PackageUpdatedTask.java b/src/com/android/launcher3/model/PackageUpdatedTask.java
index 3cdb250..04f3faa 100644
--- a/src/com/android/launcher3/model/PackageUpdatedTask.java
+++ b/src/com/android/launcher3/model/PackageUpdatedTask.java
@@ -38,7 +38,6 @@
import androidx.annotation.NonNull;
import com.android.launcher3.Flags;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherSettings.Favorites;
import com.android.launcher3.config.FeatureFlags;
@@ -106,9 +105,8 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList appsList) {
- final LauncherAppState app = taskController.getApp();
- final Context context = app.getContext();
- final IconCache iconCache = app.getIconCache();
+ final Context context = taskController.getContext();
+ final IconCache iconCache = taskController.getIconCache();
final String[] packages = mPackages;
final int packageCount = packages.length;
@@ -433,7 +431,7 @@
// Load widgets for the new package. Changes due to app updates are handled through
// AppWidgetHost events, this is just to initialize the long-press options.
for (int i = 0; i < packageCount; i++) {
- dataModel.widgetsModel.update(app, new PackageUserKey(packages[i], mUser));
+ dataModel.widgetsModel.update(new PackageUserKey(packages[i], mUser));
}
taskController.bindUpdatedWidgets(dataModel);
}
diff --git a/src/com/android/launcher3/model/SdCardAvailableReceiver.java b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
index 9e3f0e1..9ae8092 100644
--- a/src/com/android/launcher3/model/SdCardAvailableReceiver.java
+++ b/src/com/android/launcher3/model/SdCardAvailableReceiver.java
@@ -22,7 +22,6 @@
import android.content.pm.LauncherApps;
import android.os.UserHandle;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel;
import com.android.launcher3.util.ApplicationInfoWrapper;
import com.android.launcher3.util.PackageUserKey;
@@ -43,9 +42,10 @@
private final Context mContext;
private final Set<PackageUserKey> mPackages;
- public SdCardAvailableReceiver(LauncherAppState app, Set<PackageUserKey> packages) {
- mModel = app.getModel();
- mContext = app.getContext();
+ public SdCardAvailableReceiver(
+ Context context, LauncherModel model, Set<PackageUserKey> packages) {
+ mContext = context;
+ mModel = model;
mPackages = packages;
}
diff --git a/src/com/android/launcher3/model/SessionFailureTask.kt b/src/com/android/launcher3/model/SessionFailureTask.kt
index 8baf568..6ed5178 100644
--- a/src/com/android/launcher3/model/SessionFailureTask.kt
+++ b/src/com/android/launcher3/model/SessionFailureTask.kt
@@ -33,9 +33,9 @@
dataModel: BgDataModel,
apps: AllAppsList,
) {
- val iconCache = taskController.app.iconCache
+ val iconCache = taskController.iconCache
val isAppArchived =
- ApplicationInfoWrapper(taskController.app.context, packageName, user).isArchived()
+ ApplicationInfoWrapper(taskController.context, packageName, user).isArchived()
synchronized(dataModel) {
if (isAppArchived) {
val updatedItems = mutableListOf<WorkspaceItemInfo>()
diff --git a/src/com/android/launcher3/model/ShortcutsChangedTask.kt b/src/com/android/launcher3/model/ShortcutsChangedTask.kt
index 56e9e43..d6759e2 100644
--- a/src/com/android/launcher3/model/ShortcutsChangedTask.kt
+++ b/src/com/android/launcher3/model/ShortcutsChangedTask.kt
@@ -40,8 +40,7 @@
dataModel: BgDataModel,
apps: AllAppsList,
) {
- val app = taskController.app
- val context = app.context
+ val context = taskController.context
// Find WorkspaceItemInfo's that have changed on the workspace.
val matchingWorkspaceItems = ArrayList<WorkspaceItemInfo>()
@@ -88,7 +87,7 @@
.filter { itemInfo: WorkspaceItemInfo -> shortcutId == itemInfo.deepShortcutId }
.forEach { workspaceItemInfo: WorkspaceItemInfo ->
workspaceItemInfo.updateFromDeepShortcutInfo(fullDetails, context)
- app.iconCache.getShortcutIcon(
+ taskController.iconCache.getShortcutIcon(
workspaceItemInfo,
CacheableShortcutInfo(fullDetails, infoWrapper),
)
diff --git a/src/com/android/launcher3/model/UserLockStateChangedTask.java b/src/com/android/launcher3/model/UserLockStateChangedTask.java
index 3dc5ff3..4d28ccb 100644
--- a/src/com/android/launcher3/model/UserLockStateChangedTask.java
+++ b/src/com/android/launcher3/model/UserLockStateChangedTask.java
@@ -23,7 +23,6 @@
import androidx.annotation.NonNull;
-import com.android.launcher3.LauncherAppState;
import com.android.launcher3.LauncherModel.ModelUpdateTask;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.model.data.WorkspaceItemInfo;
@@ -55,8 +54,7 @@
@Override
public void execute(@NonNull ModelTaskController taskController, @NonNull BgDataModel dataModel,
@NonNull AllAppsList apps) {
- LauncherAppState app = taskController.getApp();
- Context context = app.getContext();
+ Context context = taskController.getContext();
HashMap<ShortcutKey, ShortcutInfo> pinnedShortcuts = new HashMap<>();
if (mIsUserUnlocked) {
@@ -92,7 +90,7 @@
}
si.runtimeStatusFlags &= ~FLAG_DISABLED_LOCKED_USER;
si.updateFromDeepShortcutInfo(shortcut, context);
- app.getIconCache().getShortcutIcon(si, shortcut);
+ taskController.getIconCache().getShortcutIcon(si, shortcut);
} else {
si.runtimeStatusFlags |= FLAG_DISABLED_LOCKED_USER;
}
diff --git a/src/com/android/launcher3/model/WidgetsModel.java b/src/com/android/launcher3/model/WidgetsModel.java
index 52b142d..f2144c0 100644
--- a/src/com/android/launcher3/model/WidgetsModel.java
+++ b/src/com/android/launcher3/model/WidgetsModel.java
@@ -27,6 +27,7 @@
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.Utilities;
import com.android.launcher3.config.FeatureFlags;
+import com.android.launcher3.dagger.ApplicationContext;
import com.android.launcher3.icons.IconCache;
import com.android.launcher3.icons.cache.CachedObject;
import com.android.launcher3.model.data.PackageItemInfo;
@@ -54,6 +55,8 @@
import java.util.function.Predicate;
import java.util.stream.Collectors;
+import javax.inject.Inject;
+
/**
* Widgets data model that is used by the adapters of the widget views and controllers.
*
@@ -68,6 +71,29 @@
private final Map<PackageItemInfo, List<WidgetItem>> mWidgetsByPackageItem = new HashMap<>();
@Nullable private WidgetValidityCheckForPicker mWidgetValidityCheckForPicker = null;
+ private final Context mContext;
+ private final InvariantDeviceProfile mIdp;
+ private final IconCache mIconCache;
+ private final AppFilter mAppFilter;
+
+ @Inject
+ public WidgetsModel(
+ @ApplicationContext Context context,
+ InvariantDeviceProfile idp,
+ IconCache iconCache,
+ AppFilter appFilter) {
+ mContext = context;
+ mIdp = idp;
+ mIconCache = iconCache;
+ mAppFilter = appFilter;
+ }
+
+ public WidgetsModel(Context context) {
+ this(context,
+ LauncherAppState.getIDP(context),
+ LauncherAppState.getInstance(context).getIconCache(), new AppFilter(context));
+ }
+
/**
* Returns all widgets keyed by their component key.
*/
@@ -128,36 +154,33 @@
* @param packageUser If null, all widgets and shortcuts are updated and returned, otherwise
* only widgets and shortcuts associated with the package/user are.
*/
- public List<CachedObject> update(
- LauncherAppState app, @Nullable PackageUserKey packageUser) {
+ public List<CachedObject> update(@Nullable PackageUserKey packageUser) {
if (!WIDGETS_ENABLED) {
return new ArrayList<>();
}
Preconditions.assertWorkerThread();
- Context context = app.getContext();
final ArrayList<WidgetItem> widgetsAndShortcuts = new ArrayList<>();
List<CachedObject> updatedItems = new ArrayList<>();
try {
- InvariantDeviceProfile idp = app.getInvariantDeviceProfile();
// Widgets
- WidgetManagerHelper widgetManager = new WidgetManagerHelper(context);
+ WidgetManagerHelper widgetManager = new WidgetManagerHelper(mContext);
for (AppWidgetProviderInfo widgetInfo : widgetManager.getAllProviders(packageUser)) {
LauncherAppWidgetProviderInfo launcherWidgetInfo =
- LauncherAppWidgetProviderInfo.fromProviderInfo(context, widgetInfo);
+ LauncherAppWidgetProviderInfo.fromProviderInfo(mContext, widgetInfo);
widgetsAndShortcuts.add(new WidgetItem(
- launcherWidgetInfo, idp, app.getIconCache(), app.getContext()));
+ launcherWidgetInfo, mIdp, mIconCache, mContext));
updatedItems.add(launcherWidgetInfo);
}
// Shortcuts
for (ShortcutConfigActivityInfo info :
- queryList(context, packageUser)) {
- widgetsAndShortcuts.add(new WidgetItem(info, app.getIconCache()));
+ queryList(mContext, packageUser)) {
+ widgetsAndShortcuts.add(new WidgetItem(info, mIconCache));
updatedItems.add(info);
}
- setWidgetsAndShortcuts(widgetsAndShortcuts, app, packageUser);
+ setWidgetsAndShortcuts(widgetsAndShortcuts, packageUser);
} catch (Exception e) {
if (!FeatureFlags.IS_STUDIO_BUILD && Utilities.isBinderSizeError(e)) {
// the returned value may be incomplete and will not be refreshed until the next
@@ -172,14 +195,14 @@
return updatedItems;
}
- private synchronized void setWidgetsAndShortcuts(ArrayList<WidgetItem> rawWidgetsShortcuts,
- LauncherAppState app, @Nullable PackageUserKey packageUser) {
+ private synchronized void setWidgetsAndShortcuts(
+ ArrayList<WidgetItem> rawWidgetsShortcuts, @Nullable PackageUserKey packageUser) {
if (DEBUG) {
Log.d(TAG, "addWidgetsAndShortcuts, widgetsShortcuts#=" + rawWidgetsShortcuts.size());
}
// Refresh the validity checker with latest app state.
- mWidgetValidityCheckForPicker = new WidgetValidityCheckForPicker(app);
+ mWidgetValidityCheckForPicker = new WidgetValidityCheckForPicker(mIdp, mAppFilter);
// Temporary cache for {@link PackageItemInfos} to avoid having to go through
// {@link mPackageItemInfos} to locate the key to be used for {@link #mWidgetsList}
@@ -196,19 +219,17 @@
// add and update.
mWidgetsByPackageItem.putAll(rawWidgetsShortcuts.stream()
.filter(new WidgetFlagCheck())
- .flatMap(widgetItem -> getPackageUserKeys(app.getContext(), widgetItem).stream()
+ .flatMap(widgetItem -> getPackageUserKeys(mContext, widgetItem).stream()
.map(key -> new Pair<>(packageItemInfoCache.getOrCreate(key), widgetItem)))
.collect(groupingBy(pair -> pair.first, mapping(pair -> pair.second, toList()))));
// Update each package entry
- IconCache iconCache = app.getIconCache();
for (PackageItemInfo p : packageItemInfoCache.values()) {
- iconCache.getTitleAndIconForApp(p, DEFAULT_LOOKUP_FLAG.withUseLowRes());
+ mIconCache.getTitleAndIconForApp(p, DEFAULT_LOOKUP_FLAG.withUseLowRes());
}
}
- public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user,
- LauncherAppState app) {
+ public void onPackageIconsUpdated(Set<String> packageNames, UserHandle user) {
if (!WIDGETS_ENABLED) {
return;
}
@@ -220,11 +241,10 @@
WidgetItem item = items.get(i);
if (item.user.equals(user)) {
if (item.activityInfo != null) {
- items.set(i, new WidgetItem(item.activityInfo, app.getIconCache()));
+ items.set(i, new WidgetItem(item.activityInfo, mIconCache));
} else {
- items.set(i, new WidgetItem(item.widgetInfo,
- app.getInvariantDeviceProfile(), app.getIconCache(),
- app.getContext()));
+ items.set(i, new WidgetItem(
+ item.widgetInfo, mIdp, mIconCache, mContext));
}
}
}
@@ -277,9 +297,9 @@
private final InvariantDeviceProfile mIdp;
private final AppFilter mAppFilter;
- WidgetValidityCheckForPicker(LauncherAppState app) {
- mIdp = app.getInvariantDeviceProfile();
- mAppFilter = new AppFilter(app.getContext());
+ WidgetValidityCheckForPicker(InvariantDeviceProfile idp, AppFilter appFilter) {
+ mIdp = idp;
+ mAppFilter = appFilter;
}
@Override
diff --git a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
index 99f2837..7b8f218 100644
--- a/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
+++ b/src/com/android/launcher3/model/WorkspaceItemProcessor.kt
@@ -18,6 +18,7 @@
import android.annotation.SuppressLint
import android.appwidget.AppWidgetProviderInfo
import android.content.ComponentName
+import android.content.Context
import android.content.Intent
import android.content.pm.LauncherApps
import android.content.pm.LauncherApps.ShortcutQuery
@@ -29,10 +30,10 @@
import android.util.LongSparseArray
import com.android.launcher3.Flags
import com.android.launcher3.InvariantDeviceProfile
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
import com.android.launcher3.icons.CacheableShortcutInfo
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CacheLookupFlag.Companion.DEFAULT_LOOKUP_FLAG
import com.android.launcher3.logging.FileLog
import com.android.launcher3.model.data.AppInfo
@@ -70,7 +71,10 @@
private val launcherApps: LauncherApps,
private val pendingPackages: MutableSet<PackageUserKey>,
private val shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo>,
- private val app: LauncherAppState,
+ private val context: Context,
+ private val idp: InvariantDeviceProfile,
+ private val iconCache: IconCache,
+ private val isSafeMode: Boolean,
private val bgDataModel: BgDataModel,
private val widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?>,
private val installingPkgs: HashMap<PackageUserKey, PackageInstaller.SessionInfo>,
@@ -82,9 +86,7 @@
private val allDeepShortcuts: MutableList<CacheableShortcutInfo>,
) {
- private val isSafeMode = app.isSafeModeEnabled
private val tempPackageKey = PackageUserKey(null, null)
- private val iconCache = app.iconCache
/**
* This is the entry point for processing 1 workspace item. This method is like the midfielder
@@ -159,7 +161,7 @@
)
return
}
- val appInfoWrapper = ApplicationInfoWrapper(app.context, targetPkg, c.user)
+ val appInfoWrapper = ApplicationInfoWrapper(context, targetPkg, c.user)
var validTarget = launcherApps.isPackageEnabled(targetPkg, c.user)
// If it's a deep shortcut, we'll use pinned shortcuts to restore it
@@ -306,7 +308,7 @@
)
return
}
- info = WorkspaceItemInfo(pinnedShortcut, app.context)
+ info = WorkspaceItemInfo(pinnedShortcut, context)
// If the pinned deep shortcut is no longer published,
// use the last saved icon instead of the default.
val csi = CacheableShortcutInfo(pinnedShortcut, appInfoWrapper)
@@ -369,7 +371,7 @@
info,
activityInfo,
userCache.getUserInfo(c.user),
- ApiWrapper.INSTANCE[app.context],
+ ApiWrapper.INSTANCE[context],
pmHelper,
)
}
@@ -529,7 +531,7 @@
(si == null) &&
(lapi == null) &&
!(Flags.enableSupportForArchiving() &&
- ApplicationInfoWrapper(app.context, component.packageName, c.user)
+ ApplicationInfoWrapper(context, component.packageName, c.user)
.isArchived())
) {
// Restore never started
@@ -553,7 +555,7 @@
if (si == null) 0 else (si.getProgress() * 100).toInt()
appWidgetInfo.pendingItemInfo =
WidgetsModel.newPendingItemInfo(
- app.context,
+ context,
appWidgetInfo.providerName,
appWidgetInfo.user,
)
@@ -563,7 +565,7 @@
WidgetSizes.updateWidgetSizeRangesAsync(
appWidgetInfo.appWidgetId,
lapi,
- app.context,
+ context,
appWidgetInfo.spanX,
appWidgetInfo.spanY,
)
@@ -586,7 +588,7 @@
" appWidgetId: ${c.appWidgetId}," +
" component=${component}",
)
- logWidgetInfo(app.invariantDeviceProfile, lapi)
+ logWidgetInfo(idp, lapi)
}
}
c.checkAndAddItem(appWidgetInfo, bgDataModel, memoryLogger)
diff --git a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
index 1a6d178..17f1615 100644
--- a/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
+++ b/src/com/android/launcher3/model/WorkspaceItemSpaceFinder.java
@@ -21,7 +21,7 @@
import android.util.LongSparseArray;
import com.android.launcher3.InvariantDeviceProfile;
-import com.android.launcher3.LauncherAppState;
+import com.android.launcher3.LauncherModel;
import com.android.launcher3.LauncherSettings;
import com.android.launcher3.config.FeatureFlags;
import com.android.launcher3.model.data.ItemInfo;
@@ -31,23 +31,37 @@
import java.util.ArrayList;
+import javax.inject.Inject;
+
/**
* Utility class to help find space for new workspace items
*/
public class WorkspaceItemSpaceFinder {
+ private BgDataModel mDataModel;
+ private InvariantDeviceProfile mIDP;
+ private LauncherModel mModel;
+
+ @Inject
+ WorkspaceItemSpaceFinder(
+ BgDataModel dataModel, InvariantDeviceProfile idp, LauncherModel model) {
+ mDataModel = dataModel;
+ mIDP = idp;
+ mModel = model;
+ }
+
/**
* Find a position on the screen for the given size or adds a new screen.
*
* @return screenId and the coordinates for the item in an int array of size 3.
*/
- public int[] findSpaceForItem(LauncherAppState app, BgDataModel dataModel,
+ public int[] findSpaceForItem(
IntArray workspaceScreens, IntArray addedWorkspaceScreensFinal, int spanX, int spanY) {
LongSparseArray<ArrayList<ItemInfo>> screenItems = new LongSparseArray<>();
// Use sBgItemsIdMap as all the items are already loaded.
- synchronized (dataModel) {
- for (ItemInfo info : dataModel.itemsIdMap) {
+ synchronized (mDataModel) {
+ for (ItemInfo info : mDataModel.itemsIdMap) {
if (info.container == LauncherSettings.Favorites.CONTAINER_DESKTOP) {
ArrayList<ItemInfo> items = screenItems.get(info.screenId);
if (items == null) {
@@ -75,7 +89,7 @@
for (int screen = 0; screen < screenCount; screen++) {
screenId = workspaceScreens.get(screen);
if (!screensToExclude.contains(screenId) && findNextAvailableIconSpaceInScreen(
- app, screenItems.get(screenId), coordinates, spanX, spanY)) {
+ screenItems.get(screenId), coordinates, spanX, spanY)) {
// We found a space for it
found = true;
break;
@@ -84,7 +98,7 @@
if (!found) {
// Still no position found. Add a new screen to the end.
- screenId = app.getModel().getModelDbController().getNewScreenId();
+ screenId = mModel.getModelDbController().getNewScreenId();
// Save the screen id for binding in the workspace
workspaceScreens.add(screenId);
@@ -92,7 +106,7 @@
// If we still can't find an empty space, then God help us all!!!
if (!findNextAvailableIconSpaceInScreen(
- app, screenItems.get(screenId), coordinates, spanX, spanY)) {
+ screenItems.get(screenId), coordinates, spanX, spanY)) {
throw new RuntimeException("Can't find space to add the item");
}
}
@@ -100,11 +114,8 @@
}
private boolean findNextAvailableIconSpaceInScreen(
- LauncherAppState app, ArrayList<ItemInfo> occupiedPos,
- int[] xy, int spanX, int spanY) {
- InvariantDeviceProfile profile = app.getInvariantDeviceProfile();
-
- GridOccupancy occupied = new GridOccupancy(profile.numColumns, profile.numRows);
+ ArrayList<ItemInfo> occupiedPos, int[] xy, int spanX, int spanY) {
+ GridOccupancy occupied = new GridOccupancy(mIDP.numColumns, mIDP.numRows);
if (occupiedPos != null) {
for (ItemInfo r : occupiedPos) {
occupied.markCells(r, true);
diff --git a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
index b60b8cc..f5e5e16 100644
--- a/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
+++ b/src/com/android/launcher3/model/data/ItemInfoWithIcon.java
@@ -26,6 +26,7 @@
import androidx.annotation.Nullable;
import com.android.launcher3.Flags;
+import com.android.launcher3.Utilities;
import com.android.launcher3.graphics.ThemeManager;
import com.android.launcher3.icons.BitmapInfo;
import com.android.launcher3.icons.BitmapInfo.DrawableCreationFlags;
@@ -325,10 +326,12 @@
* Returns a FastBitmapDrawable with the icon and context theme applied
*/
public FastBitmapDrawable newIcon(Context context, @DrawableCreationFlags int creationFlags) {
- if (!ThemeManager.INSTANCE.get(context).isIconThemeEnabled()) {
+ ThemeManager themeManager = ThemeManager.INSTANCE.get(context);
+ if (!themeManager.isIconThemeEnabled()) {
creationFlags &= ~FLAG_THEMED;
}
- FastBitmapDrawable drawable = bitmap.newIcon(context, creationFlags);
+ FastBitmapDrawable drawable = bitmap.newIcon(
+ context, creationFlags, Utilities.getIconShapeOrNull(context));
drawable.setIsDisabled(isDisabled());
return drawable;
}
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index f0c9bd9..c861d0b 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -58,6 +58,9 @@
/**
* {@link PageIndicator} which shows dots per page. The active page is shown with the current
* accent color.
+ * <p>
+ * TODO(b/402258632): Split PageIndicatorDots into 2 different classes: FolderPageIndicator &
+ * WorkspacePageIndicator. A lot of the functionality in this class is only used by one UI purpose.
*/
public class PageIndicatorDots extends View implements Insettable, PageIndicator {
@@ -91,6 +94,7 @@
// This is used to optimize the onDraw method by not constructing a new RectF each draw.
private static final RectF sTempRect = new RectF();
+ private static final RectF sLastActiveRect = new RectF();
private static final FloatProperty<PageIndicatorDots> CURRENT_POSITION =
new FloatProperty<PageIndicatorDots>("current_position") {
@@ -553,6 +557,9 @@
}
}
}
+ if (Math.round(mCurrentPosition) == i) {
+ sLastActiveRect.set(sTempRect);
+ }
canvas.drawRoundRect(sTempRect, mDotRadius, mDotRadius, mPaginationPaint);
// TODO(b/394355070) Verify RTL experience works correctly with visual updates
@@ -668,8 +675,8 @@
@Override
public void getOutline(View view, Outline outline) {
if (mEntryAnimationRadiusFactors == null) {
- // TODO(b/394355070): Verify Outline works correctly with visual updates
- RectF activeRect = getActiveRect();
+ RectF activeRect = enableLauncherVisualRefresh()
+ ? sLastActiveRect : getActiveRect();
outline.setRoundRect(
(int) activeRect.left,
(int) activeRect.top,
diff --git a/src/com/android/launcher3/pm/UserCache.java b/src/com/android/launcher3/pm/UserCache.java
index 20c0ecc..98a3882 100644
--- a/src/com/android/launcher3/pm/UserCache.java
+++ b/src/com/android/launcher3/pm/UserCache.java
@@ -219,6 +219,6 @@
public static UserBadgeDrawable getBadgeDrawable(Context context, UserHandle userHandle) {
return (UserBadgeDrawable) BitmapInfo.LOW_RES_INFO.withFlags(UserCache.getInstance(context)
.getUserInfo(userHandle).applyBitmapInfoFlags(FlagOp.NO_OP))
- .getBadgeDrawable(context, false /* isThemed */);
+ .getBadgeDrawable(context, false /* isThemed */, null);
}
}
diff --git a/src/com/android/launcher3/provider/LauncherDbUtils.kt b/src/com/android/launcher3/provider/LauncherDbUtils.kt
index c92328d..3641371 100644
--- a/src/com/android/launcher3/provider/LauncherDbUtils.kt
+++ b/src/com/android/launcher3/provider/LauncherDbUtils.kt
@@ -26,19 +26,17 @@
import android.os.Process
import android.os.UserManager
import android.text.TextUtils
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import com.android.launcher3.Utilities
+import com.android.launcher3.dagger.LauncherComponentProvider.appComponent
import com.android.launcher3.icons.IconCache
-import com.android.launcher3.model.LoaderCursor
import com.android.launcher3.model.UserManagerState
import com.android.launcher3.pm.PinRequestHelper
import com.android.launcher3.pm.UserCache
import com.android.launcher3.shortcuts.ShortcutKey
import com.android.launcher3.util.IntArray
import com.android.launcher3.util.IntSet
-import com.android.launcher3.util.PackageManagerHelper
/** A set of utility methods for Launcher DB used for DB updates and migration. */
object LauncherDbUtils {
@@ -155,12 +153,11 @@
null,
null,
)
- val pmHelper = PackageManagerHelper.INSTANCE[context]
val ums = UserManagerState()
ums.run {
init(UserCache.INSTANCE[context], context.getSystemService(UserManager::class.java))
}
- val lc = LoaderCursor(c, LauncherAppState.getInstance(context), ums, pmHelper, null)
+ val lc = context.appComponent.loaderCursorFactory.createLoaderCursor(c, ums, null)
val deletedShortcuts = IntSet()
while (lc.moveToNext()) {
diff --git a/src/com/android/launcher3/util/ItemInflater.kt b/src/com/android/launcher3/util/ItemInflater.kt
index ebf4656..acb3c4e 100644
--- a/src/com/android/launcher3/util/ItemInflater.kt
+++ b/src/com/android/launcher3/util/ItemInflater.kt
@@ -24,6 +24,7 @@
import android.view.View.OnFocusChangeListener
import android.view.ViewGroup
import com.android.launcher3.BubbleTextView
+import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.R
import com.android.launcher3.apppairs.AppPairIcon
@@ -46,10 +47,11 @@
private val widgetHolder: LauncherWidgetHolder,
private val clickListener: OnClickListener,
private val focusListener: OnFocusChangeListener,
- private val defaultParent: ViewGroup
+ private val defaultParent: ViewGroup,
) where T : Context, T : ActivityContext {
- private val widgetInflater = WidgetInflater(context)
+ private val widgetInflater =
+ WidgetInflater(context, LauncherAppState.getInstance(context).isSafeModeEnabled)
@JvmOverloads
fun inflateItem(item: ItemInfo, writer: ModelWriter, nullableParent: ViewGroup? = null): View? {
@@ -75,7 +77,7 @@
R.layout.folder_icon,
context,
parent,
- item as FolderInfo
+ item as FolderInfo,
)
Favorites.ITEM_TYPE_APP_PAIR ->
return AppPairIcon.inflateIcon(
@@ -83,7 +85,7 @@
context,
parent,
item as AppPairInfo,
- BubbleTextView.DISPLAY_WORKSPACE
+ BubbleTextView.DISPLAY_WORKSPACE,
)
Favorites.ITEM_TYPE_APPWIDGET,
Favorites.ITEM_TYPE_CUSTOM_APPWIDGET ->
diff --git a/src/com/android/launcher3/util/window/WindowManagerProxy.java b/src/com/android/launcher3/util/window/WindowManagerProxy.java
index 11f0bc2..68e0324 100644
--- a/src/com/android/launcher3/util/window/WindowManagerProxy.java
+++ b/src/com/android/launcher3/util/window/WindowManagerProxy.java
@@ -520,6 +520,33 @@
*/
default void onCanCreateDesksChanged(boolean canCreateDesks) {
}
+
+ /**
+ * Called when a new desk is added.
+ *
+ * @param displayId The ID of the display on which the desk was added.
+ * @param deskId The ID of the newly added desk.
+ */
+ default void onDeskAdded(int displayId, int deskId) {}
+
+ /**
+ * Called when an existing desk is removed.
+ *
+ * @param displayId The ID of the display on which the desk was removed.
+ * @param deskId The ID of the desk that was removed.
+ */
+ default void onDeskRemoved(int displayId, int deskId) {}
+
+ /**
+ * Called when the active desk changes.
+ *
+ * @param displayId The ID of the display on which the desk activation change is happening.
+ * @param newActiveDesk The ID of the new active desk or -1 if no desk is active anymore
+ * (i.e. exit desktop mode).
+ * @param oldActiveDesk The ID of the desk that was previously active, or -1 if no desk was
+ * active before.
+ */
+ default void onActiveDeskChanged(int displayId, int newActiveDesk, int oldActiveDesk) {}
}
}
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 82cc40d..8c01c59 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -19,6 +19,7 @@
import static com.android.launcher3.LauncherState.EDIT_MODE;
import static com.android.launcher3.config.FeatureFlags.MULTI_SELECT_EDIT_MODE;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.IGNORE;
+import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_BUTTON_TAP_OR_LONGPRESS;
@@ -219,6 +220,11 @@
OptionsPopupView::enterHomeGardening));
}
options.add(new OptionItem(launcher,
+ R.string.all_apps_home_screen,
+ R.drawable.ic_apps,
+ LAUNCHER_ALL_APPS_TAP_OR_LONGPRESS,
+ OptionsPopupView::enterAllApps));
+ options.add(new OptionItem(launcher,
R.string.settings_button_text,
R.drawable.ic_setting,
LAUNCHER_SETTINGS_BUTTON_TAP_OR_LONGPRESS,
@@ -226,6 +232,20 @@
return options;
}
+ /**
+ * Used by the options to open All Apps, uses an intent as to not tie the implementation of
+ * opening All Apps with OptionsPopup, instead it uses the public API to open All Apps.
+ */
+ public static boolean enterAllApps(View view) {
+ Launcher launcher = Launcher.getLauncher(view.getContext());
+ launcher.startActivity(
+ new Intent(Intent.ACTION_ALL_APPS)
+ .setComponent(launcher.getComponentName())
+ .setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ );
+ return true;
+ }
+
private static boolean enterHomeGardening(View view) {
Launcher launcher = Launcher.getLauncher(view.getContext());
launcher.getStateManager().goToState(EDIT_MODE);
diff --git a/src/com/android/launcher3/widget/WidgetInflater.kt b/src/com/android/launcher3/widget/WidgetInflater.kt
index d6cadc7..03f680a 100644
--- a/src/com/android/launcher3/widget/WidgetInflater.kt
+++ b/src/com/android/launcher3/widget/WidgetInflater.kt
@@ -19,14 +19,21 @@
import android.content.Context
import com.android.launcher3.BuildConfig
import com.android.launcher3.Launcher
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
+import com.android.launcher3.dagger.ApplicationContext
import com.android.launcher3.logging.FileLog
import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.qsb.QsbContainerView
+import javax.inject.Inject
+import javax.inject.Named
/** Utility class for handling widget inflation taking into account all the restore state updates */
-class WidgetInflater(private val context: Context) {
+class WidgetInflater
+@Inject
+constructor(
+ @ApplicationContext private val context: Context,
+ @Named("SAFE_MODE") private val isSafeModeEnabled: Boolean,
+) {
private val widgetHelper = WidgetManagerHelper(context)
@@ -41,9 +48,8 @@
)
}
}
- if (LauncherAppState.INSTANCE.get(context).isSafeModeEnabled) {
- return InflationResult(TYPE_PENDING)
- }
+ if (isSafeModeEnabled) return InflationResult(TYPE_PENDING)
+
val appWidgetInfo: LauncherAppWidgetProviderInfo?
var removalReason = ""
@RestoreError var logReason = RestoreError.OTHER_WIDGET_INFLATION_FAIL
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
index 4ccf16b..ec91622 100644
--- a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.widget.picker.WidgetRecommendationCategory.DEFAULT_WIDGET_RECOMMENDATION_CATEGORY;
import static com.android.launcher3.widget.util.WidgetsTableUtils.groupWidgetItemsUsingRowPxWithReordering;
import android.content.ComponentName;
@@ -55,6 +56,7 @@
public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
private @Px float mAvailableHeight = Float.MAX_VALUE;
private @Px float mAvailableWidth = 0;
+ private int mLastUiMode = -1;
private static final String INITIALLY_DISPLAYED_WIDGETS_STATE_KEY =
"widgetRecommendationsView:mDisplayedWidgets";
private static final int MAX_CATEGORIES = 3;
@@ -151,41 +153,7 @@
mPageSwitchListeners.add(pageChangeListener);
}
- /**
- * Displays all the provided recommendations in a single table if they fit.
- *
- * @param recommendedWidgets list of widgets to be displayed in recommendation section.
- * @param deviceProfile the current {@link DeviceProfile}
- * @param availableHeight height in px that can be used to display the recommendations;
- * recommendations that don't fit in this height won't be shown
- * @param availableWidth width in px that the recommendations should display in
- * @param cellPadding padding in px that should be applied to each widget in the
- * recommendations
- * @return number of recommendations that could fit in the available space.
- */
- public int setRecommendations(
- List<WidgetItem> recommendedWidgets, DeviceProfile deviceProfile,
- final @Px float availableHeight, final @Px int availableWidth,
- final @Px int cellPadding) {
- this.mAvailableHeight = availableHeight;
- this.mAvailableWidth = availableWidth;
- clear();
-
- Set<ComponentName> displayedWidgets = maybeDisplayInTable(recommendedWidgets,
- deviceProfile,
- availableWidth, cellPadding);
-
- if (mDisplayedWidgets.isEmpty()) {
- // Save the widgets shown for the first time user opened the picker; so that, they can
- // be maintained across orientation changes.
- mDisplayedWidgets = displayedWidgets;
- }
-
- updateTitleAndIndicator(/* requestedPage= */ 0);
- return displayedWidgets.size();
- }
-
- private boolean shouldShowFullPageView(
+ private boolean shouldShowSinglePageView(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations) {
if (mShowFullPageViewIfLowDensity) {
boolean hasLessCategories = recommendations.size() < MAX_CATEGORIES;
@@ -213,63 +181,82 @@
* @param cellPadding padding in px that should be applied to each widget in the
* recommendations
* @param requestedPage page number to display initially.
+ * @param forceUpdate whether to re-render even if available space didn't change
* @return number of recommendations that could fit in the available space.
*/
public int setRecommendations(
Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations,
DeviceProfile deviceProfile, final @Px float availableHeight,
- final @Px int availableWidth, final @Px int cellPadding, final int requestedPage) {
- if (shouldShowFullPageView(recommendations)) {
- // Show all widgets in single page with unlimited available height.
- return setRecommendations(
- recommendations.values().stream().flatMap(Collection::stream)
- .collect(Collectors.toList()),
- deviceProfile, /*availableHeight=*/ Float.MAX_VALUE, availableWidth,
- cellPadding);
+ final @Px int availableWidth, final @Px int cellPadding, final int requestedPage,
+ final boolean forceUpdate) {
+ if (forceUpdate || shouldUpdate(availableWidth, availableHeight)) {
+ Context context = getContext();
+ this.mAvailableHeight = availableHeight;
+ this.mAvailableWidth = availableWidth;
+ this.mLastUiMode = context.getResources().getConfiguration().uiMode;
- }
- this.mAvailableHeight = availableHeight;
- this.mAvailableWidth = availableWidth;
- Context context = getContext();
- // For purpose of recommendations section, we don't want paging dots to be halved in two
- // pane display, so, we always provide isTwoPanels = "false".
- mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
- clear();
-
- int displayedCategories = 0;
- Set<ComponentName> allDisplayedWidgets = new HashSet<>();
-
- // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
- for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
- new TreeMap<>(recommendations).entrySet()) {
- // If none of the recommendations for the category could fit in the mAvailableHeight, we
- // don't want to add that category; and we look for the next one.
- Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(entry.getValue(),
- deviceProfile,
- availableWidth, cellPadding);
- if (!displayedWidgetsForCategory.isEmpty()) {
- mCategoryTitles.add(
- context.getResources().getString(entry.getKey().categoryTitleRes));
- displayedCategories++;
- allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+ final Map<WidgetRecommendationCategory, List<WidgetItem>> mappedRecommendations;
+ if (shouldShowSinglePageView(recommendations)) { // map to single category.
+ mappedRecommendations = Map.of(DEFAULT_WIDGET_RECOMMENDATION_CATEGORY,
+ recommendations.values().stream().flatMap(
+ Collection::stream).toList());
+ } else {
+ mappedRecommendations = recommendations;
}
- if (displayedCategories == MAX_CATEGORIES) {
- break;
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/true, /*isTwoPanels=*/ false);
+ clear();
+
+ int displayedCategories = 0;
+ Set<ComponentName> allDisplayedWidgets = new HashSet<>();
+
+ // Render top MAX_CATEGORIES in separate tables. Each table becomes a page.
+ for (Map.Entry<WidgetRecommendationCategory, List<WidgetItem>> entry :
+ new TreeMap<>(mappedRecommendations).entrySet()) {
+ // If none of the recommendations for the category could fit in the
+ // mAvailableHeight, we don't want to add that category; and we look for the next
+ // one.
+ Set<ComponentName> displayedWidgetsForCategory = maybeDisplayInTable(
+ entry.getValue(),
+ deviceProfile,
+ availableWidth, cellPadding);
+ if (!displayedWidgetsForCategory.isEmpty()) {
+ mCategoryTitles.add(
+ context.getResources().getString(entry.getKey().categoryTitleRes));
+ displayedCategories++;
+ allDisplayedWidgets.addAll(displayedWidgetsForCategory);
+ }
+
+ if (displayedCategories == MAX_CATEGORIES) {
+ break;
+ }
}
- }
- if (mDisplayedWidgets.isEmpty()) {
- // Save the widgets shown for the first time user opened the picker; so that, they can
- // be maintained across orientation changes.
- mDisplayedWidgets = allDisplayedWidgets;
- }
+ if (mDisplayedWidgets.isEmpty()) {
+ // Save the widgets shown for the first time user opened the picker; so that,
+ // they can
+ // be maintained across orientation changes.
+ mDisplayedWidgets = allDisplayedWidgets;
+ }
- updateTitleAndIndicator(requestedPage);
- // For purpose of recommendations section, we don't want paging dots to be halved in two
- // pane display, so, we always provide isTwoPanels = "false".
- mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
- return allDisplayedWidgets.size();
+ updateTitleAndIndicator(requestedPage);
+ // For purpose of recommendations section, we don't want paging dots to be halved in two
+ // pane display, so, we always provide isTwoPanels = "false".
+ mPageIndicator.setPauseScroll(/*pause=*/false, /*isTwoPanels=*/false);
+ return allDisplayedWidgets.size();
+ } else {
+ return mDisplayedWidgets.size();
+ }
+ }
+
+ /**
+ * Returns if we should re-render the views.
+ */
+ private boolean shouldUpdate(int availableWidth, float availableHeight) {
+ return this.mAvailableWidth != availableWidth || this.mAvailableHeight != availableHeight
+ || getContext().getResources().getConfiguration().uiMode != this.mLastUiMode;
}
private void clear() {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index b0abf23..44c0ebd 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -628,10 +628,12 @@
if (mIsInSearchMode) {
return;
}
+ boolean forceUpdate = false;
// We avoid applying new recommendations when some are already displayed.
if (mRecommendedWidgetsMap.isEmpty()) {
mRecommendedWidgetsMap =
mActivityContext.getWidgetPickerDataProvider().get().getRecommendations();
+ forceUpdate = true;
}
mRecommendedWidgetsCount = mWidgetRecommendationsView.setRecommendations(
mRecommendedWidgetsMap,
@@ -639,7 +641,8 @@
/* availableHeight= */ getMaxAvailableHeightForRecommendations(),
/* availableWidth= */ mMaxSpanPerRow,
/* cellPadding= */ mWidgetCellHorizontalPadding,
- /* requestedPage= */ mRecommendationsCurrentPage
+ /* requestedPage= */ mRecommendationsCurrentPage,
+ /* forceUpdate= */ forceUpdate
);
mWidgetRecommendationsContainer.setVisibility(
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 679b0f5..fc99fcc 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -112,21 +112,48 @@
// Bind the widget items.
for (int i = 0; i < widgetItemsTable.size(); i++) {
List<WidgetItem> widgetItemsPerRow = widgetItemsTable.get(i);
- for (int j = 0; j < widgetItemsPerRow.size(); j++) {
- WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
- row.setVisibility(View.VISIBLE);
- WidgetCell widget = (WidgetCell) row.getChildAt(j);
- widget.clear();
- widget.addPreviewReadyListener(row);
- WidgetItem widgetItem = widgetItemsPerRow.get(j);
- widget.setVisibility(View.VISIBLE);
+ WidgetTableRow row = (WidgetTableRow) table.getChildAt(i);
- widget.applyFromCellItem(widgetItem);
- widget.requestLayout();
+ if (areRowItemsUnchanged(row, widgetItemsPerRow)) { // Just show widgets in row as is
+ row.setVisibility(View.VISIBLE);
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.setVisibility(View.VISIBLE);
+ }
+ } else {
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ row.setVisibility(View.VISIBLE);
+ WidgetCell widget = (WidgetCell) row.getChildAt(j);
+ widget.clear();
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ widget.addPreviewReadyListener(row);
+ widget.setVisibility(View.VISIBLE);
+
+ widget.applyFromCellItem(widgetItem);
+ widget.requestLayout();
+ }
}
}
}
+ private boolean areRowItemsUnchanged(WidgetTableRow row, List<WidgetItem> widgetItemsPerRow) {
+ // NOTE: on rotation or fold / unfold, we bind different view holders
+ // so, we don't any special handling for that case.
+ if (row.getChildCount() != widgetItemsPerRow.size()) { // Items not equal
+ return false;
+ }
+
+ for (int j = 0; j < widgetItemsPerRow.size(); j++) {
+ WidgetCell widgetCell = (WidgetCell) row.getChildAt(j);
+ WidgetItem widgetItem = widgetItemsPerRow.get(j);
+ if (widgetCell.getWidgetItem() == null
+ || !widgetCell.getWidgetItem().equals(widgetItem)) {
+ return false; // Items at given position in row aren't same.
+ }
+ }
+ return true;
+ }
+
/**
* Adds and hides table rows and columns from {@code table} to ensure there is sufficient room
* to display {@code widgetItemsTable}.
@@ -151,26 +178,31 @@
tableRow.setGravity(Gravity.TOP);
table.addView(tableRow);
}
- // Pass resize delay to let the "move" and "change" animations run before resizing the
- // row.
- tableRow.setupRow(widgetItems.size(),
- /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
- if (tableRow.getChildCount() > widgetItems.size()) {
- for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
- tableRow.getChildAt(j).setVisibility(View.GONE);
- }
- } else {
- for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
- WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
- R.layout.widget_cell, tableRow, false);
- // set up touch.
- widget.setOnClickListener(mIconClickListener);
- widget.addPreviewReadyListener(tableRow);
- View preview = widget.findViewById(R.id.widget_preview_container);
- preview.setOnClickListener(mIconClickListener);
- preview.setOnLongClickListener(mIconLongClickListener);
- widget.setAnimatePreview(false);
- tableRow.addView(widget);
+
+ // If the row items are unchanged, we don't need to re-setup the row or the items;
+ // we can just show the row as is.
+ if (!areRowItemsUnchanged(tableRow, widgetItems)) {
+ // Pass resize delay to let the "move" and "change" animations run before resizing
+ // the row.
+ tableRow.setupRow(widgetItems.size(),
+ /*resizeDelayMs=*/ WIDGET_LIST_ITEM_APPEARANCE_START_DELAY);
+ if (tableRow.getChildCount() > widgetItems.size()) {
+ for (int j = widgetItems.size(); j < tableRow.getChildCount(); j++) {
+ tableRow.getChildAt(j).setVisibility(View.GONE);
+ }
+ } else {
+ for (int j = tableRow.getChildCount(); j < widgetItems.size(); j++) {
+ WidgetCell widget = (WidgetCell) mLayoutInflater.inflate(
+ R.layout.widget_cell, tableRow, false);
+ // set up touch.
+ widget.setOnClickListener(mIconClickListener);
+ widget.addPreviewReadyListener(tableRow);
+ View preview = widget.findViewById(R.id.widget_preview_container);
+ preview.setOnClickListener(mIconClickListener);
+ preview.setOnLongClickListener(mIconLongClickListener);
+ widget.setAnimatePreview(false);
+ tableRow.addView(widget);
+ }
}
}
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
index d611ae8..91ba628 100644
--- a/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/icons/UserBadgeDrawableTest.kt
@@ -34,19 +34,20 @@
private val context = InstrumentationRegistry.getInstrumentation().targetContext
private val canvas = mock<Canvas>()
private val systemUnderTest =
- UserBadgeDrawable(context, R.drawable.ic_work_app_badge, R.color.badge_tint_work, false)
+ UserBadgeDrawable(
+ context,
+ R.drawable.ic_work_app_badge,
+ R.color.badge_tint_work,
+ false /* isThemed */,
+ null, /* shape */
+ )
@Test
fun draw_opaque() {
val colorList = mutableListOf<Int>()
- whenever(
- canvas.drawCircle(
- any(),
- any(),
- any(),
- any()
- )
- ).then { colorList.add(it.getArgument<Paint>(3).color) }
+ whenever(canvas.drawCircle(any(), any(), any(), any())).then {
+ colorList.add(it.getArgument<Paint>(3).color)
+ }
systemUnderTest.alpha = 255
systemUnderTest.draw(canvas)
@@ -57,14 +58,9 @@
@Test
fun draw_transparent() {
val colorList = mutableListOf<Int>()
- whenever(
- canvas.drawCircle(
- any(),
- any(),
- any(),
- any()
- )
- ).then { colorList.add(it.getArgument<Paint>(3).color) }
+ whenever(canvas.drawCircle(any(), any(), any(), any())).then {
+ colorList.add(it.getArgument<Paint>(3).color)
+ }
systemUnderTest.alpha = 0
systemUnderTest.draw(canvas)
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
index 08b8f81..25f4cf1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/AddWorkspaceItemsTaskTest.kt
@@ -33,7 +33,6 @@
import org.mockito.kotlin.any
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
-import org.mockito.kotlin.same
import org.mockito.kotlin.verify
import org.mockito.kotlin.verifyNoMoreInteractions
import org.mockito.kotlin.whenever
@@ -142,10 +141,9 @@
/** Sets up the item space data that will be returned from WorkspaceItemSpaceFinder. */
private fun givenNewItemSpaces(vararg newItemSpaces: NewItemSpace) {
val spaceStack = newItemSpaces.toMutableList()
- whenever(
- mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any(), any(), any())
- )
- .then { spaceStack.removeFirst().toIntArray() }
+ whenever(mWorkspaceItemSpaceFinder.findSpaceForItem(any(), any(), any(), any())).then {
+ spaceStack.removeFirst().toIntArray()
+ }
}
/**
@@ -155,8 +153,6 @@
private fun verifyItemSpaceFinderCall(nonEmptyScreenIds: List<Int>, numberOfExpectedCall: Int) {
verify(mWorkspaceItemSpaceFinder, times(numberOfExpectedCall))
.findSpaceForItem(
- eq(mAppState),
- same(mModelHelper.bgDataModel),
eq(IntArray.wrap(*nonEmptyScreenIds.toIntArray())),
eq(IntArray()),
eq(1),
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
index ad40818..b848d27 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
+++ b/tests/multivalentTests/src/com/android/launcher3/model/LoaderCursorTest.java
@@ -55,7 +55,6 @@
import static junit.framework.Assert.assertTrue;
import android.content.ComponentName;
-import android.content.Context;
import android.content.Intent;
import android.database.MatrixCursor;
import android.graphics.Bitmap;
@@ -76,6 +75,7 @@
import com.android.launcher3.model.data.WorkspaceItemInfo;
import com.android.launcher3.util.Executors;
import com.android.launcher3.util.LauncherModelHelper;
+import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext;
import com.android.launcher3.util.PackageManagerHelper;
import org.junit.After;
@@ -96,11 +96,10 @@
private LauncherModelHelper mModelHelper;
private LauncherAppState mApp;
- private PackageManagerHelper mPmHelper;
private MatrixCursor mCursor;
private InvariantDeviceProfile mIDP;
- private Context mContext;
+ private SandboxModelContext mContext;
private LoaderCursor mLoaderCursor;
@@ -116,7 +115,6 @@
mContext = mModelHelper.sandboxContext;
mIDP = InvariantDeviceProfile.INSTANCE.get(mContext);
mApp = LauncherAppState.getInstance(mContext);
- mPmHelper = PackageManagerHelper.INSTANCE.get(mContext);
mCursor = new MatrixCursor(new String[] {
ICON, TITLE, _ID, CONTAINER, ITEM_TYPE,
@@ -126,7 +124,8 @@
});
UserManagerState ums = new UserManagerState();
- mLoaderCursor = new LoaderCursor(mCursor, mApp, ums, mPmHelper, null);
+ mLoaderCursor = mContext.getAppComponent().getLoaderCursorFactory()
+ .createLoaderCursor(mCursor, ums, null);
ums.allUsers.put(0, Process.myUserHandle());
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
index eec6eed..5fd93d1 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/PackageUpdatedTaskTest.kt
@@ -27,8 +27,9 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.AppFilter
import com.android.launcher3.Flags.FLAG_ENABLE_PRIVATE_SPACE
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings
+import com.android.launcher3.dagger.LauncherAppComponent
+import com.android.launcher3.dagger.LauncherAppSingleton
import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.PackageUpdatedTask.OP_ADD
import com.android.launcher3.model.PackageUpdatedTask.OP_REMOVE
@@ -39,17 +40,20 @@
import com.android.launcher3.model.PackageUpdatedTask.OP_USER_AVAILABILITY_CHANGE
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
+import com.android.launcher3.util.AllModulesForTest
import com.android.launcher3.util.Executors
import com.android.launcher3.util.LauncherModelHelper
-import com.android.launcher3.util.LauncherModelHelper.SandboxModelContext
import com.android.launcher3.util.TestUtil
import com.google.common.truth.Truth.assertThat
+import dagger.BindsInstance
+import dagger.Component
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.kotlin.any
+import org.mockito.kotlin.doAnswer
import org.mockito.kotlin.mock
import org.mockito.kotlin.spy
import org.mockito.kotlin.verify
@@ -61,10 +65,8 @@
@get:Rule val setFlagsRule = SetFlagsRule()
private val mUser = myUserHandle()
- private val mDataModel: BgDataModel = BgDataModel()
private val mLauncherModelHelper = LauncherModelHelper()
- private val mContext: SandboxModelContext = spy(mLauncherModelHelper.sandboxContext)
- private val mAppState: LauncherAppState = spy(LauncherAppState.getInstance(mContext))
+ private val mContext = mLauncherModelHelper.sandboxContext
private val expectedPackage = "Test.Package"
private val expectedComponent = ComponentName(expectedPackage, "TestClass")
@@ -72,22 +74,33 @@
private val expectedWorkspaceItem = spy(WorkspaceItemInfo())
private val mockIconCache: IconCache = mock()
- private val mockTaskController: ModelTaskController = mock<ModelTaskController>()
private val mockAppFilter: AppFilter = mock<AppFilter>()
+ private lateinit var mAllAppsList: AllAppsList
+
private val mockApplicationInfo: ApplicationInfo = mock<ApplicationInfo>()
private val mockActivityInfo: ActivityInfo = mock<ActivityInfo>()
- private lateinit var mAllAppsList: AllAppsList
+ private lateinit var mDataModel: BgDataModel
+ private lateinit var mockTaskController: ModelTaskController
@Before
fun setup() {
mAllAppsList = spy(AllAppsList(mockIconCache, mockAppFilter))
- mLauncherModelHelper.sandboxContext.spyService(LauncherApps::class.java).apply {
+ mContext.initDaggerComponent(
+ DaggerPackageUpdatedTaskTest_TestComponent.builder()
+ .bindAllAppsList(mAllAppsList)
+ .bindAppFilter(mockAppFilter)
+ .bindIconCache(mockIconCache)
+ )
+
+ mContext.spyService(LauncherApps::class.java).apply {
whenever(getActivityList(expectedPackage, mUser))
.thenReturn(listOf(expectedActivityInfo))
}
- whenever(mAppState.iconCache).thenReturn(mockIconCache)
- whenever(mockTaskController.app).thenReturn(mAppState)
+
+ mockTaskController = spy((mContext.appComponent as TestComponent).getTaskController())
+ mDataModel = (mContext.appComponent as TestComponent).getDataModel()
+
whenever(mockAppFilter.shouldShowApp(expectedComponent)).thenReturn(true)
mockApplicationInfo.apply {
uid = 1
@@ -122,7 +135,6 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mockIconCache).updateIconsForPkg(expectedPackage, mUser)
verify(mAllAppsList).addPackage(mContext, expectedPackage, mUser)
@@ -141,7 +153,6 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mockIconCache).updateIconsForPkg(expectedPackage, mUser)
verify(mAllAppsList).updatePackage(mContext, expectedPackage, mUser)
@@ -159,7 +170,6 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mockIconCache).removeIconsForPkg(expectedPackage, mUser)
verify(mAllAppsList).removePackage(expectedPackage, mUser)
@@ -176,7 +186,6 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mAllAppsList).removePackage(expectedPackage, mUser)
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem))
@@ -190,10 +199,11 @@
// When
mDataModel.addItem(mContext, expectedWorkspaceItem, true)
mAllAppsList.add(AppInfo(mContext, expectedActivityInfo, mUser), expectedActivityInfo)
+ mAllAppsList.getAndResetChangeFlag()
+ doAnswer {}.whenever(mockTaskController).bindApplicationsIfNeeded()
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mAllAppsList).updateDisabledFlags(any(), any())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWorkspaceItem))
@@ -206,10 +216,11 @@
val taskUnderTest = PackageUpdatedTask(OP_UNSUSPEND, mUser, expectedPackage)
// When
mDataModel.addItem(mContext, expectedWorkspaceItem, true)
+ mAllAppsList.getAndResetChangeFlag()
+ doAnswer {}.whenever(mockTaskController).bindApplicationsIfNeeded()
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mAllAppsList).updateDisabledFlags(any(), any())
verify(mockTaskController).bindUpdatedWorkspaceItems(emptyList())
@@ -226,10 +237,29 @@
TestUtil.runOnExecutorSync(Executors.MODEL_EXECUTOR) {
taskUnderTest.execute(mockTaskController, mDataModel, mAllAppsList)
}
- mLauncherModelHelper.loadModelSync()
// Then
verify(mAllAppsList).updateDisabledFlags(any(), any())
verify(mockTaskController).bindUpdatedWorkspaceItems(emptyList())
assertThat(mAllAppsList.data).isEmpty()
}
+
+ @LauncherAppSingleton
+ @Component(modules = [AllModulesForTest::class])
+ interface TestComponent : LauncherAppComponent {
+
+ fun getDataModel(): BgDataModel
+
+ fun getTaskController(): ModelTaskController
+
+ @Component.Builder
+ interface Builder : LauncherAppComponent.Builder {
+ @BindsInstance fun bindAppFilter(appFilter: AppFilter): Builder
+
+ @BindsInstance fun bindIconCache(iconCache: IconCache): Builder
+
+ @BindsInstance fun bindAllAppsList(list: AllAppsList): Builder
+
+ override fun build(): TestComponent
+ }
+ }
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
index c6863f4..0ab736b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/ShortcutsChangedTaskTest.kt
@@ -30,7 +30,6 @@
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.filters.SmallTest
import com.android.launcher3.Flags
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_DEEP_SHORTCUT
import com.android.launcher3.icons.BitmapInfo
import com.android.launcher3.icons.CacheableShortcutInfo
@@ -73,7 +72,6 @@
private val user: UserHandle = myUserHandle()
private val mockTaskController: ModelTaskController = mock()
private val mockAllApps: AllAppsList = mock()
- private val mockAppState: LauncherAppState = mock()
private val mockIconCache: IconCache = mock()
private val expectedWai =
@@ -93,9 +91,8 @@
modelHelper.loadModelSync()
context = modelHelper.sandboxContext
launcherApps = context.spyService(LauncherApps::class.java)
- whenever(mockTaskController.app).thenReturn(mockAppState)
- whenever(mockAppState.context).thenReturn(context)
- whenever(mockAppState.iconCache).thenReturn(mockIconCache)
+ whenever(mockTaskController.context).thenReturn(context)
+ whenever(mockTaskController.iconCache).thenReturn(mockIconCache)
whenever(mockIconCache.getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>()))
.then { _ -> { expectedWai.bitmap = BitmapInfo.LOW_RES_INFO } }
shortcuts = emptyList()
@@ -132,8 +129,7 @@
// When
shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
// Then
- verify(mockAppState.iconCache)
- .getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
+ verify(mockIconCache).getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
@@ -196,8 +192,7 @@
// When
shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
// Then
- verify(mockAppState.iconCache)
- .getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
+ verify(mockIconCache).getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
@@ -256,8 +251,7 @@
// When
shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
// Then
- verify(mockAppState.iconCache)
- .getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
+ verify(mockIconCache).getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
@@ -287,8 +281,7 @@
// When
shortcutsChangedTask.execute(mockTaskController, modelHelper.bgDataModel, mockAllApps)
// Then
- verify(mockAppState.iconCache)
- .getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
+ verify(mockIconCache).getShortcutIcon(eq(expectedWai), any<CacheableShortcutInfo>())
verify(mockTaskController).bindUpdatedWorkspaceItems(listOf(expectedWai))
}
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
index 777d81b..2e6ecc5 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WidgetsModelTest.kt
@@ -25,9 +25,9 @@
import android.platform.test.rule.LimitDevicesRule
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.launcher3.AppFilter
import com.android.launcher3.DeviceProfile
import com.android.launcher3.InvariantDeviceProfile
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.data.PackageItemInfo
import com.android.launcher3.pm.UserCache
@@ -62,7 +62,6 @@
@Rule @JvmField val mockitoRule: MockitoRule = MockitoJUnit.rule()
@Mock private lateinit var appWidgetManager: AppWidgetManager
- @Mock private lateinit var app: LauncherAppState
@Mock private lateinit var iconCacheMock: IconCache
private lateinit var context: Context
@@ -93,9 +92,6 @@
whenever(iconCacheMock.getTitleNoCache(any<LauncherAppWidgetProviderInfo>()))
.thenReturn("title")
- whenever(app.iconCache).thenReturn(iconCacheMock)
- whenever(app.context).thenReturn(context)
- whenever(app.invariantDeviceProfile).thenReturn(idp)
val widgetToCategoryEntry: Map.Entry<ComponentName, IntSet> =
WidgetSections.getWidgetsToCategory(context).entries.first()
@@ -128,7 +124,7 @@
val userCache = spy(UserCache.INSTANCE.get(context))
whenever(userCache.userProfiles).thenReturn(listOf(UserHandle.CURRENT))
- underTest = WidgetsModel()
+ underTest = WidgetsModel(context, idp, iconCacheMock, AppFilter(context))
}
@Test
@@ -237,7 +233,7 @@
update = false
// Similarly, model could update its code independently while a client is
// iterating on the list.
- underTest.update(app, /* packageUser= */ null)
+ underTest.update(/* packageUser= */ null)
}
}
@@ -253,7 +249,7 @@
private fun loadWidgets() {
val latch = CountDownLatch(1)
Executors.MODEL_EXECUTOR.execute {
- underTest.update(app, /* packageUser= */ null)
+ underTest.update(/* packageUser= */ null)
latch.countDown()
}
if (!latch.await(LOAD_WIDGETS_TIMEOUT_SECONDS, TimeUnit.SECONDS)) {
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
index a55d64b..cba7b88 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemProcessorTest.kt
@@ -31,7 +31,7 @@
import android.util.LongSparseArray
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.Flags
-import com.android.launcher3.LauncherAppState
+import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
@@ -41,6 +41,7 @@
import com.android.launcher3.Utilities.EMPTY_PERSON_ARRAY
import com.android.launcher3.backuprestore.LauncherRestoreEventLogger.RestoreError
import com.android.launcher3.icons.CacheableShortcutInfo
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.data.FolderInfo
import com.android.launcher3.model.data.IconRequestInfo
import com.android.launcher3.model.data.ItemInfo
@@ -66,12 +67,14 @@
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito.RETURNS_DEEP_STUBS
import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doAnswer
@@ -88,12 +91,12 @@
@Mock private lateinit var mockIconRequestInfo: IconRequestInfo<WorkspaceItemInfo>
@Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
@Mock private lateinit var mockBgDataModel: BgDataModel
- @Mock private lateinit var mockAppState: LauncherAppState
@Mock private lateinit var mockPmHelper: PackageManagerHelper
- @Mock private lateinit var mockCursor: LoaderCursor
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var mockCursor: LoaderCursor
@Mock private lateinit var mockUserCache: UserCache
@Mock private lateinit var mockUserManagerState: UserManagerState
@Mock private lateinit var mockWidgetInflater: WidgetInflater
+ @Mock private lateinit var mockIconCache: IconCache
lateinit var mModelHelper: LauncherModelHelper
lateinit var mContext: SandboxModelContext
@@ -114,6 +117,7 @@
@Before
fun setup() {
+ MockitoAnnotations.initMocks(this)
mModelHelper = LauncherModelHelper()
mContext = mModelHelper.sandboxContext
mLauncherApps =
@@ -122,9 +126,6 @@
doReturn(true).whenever(this).isActivityEnabled(mComponentName, mUserHandle)
}
mUserHandle = Process.myUserHandle()
- mockIconRequestInfo = mock<IconRequestInfo<WorkspaceItemInfo>>()
- mockWorkspaceInfo = mock<WorkspaceItemInfo>()
- mockBgDataModel = mock<BgDataModel>()
mComponentName = ComponentName("package", "class")
mUnlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
mIntent =
@@ -133,40 +134,26 @@
`package` = "pkg"
putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "")
}
- mockAppState =
- mock<LauncherAppState>().apply {
- whenever(context).thenReturn(mContext)
- whenever(iconCache).thenReturn(mock())
- whenever(iconCache.getShortcutIcon(any(), any(), any())).then {}
- }
- mockPmHelper =
- mock<PackageManagerHelper>().apply {
- whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
- .thenReturn(mIntent)
- }
- mockCursor =
- mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
- user = mUserHandle
- itemType = ITEM_TYPE_APPLICATION
- id = 1
- restoreFlag = 1
- serialNumber = 101
- whenever(parseIntent()).thenReturn(mIntent)
- whenever(markRestored()).doAnswer { restoreFlag = 0 }
- whenever(updater().put(Favorites.INTENT, mIntent.toUri(0)).commit()).thenReturn(1)
- whenever(getAppShortcutInfo(any(), any(), any(), any()))
- .thenReturn(mockWorkspaceInfo)
- whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
- }
- mockUserCache =
- mock<UserCache>().apply {
- val userIconInfo =
- mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
- whenever(getUserInfo(any())).thenReturn(userIconInfo)
- }
+ whenever(mockIconCache.getShortcutIcon(any(), any(), any())).then {}
+ whenever(mockPmHelper.getAppLaunchIntent(mComponentName.packageName, mUserHandle))
+ .thenReturn(mIntent)
+ mockCursor.apply {
+ user = mUserHandle
+ itemType = ITEM_TYPE_APPLICATION
+ id = 1
+ restoreFlag = 1
+ serialNumber = 101
+ whenever(parseIntent()).thenReturn(mIntent)
+ whenever(markRestored()).doAnswer { restoreFlag = 0 }
+ whenever(updater().put(Favorites.INTENT, mIntent.toUri(0)).commit()).thenReturn(1)
+ whenever(getAppShortcutInfo(any(), any(), any(), any())).thenReturn(mockWorkspaceInfo)
+ whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
+ }
+ mockUserCache.apply {
+ val userIconInfo = mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
+ whenever(getUserInfo(any())).thenReturn(userIconInfo)
+ }
- mockUserManagerState = mock<UserManagerState>()
- mockWidgetInflater = mock<WidgetInflater>()
mKeyToPinnedShortcutsMap = mutableMapOf()
mInstallingPkgs = hashMapOf()
mAllDeepShortcuts = mutableListOf()
@@ -187,7 +174,6 @@
userManagerState: UserManagerState = mockUserManagerState,
launcherApps: LauncherApps = mLauncherApps,
shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = mKeyToPinnedShortcutsMap,
- app: LauncherAppState = mockAppState,
bgDataModel: BgDataModel = mockBgDataModel,
widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mWidgetProvidersMap,
widgetInflater: WidgetInflater = mockWidgetInflater,
@@ -205,7 +191,7 @@
userCache = userCache,
userManagerState = userManagerState,
launcherApps = launcherApps,
- app = app,
+ context = mContext,
bgDataModel = bgDataModel,
widgetProvidersMap = widgetProvidersMap,
widgetInflater = widgetInflater,
@@ -217,6 +203,9 @@
shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts,
installingPkgs = installingPkgs,
allDeepShortcuts = allDeepShortcuts,
+ iconCache = mockIconCache,
+ idp = InvariantDeviceProfile.INSTANCE.get(mContext),
+ isSafeMode = false,
)
@Test
diff --git a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
index dd03eee..a18f93b 100644
--- a/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
+++ b/tests/multivalentTests/src/com/android/launcher3/model/WorkspaceItemSpaceFinderTest.kt
@@ -29,8 +29,6 @@
@RunWith(AndroidJUnit4::class)
class WorkspaceItemSpaceFinderTest : AbstractWorkspaceModelTest() {
- private val mItemSpaceFinder = WorkspaceItemSpaceFinder()
-
@Before
override fun setup() {
super.setup()
@@ -42,15 +40,12 @@
}
private fun findSpace(spanX: Int, spanY: Int): NewItemSpace =
- mItemSpaceFinder
- .findSpaceForItem(
- mAppState,
+ WorkspaceItemSpaceFinder(
mModelHelper.bgDataModel,
- mExistingScreens,
- mNewScreens,
- spanX,
- spanY,
+ mAppState.invariantDeviceProfile,
+ mModelHelper.model,
)
+ .findSpaceForItem(mExistingScreens, mNewScreens, spanX, spanY)
.let { NewItemSpace.fromIntArray(it) }
private fun assertRegionVacant(newItemSpace: NewItemSpace, spanX: Int, spanY: Int) {
diff --git a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
index 95d5076..f490bd6 100644
--- a/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
+++ b/tests/src/com/android/launcher3/dragging/TaplUninstallRemoveTest.java
@@ -36,7 +36,6 @@
import com.android.launcher3.ui.PortraitLandscapeRunner.PortraitLandscape;
import com.android.launcher3.util.TestUtil;
import com.android.launcher3.util.Wait;
-import com.android.launcher3.util.rule.ScreenRecordRule;
import org.junit.Test;
@@ -127,7 +126,6 @@
* Adds three icons to the workspace and removes one of them by dragging to uninstall.
*/
@Test
- @ScreenRecordRule.ScreenRecord // b/399756302
@PlatinumTest(focusArea = "launcher")
public void uninstallWorkspaceIcon() throws IOException {
Point[] gridPositions = TestUtil.getCornersAndCenterPositions(mLauncher);
diff --git a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
index 246219f..7f9b7a0 100644
--- a/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
+++ b/tests/src/com/android/launcher3/model/LoaderTaskTest.kt
@@ -16,7 +16,6 @@
import com.android.dx.mockito.inline.extended.ExtendedMockito
import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.launcher3.Flags
-import com.android.launcher3.LauncherAppState
import com.android.launcher3.LauncherModel
import com.android.launcher3.LauncherModel.LoaderTransaction
import com.android.launcher3.LauncherPrefs
@@ -31,6 +30,7 @@
import com.android.launcher3.icons.IconCache
import com.android.launcher3.icons.cache.CachingLogic
import com.android.launcher3.icons.cache.IconCacheUpdateHandler
+import com.android.launcher3.model.LoaderTask.LoaderTaskFactory
import com.android.launcher3.model.data.AppInfo
import com.android.launcher3.model.data.IconRequestInfo
import com.android.launcher3.model.data.WorkspaceItemInfo
@@ -43,7 +43,6 @@
import com.android.launcher3.util.LooperIdleLock
import com.android.launcher3.util.TestUtil
import com.android.launcher3.util.UserIconInfo
-import com.google.common.truth.Truth
import com.google.common.truth.Truth.assertThat
import dagger.BindsInstance
import dagger.Component
@@ -57,7 +56,6 @@
import org.mockito.ArgumentCaptor
import org.mockito.Mock
import org.mockito.Mockito
-import org.mockito.Mockito.mock
import org.mockito.Mockito.times
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -93,26 +91,28 @@
@Mock private lateinit var bgAllAppsList: AllAppsList
@Mock private lateinit var modelDelegate: ModelDelegate
- @Mock private lateinit var launcherBinder: BaseLauncherBinder
- private lateinit var launcherModel: LauncherModel
- @Mock private lateinit var transaction: LoaderTransaction
+ @Mock private lateinit var launcherModel: LauncherModel
@Mock private lateinit var iconCache: IconCache
- @Mock private lateinit var idleLock: LooperIdleLock
- @Mock private lateinit var iconCacheUpdateHandler: IconCacheUpdateHandler
@Mock private lateinit var userCache: UserCache
- @Spy private var userManagerState: UserManagerState? = UserManagerState()
+ @Mock private lateinit var launcherBinder: BaseLauncherBinder
+ @Mock private lateinit var transaction: LoaderTransaction
+ @Mock private lateinit var idleLock: LooperIdleLock
+ @Mock private lateinit var iconCacheUpdateHandler: IconCacheUpdateHandler
+
+ @Spy private var userManagerState: UserManagerState = UserManagerState()
@get:Rule val setFlagsRule = SetFlagsRule()
- private val app: LauncherAppState
- get() = context.appComponent.launcherAppState
+ private val testComponent: TestComponent
+ get() = context.appComponent as TestComponent
+
+ private val bgDataModel: BgDataModel
+ get() = testComponent.getDataModel()
@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()
.strictness(Strictness.LENIENT)
@@ -125,14 +125,17 @@
`when`(launcherModel.beginLoader(any())).thenReturn(transaction)
`when`(launcherModel.modelDbController)
.thenReturn(FactitiousDbController(context, INSERTION_STATEMENT_FILE))
+ `when`(launcherModel.modelDelegate).thenReturn(modelDelegate)
`when`(launcherBinder.newIdleLock(any())).thenReturn(idleLock)
`when`(idleLock.awaitLocked(1000)).thenReturn(false)
`when`(iconCache.getUpdateHandler()).thenReturn(iconCacheUpdateHandler)
+
context.initDaggerComponent(
DaggerLoaderTaskTest_TestComponent.builder()
.bindUserCache(userCache)
.bindIconCache(iconCache)
.bindLauncherModel(launcherModel)
+ .bindAllAppsList(bgAllAppsList)
)
context.appComponent.idp.apply {
numRows = 5
@@ -152,14 +155,16 @@
@Test
fun loadsDataProperly() =
- with(BgDataModel()) {
+ with(bgDataModel) {
val MAIN_HANDLE = Process.myUserHandle()
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)
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
- Truth.assertThat(
+ assertThat(
itemsIdMap
.filter {
it.container == CONTAINER_DESKTOP || it.container == CONTAINER_HOTSEAT
@@ -167,9 +172,8 @@
.size
)
.isAtLeast(32)
- Truth.assertThat(itemsIdMap.filter { ModelUtils.WIDGET_FILTER.test(it) }.size)
- .isAtLeast(7)
- Truth.assertThat(
+ assertThat(itemsIdMap.filter { ModelUtils.WIDGET_FILTER.test(it) }.size).isAtLeast(7)
+ assertThat(
itemsIdMap
.filter {
it.itemType == ITEM_TYPE_FOLDER || it.itemType == ITEM_TYPE_APP_PAIR
@@ -177,12 +181,14 @@
.size
)
.isAtLeast(8)
- Truth.assertThat(itemsIdMap.size()).isAtLeast(40)
+ assertThat(itemsIdMap.size()).isAtLeast(40)
}
@Test
fun bindsLoadedDataCorrectly() {
- LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
verify(launcherBinder).bindWorkspace(true, false)
@@ -200,7 +206,7 @@
@Test
fun setsQuietModeFlagCorrectlyForWorkProfile() =
- with(BgDataModel()) {
+ with(bgDataModel) {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE)
val MAIN_HANDLE = Process.myUserHandle()
val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
@@ -208,7 +214,9 @@
`when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 1))
- LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
verify(bgAllAppsList)
@@ -221,7 +229,7 @@
@Test
fun setsQuietModeFlagCorrectlyForPrivateProfile() =
- with(BgDataModel()) {
+ with(bgDataModel) {
setFlagsRule.enableFlags(Flags.FLAG_ENABLE_PRIVATE_SPACE)
val MAIN_HANDLE = Process.myUserHandle()
val mockUserHandles = arrayListOf<UserHandle>(MAIN_HANDLE)
@@ -229,7 +237,9 @@
`when`(userManagerState?.isUserQuiet(MAIN_HANDLE)).thenReturn(true)
`when`(userCache.getUserInfo(MAIN_HANDLE)).thenReturn(UserIconInfo(MAIN_HANDLE, 3))
- LoaderTask(app, bgAllAppsList, this, modelDelegate, launcherBinder, userManagerState)
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
verify(bgAllAppsList)
@@ -268,7 +278,9 @@
RestoreDbTask.setPending(spyContext)
// When
- LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
// Then
@@ -336,7 +348,9 @@
Settings.Secure.putInt(spyContext.contentResolver, "launcher_broadcast_installed_apps", 0)
// When
- LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
// Then
@@ -375,7 +389,9 @@
RestoreDbTask.setPending(spyContext)
// When
- LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
// Then
@@ -414,7 +430,9 @@
RestoreDbTask.setPending(spyContext)
// When
- LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ testComponent
+ .getLoaderTaskFactory()
+ .newLoaderTask(launcherBinder, userManagerState)
.runSyncOnBackgroundThread()
// Then
@@ -444,7 +462,8 @@
)
val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
// When
- val loader = LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ val loader =
+ testComponent.getLoaderTaskFactory().newLoaderTask(launcherBinder, userManagerState)
val actualIconRequest =
loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
// Then
@@ -474,7 +493,8 @@
)
val expectedAppInfo = AppInfo().apply { componentName = expectedComponent }
// When
- val loader = LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ val loader =
+ testComponent.getLoaderTaskFactory().newLoaderTask(launcherBinder, userManagerState)
val actualIconRequest =
loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
// Then
@@ -505,7 +525,8 @@
val expectedAppInfo =
AppInfo().apply { componentName = ComponentName("differentPkg", "differentClass") }
// When
- val loader = LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ val loader =
+ testComponent.getLoaderTaskFactory().newLoaderTask(launcherBinder, userManagerState)
val actualIconRequest =
loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
// Then
@@ -532,7 +553,8 @@
)
val expectedAppInfo = AppInfo()
// When
- val loader = LoaderTask(app, bgAllAppsList, BgDataModel(), modelDelegate, launcherBinder)
+ val loader =
+ testComponent.getLoaderTaskFactory().newLoaderTask(launcherBinder, userManagerState)
val actualIconRequest =
loader.getAppInfoIconRequestInfo(expectedAppInfo, activityInfo, workspaceIconRequests)
// Then
@@ -543,6 +565,11 @@
@LauncherAppSingleton
@Component(modules = [AllModulesForTest::class])
interface TestComponent : LauncherAppComponent {
+
+ fun getLoaderTaskFactory(): LoaderTaskFactory
+
+ fun getDataModel(): BgDataModel
+
@Component.Builder
interface Builder : LauncherAppComponent.Builder {
@BindsInstance fun bindUserCache(userCache: UserCache): Builder
@@ -551,6 +578,8 @@
@BindsInstance fun bindIconCache(iconCache: IconCache): Builder
+ @BindsInstance fun bindAllAppsList(list: AllAppsList): Builder
+
override fun build(): TestComponent
}
}
diff --git a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
index 8db049c..fd44023 100644
--- a/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
+++ b/tests/src/com/android/launcher3/model/WorkspaceItemProcessorExtraTest.kt
@@ -30,12 +30,13 @@
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.android.launcher3.Flags.FLAG_ENABLE_SUPPORT_FOR_ARCHIVING
-import com.android.launcher3.LauncherAppState
+import com.android.launcher3.InvariantDeviceProfile
import com.android.launcher3.LauncherSettings.Favorites
import com.android.launcher3.LauncherSettings.Favorites.CONTAINER_DESKTOP
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPLICATION
import com.android.launcher3.LauncherSettings.Favorites.ITEM_TYPE_APPWIDGET
import com.android.launcher3.icons.CacheableShortcutInfo
+import com.android.launcher3.icons.IconCache
import com.android.launcher3.model.data.IconRequestInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo
import com.android.launcher3.model.data.LauncherAppWidgetInfo.FLAG_RESTORE_STARTED
@@ -52,11 +53,12 @@
import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
+import org.mockito.Answers
import org.mockito.ArgumentCaptor
import org.mockito.Mock
-import org.mockito.Mockito
import org.mockito.Mockito.RETURNS_DEEP_STUBS
import org.mockito.Mockito.verify
+import org.mockito.MockitoAnnotations
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.doAnswer
@@ -71,10 +73,10 @@
@Mock private lateinit var mockWorkspaceInfo: WorkspaceItemInfo
@Mock private lateinit var mockBgDataModel: BgDataModel
@Mock private lateinit var mockContext: Context
- @Mock private lateinit var mockAppState: LauncherAppState
+ @Mock private lateinit var mockIconCache: IconCache
@Mock private lateinit var mockPmHelper: PackageManagerHelper
@Mock private lateinit var mockLauncherApps: LauncherApps
- @Mock private lateinit var mockCursor: LoaderCursor
+ @Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var mockCursor: LoaderCursor
@Mock private lateinit var mockUserCache: UserCache
@Mock private lateinit var mockUserManagerState: UserManagerState
@Mock private lateinit var mockWidgetInflater: WidgetInflater
@@ -95,10 +97,9 @@
@Before
fun setup() {
+ MockitoAnnotations.initMocks(this)
+
mUserHandle = UserHandle(0)
- mockIconRequestInfo = mock<IconRequestInfo<WorkspaceItemInfo>>()
- mockWorkspaceInfo = mock<WorkspaceItemInfo>()
- mockBgDataModel = mock<BgDataModel>()
mComponentName = ComponentName("package", "class")
mUnlockedUsersArray = LongSparseArray<Boolean>(1).apply { put(101, true) }
intent =
@@ -107,52 +108,36 @@
`package` = "pkg"
putExtra(ShortcutKey.EXTRA_SHORTCUT_ID, "")
}
- mockLauncherApps =
- mock<LauncherApps>().apply {
- whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
- whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
- }
- mockContext =
- mock<Context>().apply {
- whenever(packageManager).thenReturn(mock())
- whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
- whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
- whenever(getSystemService(LauncherApps::class.java)).thenReturn(mockLauncherApps)
- }
- mockAppState =
- mock<LauncherAppState>().apply {
- whenever(context).thenReturn(mockContext)
- whenever(iconCache).thenReturn(mock())
- whenever(iconCache.getShortcutIcon(any(), any(), any())).then {}
- }
- mockPmHelper =
- mock<PackageManagerHelper>().apply {
- whenever(getAppLaunchIntent(mComponentName.packageName, mUserHandle))
- .thenReturn(intent)
- }
- mockCursor =
- Mockito.mock(LoaderCursor::class.java, RETURNS_DEEP_STUBS).apply {
- user = mUserHandle
- itemType = ITEM_TYPE_APPLICATION
- id = 1
- restoreFlag = 1
- serialNumber = 101
- whenever(parseIntent()).thenReturn(intent)
- whenever(markRestored()).doAnswer { restoreFlag = 0 }
- whenever(updater().put(Favorites.INTENT, intent.toUri(0)).commit()).thenReturn(1)
- whenever(getAppShortcutInfo(any(), any(), any(), any()))
- .thenReturn(mockWorkspaceInfo)
- whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
- }
- mockUserCache =
- mock<UserCache>().apply {
- val userIconInfo =
- mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
- whenever(getUserInfo(any())).thenReturn(userIconInfo)
- }
+ mockLauncherApps.apply {
+ whenever(isPackageEnabled("package", mUserHandle)).thenReturn(true)
+ whenever(isActivityEnabled(mComponentName, mUserHandle)).thenReturn(true)
+ }
+ mockContext.apply {
+ whenever(packageManager).thenReturn(mock())
+ whenever(packageManager.getUserBadgedLabel(any(), any())).thenReturn("")
+ whenever(applicationContext).thenReturn(ApplicationProvider.getApplicationContext())
+ whenever(getSystemService(LauncherApps::class.java)).thenReturn(mockLauncherApps)
+ }
- mockUserManagerState = mock<UserManagerState>()
- mockWidgetInflater = mock<WidgetInflater>()
+ whenever(mockIconCache.getShortcutIcon(any(), any(), any())).then {}
+ whenever(mockPmHelper.getAppLaunchIntent(mComponentName.packageName, mUserHandle))
+ .thenReturn(intent)
+ mockCursor.apply {
+ user = mUserHandle
+ itemType = ITEM_TYPE_APPLICATION
+ id = 1
+ restoreFlag = 1
+ serialNumber = 101
+ whenever(parseIntent()).thenReturn(intent)
+ whenever(markRestored()).doAnswer { restoreFlag = 0 }
+ whenever(updater().put(Favorites.INTENT, intent.toUri(0)).commit()).thenReturn(1)
+ whenever(getAppShortcutInfo(any(), any(), any(), any())).thenReturn(mockWorkspaceInfo)
+ whenever(createIconRequestInfo(any(), any())).thenReturn(mockIconRequestInfo)
+ }
+
+ val mockUserInfo = mock<UserIconInfo>().apply { whenever(isPrivate).thenReturn(false) }
+ whenever(mockUserCache.getUserInfo(any())).thenReturn(mockUserInfo)
+
mKeyToPinnedShortcutsMap = mutableMapOf()
mInstallingPkgs = hashMapOf()
mAllDeepShortcuts = mutableListOf()
@@ -283,7 +268,7 @@
userManagerState: UserManagerState = mockUserManagerState,
launcherApps: LauncherApps = mockLauncherApps,
shortcutKeyToPinnedShortcuts: Map<ShortcutKey, ShortcutInfo> = mKeyToPinnedShortcutsMap,
- app: LauncherAppState = mockAppState,
+ context: Context = mockContext,
bgDataModel: BgDataModel = mockBgDataModel,
widgetProvidersMap: MutableMap<ComponentKey, AppWidgetProviderInfo?> = mWidgetProvidersMap,
widgetInflater: WidgetInflater = mockWidgetInflater,
@@ -297,11 +282,11 @@
) =
WorkspaceItemProcessor(
c = cursor,
+ context = context,
memoryLogger = memoryLogger,
userCache = userCache,
userManagerState = userManagerState,
launcherApps = launcherApps,
- app = app,
bgDataModel = bgDataModel,
widgetProvidersMap = widgetProvidersMap,
widgetInflater = widgetInflater,
@@ -313,5 +298,8 @@
shortcutKeyToPinnedShortcuts = shortcutKeyToPinnedShortcuts,
installingPkgs = installingPkgs,
allDeepShortcuts = allDeepShortcuts,
+ idp = InvariantDeviceProfile.INSTANCE.get(context),
+ iconCache = mockIconCache,
+ isSafeMode = false,
)
}