Loading quickstep/src/com/android/launcher3/WidgetPickerActivity.java +21 −26 Original line number Diff line number Diff line Loading @@ -47,11 +47,11 @@ import com.android.launcher3.model.WidgetItem; import com.android.launcher3.model.WidgetPredictionsRequester; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.widget.WidgetCell; import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder; import com.android.launcher3.widget.model.WidgetsListBaseEntry; import com.android.launcher3.widget.model.WidgetsListContentEntry; import com.android.launcher3.widget.picker.WidgetsFullSheet; import java.util.ArrayList; Loading @@ -60,10 +60,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; /** An Activity that can host Launcher's widget picker. */ public class WidgetPickerActivity extends BaseActivity { Loading Loading @@ -112,7 +110,8 @@ public class WidgetPickerActivity extends BaseActivity { private WidgetsModel mModel; private LauncherAppState mApp; private WidgetPredictionsRequester mWidgetPredictionsRequester; private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {}); private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> { }); private int mDesiredWidgetWidth; private int mDesiredWidgetHeight; Loading Loading @@ -287,41 +286,37 @@ public class WidgetPickerActivity extends BaseActivity { }; } /** Updates the model with widgets, applies filters and launches the widgets sheet once * widgets are available */ /** * Updates the model with widgets, applies filters and launches the widgets sheet once * widgets are available */ private void refreshAndBindWidgets() { MODEL_EXECUTOR.execute(() -> { LauncherAppState app = LauncherAppState.getInstance(this); Context context = app.getContext(); mModel.update(app, null); final List<WidgetsListBaseEntry> allWidgets = mModel.getFilteredWidgetsListForPicker(context, mWidgetsFilter); final List<WidgetsListBaseEntry> defaultWidgets = shouldShowDefaultWidgets() ? mModel.getFilteredWidgetsListForPicker(context, mDefaultWidgetsFilter) : List.of(); bindWidgets(allWidgets, defaultWidgets); bindWidgets(mModel.getWidgetsByPackageItem()); // Open sheet once widgets are available, so that it doesn't interrupt the open // animation. openWidgetsSheet(); if (mUiSurface != null) { Map<ComponentKey, WidgetItem> allWidgetItems = allWidgets.stream() .filter(entry -> entry instanceof WidgetsListContentEntry) .flatMap(entry -> entry.mWidgets.stream()) .distinct() .collect(Collectors.toMap( widget -> new ComponentKey(widget.componentName, widget.user), Function.identity() )); mWidgetPredictionsRequester = new WidgetPredictionsRequester(app.getContext(), mUiSurface, allWidgetItems); mUiSurface, mModel.getWidgetsByComponentKey()); mWidgetPredictionsRequester.request(mAddedWidgets, this::bindRecommendedWidgets); } }); } private void bindWidgets(List<WidgetsListBaseEntry> allWidgets, List<WidgetsListBaseEntry> defaultWidgets) { private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets) { WidgetsListBaseEntriesBuilder builder = new WidgetsListBaseEntriesBuilder( mApp.getContext()); final List<WidgetsListBaseEntry> allWidgets = builder.build(widgets, mWidgetsFilter); final List<WidgetsListBaseEntry> defaultWidgets = shouldShowDefaultWidgets() ? builder.build(widgets, mDefaultWidgetsFilter) : List.of(); MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(allWidgets, defaultWidgets)); } Loading Loading @@ -392,7 +387,7 @@ public class WidgetPickerActivity extends BaseActivity { mActiveOnBackAnimationCallback.onBackCancelled(); mActiveOnBackAnimationCallback = null; } }; } private boolean shouldShowDefaultWidgets() { // If optional filters such as size filter are present, we display them as default widgets. Loading src/com/android/launcher3/model/BaseLauncherBinder.java +3 −2 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.LooperIdleLock; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.RunnableList; import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder; import com.android.launcher3.widget.model.WidgetsListBaseEntry; import java.util.ArrayList; Loading Loading @@ -195,8 +196,8 @@ public class BaseLauncherBinder { if (!WIDGETS_ENABLED) { return; } final List<WidgetsListBaseEntry> widgets = mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext()); List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mApp.getContext()) .build(mBgDataModel.widgetsModel.getWidgetsByPackageItem()); executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor); } Loading src/com/android/launcher3/model/ModelTaskController.kt +4 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import com.android.launcher3.model.BgDataModel.FixedContainerItems import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.WorkspaceItemInfo 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 Loading Loading @@ -78,7 +79,9 @@ class ModelTaskController( } fun bindUpdatedWidgets(dataModel: BgDataModel) { val widgets = dataModel.widgetsModel.getWidgetsListForPicker(app.context) val widgets = WidgetsListBaseEntriesBuilder(app.context) .build(dataModel.widgetsModel.widgetsByPackageItem) scheduleCallbackTask { it.bindAllWidgets(widgets) } } Loading src/com/android/launcher3/model/WidgetsModel.java +5 −47 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import com.android.launcher3.AppFilter; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.ComponentWithLabelAndIcon; import com.android.launcher3.icons.IconCache; Loading @@ -39,9 +38,6 @@ import com.android.launcher3.util.Preconditions; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.WidgetSections; import com.android.launcher3.widget.model.WidgetsListBaseEntry; import com.android.launcher3.widget.model.WidgetsListContentEntry; import com.android.launcher3.widget.model.WidgetsListHeaderEntry; import com.android.wm.shell.Flags; import java.util.ArrayList; Loading Loading @@ -75,6 +71,9 @@ public class WidgetsModel { * Returns all widgets keyed by their component key. */ public synchronized Map<ComponentKey, WidgetItem> getWidgetsByComponentKey() { if (!WIDGETS_ENABLED) { return Collections.emptyMap(); } return mWidgetsByPackageItem.values().stream() .flatMap(Collection::stream).distinct() .collect(Collectors.toMap( Loading @@ -87,51 +86,10 @@ public class WidgetsModel { * Returns widgets grouped by the package item that they should belong to. */ public synchronized Map<PackageItemInfo, List<WidgetItem>> getWidgetsByPackageItem() { return mWidgetsByPackageItem; } /** * Returns a list of {@link WidgetsListBaseEntry} filtered using given widget item filter. All * {@link WidgetItem}s in a single row are sorted (based on label and user), but the overall * list of {@link WidgetsListBaseEntry}s is not sorted. * * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List) */ public synchronized ArrayList<WidgetsListBaseEntry> getFilteredWidgetsListForPicker( Context context, Predicate<WidgetItem> widgetItemFilter) { if (!WIDGETS_ENABLED) { return new ArrayList<>(); return Collections.emptyMap(); } ArrayList<WidgetsListBaseEntry> result = new ArrayList<>(); AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context); for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsByPackageItem.entrySet()) { PackageItemInfo pkgItem = entry.getKey(); List<WidgetItem> widgetItems = entry.getValue() .stream() .filter(widgetItemFilter).toList(); if (!widgetItems.isEmpty()) { String sectionName = (pkgItem.title == null) ? "" : indexer.computeSectionName(pkgItem.title); result.add(WidgetsListHeaderEntry.create(pkgItem, sectionName, widgetItems)); result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems)); } } return result; } /** * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row * are sorted (based on label and user), but the overall list of * {@link WidgetsListBaseEntry}s is not sorted. * * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List) */ public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) { // return all items return getFilteredWidgetsListForPicker(context, /*widgetItemFilter=*/ item -> true); return mWidgetsByPackageItem; } /** Loading src/com/android/launcher3/widget/model/WidgetsListBaseEntriesBuilder.kt 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.widget.model import android.content.Context import com.android.launcher3.compat.AlphabeticIndexCompat import com.android.launcher3.model.WidgetItem import com.android.launcher3.model.data.PackageItemInfo import java.util.function.Predicate /** * A helper class that builds the list of [WidgetsListBaseEntry]s used by the UI to display widgets. */ class WidgetsListBaseEntriesBuilder(val context: Context) { /** Builds the widgets list entries in a format understandable by the widget picking UI. */ @JvmOverloads fun build( widgetsByPackageItem: Map<PackageItemInfo, List<WidgetItem>>, widgetFilter: Predicate<WidgetItem> = Predicate<WidgetItem> { true }, ): List<WidgetsListBaseEntry> { val indexer = AlphabeticIndexCompat(context) return buildList { for ((pkgItem, widgetItems) in widgetsByPackageItem.entries) { val filteredWidgetItems = widgetItems.filter { widgetFilter.test(it) } if (filteredWidgetItems.isNotEmpty()) { // Enables fast scroll popup to show right characters in all locales. val sectionName = pkgItem.title?.let { indexer.computeSectionName(it) } ?: "" add(WidgetsListHeaderEntry.create(pkgItem, sectionName, filteredWidgetItems)) add(WidgetsListContentEntry(pkgItem, sectionName, filteredWidgetItems)) } } } } } Loading
quickstep/src/com/android/launcher3/WidgetPickerActivity.java +21 −26 Original line number Diff line number Diff line Loading @@ -47,11 +47,11 @@ import com.android.launcher3.model.WidgetItem; import com.android.launcher3.model.WidgetPredictionsRequester; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.model.data.ItemInfo; import com.android.launcher3.model.data.PackageItemInfo; import com.android.launcher3.popup.PopupDataProvider; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.widget.WidgetCell; import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder; import com.android.launcher3.widget.model.WidgetsListBaseEntry; import com.android.launcher3.widget.model.WidgetsListContentEntry; import com.android.launcher3.widget.picker.WidgetsFullSheet; import java.util.ArrayList; Loading @@ -60,10 +60,8 @@ import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; /** An Activity that can host Launcher's widget picker. */ public class WidgetPickerActivity extends BaseActivity { Loading Loading @@ -112,7 +110,8 @@ public class WidgetPickerActivity extends BaseActivity { private WidgetsModel mModel; private LauncherAppState mApp; private WidgetPredictionsRequester mWidgetPredictionsRequester; private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> {}); private final PopupDataProvider mPopupDataProvider = new PopupDataProvider(i -> { }); private int mDesiredWidgetWidth; private int mDesiredWidgetHeight; Loading Loading @@ -287,41 +286,37 @@ public class WidgetPickerActivity extends BaseActivity { }; } /** Updates the model with widgets, applies filters and launches the widgets sheet once * widgets are available */ /** * Updates the model with widgets, applies filters and launches the widgets sheet once * widgets are available */ private void refreshAndBindWidgets() { MODEL_EXECUTOR.execute(() -> { LauncherAppState app = LauncherAppState.getInstance(this); Context context = app.getContext(); mModel.update(app, null); final List<WidgetsListBaseEntry> allWidgets = mModel.getFilteredWidgetsListForPicker(context, mWidgetsFilter); final List<WidgetsListBaseEntry> defaultWidgets = shouldShowDefaultWidgets() ? mModel.getFilteredWidgetsListForPicker(context, mDefaultWidgetsFilter) : List.of(); bindWidgets(allWidgets, defaultWidgets); bindWidgets(mModel.getWidgetsByPackageItem()); // Open sheet once widgets are available, so that it doesn't interrupt the open // animation. openWidgetsSheet(); if (mUiSurface != null) { Map<ComponentKey, WidgetItem> allWidgetItems = allWidgets.stream() .filter(entry -> entry instanceof WidgetsListContentEntry) .flatMap(entry -> entry.mWidgets.stream()) .distinct() .collect(Collectors.toMap( widget -> new ComponentKey(widget.componentName, widget.user), Function.identity() )); mWidgetPredictionsRequester = new WidgetPredictionsRequester(app.getContext(), mUiSurface, allWidgetItems); mUiSurface, mModel.getWidgetsByComponentKey()); mWidgetPredictionsRequester.request(mAddedWidgets, this::bindRecommendedWidgets); } }); } private void bindWidgets(List<WidgetsListBaseEntry> allWidgets, List<WidgetsListBaseEntry> defaultWidgets) { private void bindWidgets(Map<PackageItemInfo, List<WidgetItem>> widgets) { WidgetsListBaseEntriesBuilder builder = new WidgetsListBaseEntriesBuilder( mApp.getContext()); final List<WidgetsListBaseEntry> allWidgets = builder.build(widgets, mWidgetsFilter); final List<WidgetsListBaseEntry> defaultWidgets = shouldShowDefaultWidgets() ? builder.build(widgets, mDefaultWidgetsFilter) : List.of(); MAIN_EXECUTOR.execute(() -> mPopupDataProvider.setAllWidgets(allWidgets, defaultWidgets)); } Loading Loading @@ -392,7 +387,7 @@ public class WidgetPickerActivity extends BaseActivity { mActiveOnBackAnimationCallback.onBackCancelled(); mActiveOnBackAnimationCallback = null; } }; } private boolean shouldShowDefaultWidgets() { // If optional filters such as size filter are present, we display them as default widgets. Loading
src/com/android/launcher3/model/BaseLauncherBinder.java +3 −2 Original line number Diff line number Diff line Loading @@ -52,6 +52,7 @@ import com.android.launcher3.util.LooperExecutor; import com.android.launcher3.util.LooperIdleLock; import com.android.launcher3.util.PackageUserKey; import com.android.launcher3.util.RunnableList; import com.android.launcher3.widget.model.WidgetsListBaseEntriesBuilder; import com.android.launcher3.widget.model.WidgetsListBaseEntry; import java.util.ArrayList; Loading Loading @@ -195,8 +196,8 @@ public class BaseLauncherBinder { if (!WIDGETS_ENABLED) { return; } final List<WidgetsListBaseEntry> widgets = mBgDataModel.widgetsModel.getWidgetsListForPicker(mApp.getContext()); List<WidgetsListBaseEntry> widgets = new WidgetsListBaseEntriesBuilder(mApp.getContext()) .build(mBgDataModel.widgetsModel.getWidgetsByPackageItem()); executeCallbacksTask(c -> c.bindAllWidgets(widgets), mUiExecutor); } Loading
src/com/android/launcher3/model/ModelTaskController.kt +4 −1 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import com.android.launcher3.model.BgDataModel.FixedContainerItems import com.android.launcher3.model.data.ItemInfo import com.android.launcher3.model.data.WorkspaceItemInfo 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 Loading Loading @@ -78,7 +79,9 @@ class ModelTaskController( } fun bindUpdatedWidgets(dataModel: BgDataModel) { val widgets = dataModel.widgetsModel.getWidgetsListForPicker(app.context) val widgets = WidgetsListBaseEntriesBuilder(app.context) .build(dataModel.widgetsModel.widgetsByPackageItem) scheduleCallbackTask { it.bindAllWidgets(widgets) } } Loading
src/com/android/launcher3/model/WidgetsModel.java +5 −47 Original line number Diff line number Diff line Loading @@ -26,7 +26,6 @@ import com.android.launcher3.AppFilter; import com.android.launcher3.InvariantDeviceProfile; import com.android.launcher3.LauncherAppState; import com.android.launcher3.Utilities; import com.android.launcher3.compat.AlphabeticIndexCompat; import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.icons.ComponentWithLabelAndIcon; import com.android.launcher3.icons.IconCache; Loading @@ -39,9 +38,6 @@ import com.android.launcher3.util.Preconditions; import com.android.launcher3.widget.LauncherAppWidgetProviderInfo; import com.android.launcher3.widget.WidgetManagerHelper; import com.android.launcher3.widget.WidgetSections; import com.android.launcher3.widget.model.WidgetsListBaseEntry; import com.android.launcher3.widget.model.WidgetsListContentEntry; import com.android.launcher3.widget.model.WidgetsListHeaderEntry; import com.android.wm.shell.Flags; import java.util.ArrayList; Loading Loading @@ -75,6 +71,9 @@ public class WidgetsModel { * Returns all widgets keyed by their component key. */ public synchronized Map<ComponentKey, WidgetItem> getWidgetsByComponentKey() { if (!WIDGETS_ENABLED) { return Collections.emptyMap(); } return mWidgetsByPackageItem.values().stream() .flatMap(Collection::stream).distinct() .collect(Collectors.toMap( Loading @@ -87,51 +86,10 @@ public class WidgetsModel { * Returns widgets grouped by the package item that they should belong to. */ public synchronized Map<PackageItemInfo, List<WidgetItem>> getWidgetsByPackageItem() { return mWidgetsByPackageItem; } /** * Returns a list of {@link WidgetsListBaseEntry} filtered using given widget item filter. All * {@link WidgetItem}s in a single row are sorted (based on label and user), but the overall * list of {@link WidgetsListBaseEntry}s is not sorted. * * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List) */ public synchronized ArrayList<WidgetsListBaseEntry> getFilteredWidgetsListForPicker( Context context, Predicate<WidgetItem> widgetItemFilter) { if (!WIDGETS_ENABLED) { return new ArrayList<>(); return Collections.emptyMap(); } ArrayList<WidgetsListBaseEntry> result = new ArrayList<>(); AlphabeticIndexCompat indexer = new AlphabeticIndexCompat(context); for (Map.Entry<PackageItemInfo, List<WidgetItem>> entry : mWidgetsByPackageItem.entrySet()) { PackageItemInfo pkgItem = entry.getKey(); List<WidgetItem> widgetItems = entry.getValue() .stream() .filter(widgetItemFilter).toList(); if (!widgetItems.isEmpty()) { String sectionName = (pkgItem.title == null) ? "" : indexer.computeSectionName(pkgItem.title); result.add(WidgetsListHeaderEntry.create(pkgItem, sectionName, widgetItems)); result.add(new WidgetsListContentEntry(pkgItem, sectionName, widgetItems)); } } return result; } /** * Returns a list of {@link WidgetsListBaseEntry}. All {@link WidgetItem} in a single row * are sorted (based on label and user), but the overall list of * {@link WidgetsListBaseEntry}s is not sorted. * * @see com.android.launcher3.widget.picker.WidgetsListAdapter#setWidgets(List) */ public synchronized ArrayList<WidgetsListBaseEntry> getWidgetsListForPicker(Context context) { // return all items return getFilteredWidgetsListForPicker(context, /*widgetItemFilter=*/ item -> true); return mWidgetsByPackageItem; } /** Loading
src/com/android/launcher3/widget/model/WidgetsListBaseEntriesBuilder.kt 0 → 100644 +51 −0 Original line number Diff line number Diff line /* * Copyright (C) 2024 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.launcher3.widget.model import android.content.Context import com.android.launcher3.compat.AlphabeticIndexCompat import com.android.launcher3.model.WidgetItem import com.android.launcher3.model.data.PackageItemInfo import java.util.function.Predicate /** * A helper class that builds the list of [WidgetsListBaseEntry]s used by the UI to display widgets. */ class WidgetsListBaseEntriesBuilder(val context: Context) { /** Builds the widgets list entries in a format understandable by the widget picking UI. */ @JvmOverloads fun build( widgetsByPackageItem: Map<PackageItemInfo, List<WidgetItem>>, widgetFilter: Predicate<WidgetItem> = Predicate<WidgetItem> { true }, ): List<WidgetsListBaseEntry> { val indexer = AlphabeticIndexCompat(context) return buildList { for ((pkgItem, widgetItems) in widgetsByPackageItem.entries) { val filteredWidgetItems = widgetItems.filter { widgetFilter.test(it) } if (filteredWidgetItems.isNotEmpty()) { // Enables fast scroll popup to show right characters in all locales. val sectionName = pkgItem.title?.let { indexer.computeSectionName(it) } ?: "" add(WidgetsListHeaderEntry.create(pkgItem, sectionName, filteredWidgetItems)) add(WidgetsListContentEntry(pkgItem, sectionName, filteredWidgetItems)) } } } } }