Merge "Fix flicker issue in widget picker with app activity in background" into main
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);
+ }
}
}
}