Merge changes from topic "categories" into main
* changes:
Initial UI changes for displaying categorized suggestions.
Use full-width two picker in both orientations in tablets
Use derived padding instead of static 300dp for large portrait displays
Align items in the center within each table row within suggestions.
diff --git a/res/layout/widget_recommendations.xml b/res/layout/widget_recommendations.xml
new file mode 100644
index 0000000..89821ac
--- /dev/null
+++ b/res/layout/widget_recommendations.xml
@@ -0,0 +1,58 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<merge xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:launcher="http://schemas.android.com/apk/res-auto">
+
+ <!--
+ Shown when there are more than one pages
+ Note: on page change, using accessibility live region lets user know that the title has changed.
+ -->
+ <TextView
+ android:id="@+id/recommendations_page_title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginBottom="12dp"
+ android:layout_marginTop="16dp"
+ android:accessibilityLiveRegion="polite"
+ android:gravity="center_horizontal"
+ android:lineHeight="20sp"
+ android:textColor="?attr/widgetPickerTitleColor"
+ android:textFontWeight="500"
+ android:textSize="16sp"
+ android:visibility="gone" />
+ <!-- Shown when there are more than one pages -->
+ <com.android.launcher3.pageindicators.PageIndicatorDots
+ android:id="@+id/widget_recommendations_page_indicator"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center_horizontal"
+ android:elevation="1dp"
+ android:visibility="gone" />
+ <!--
+ Note: importantForAccessibility = yes on this view ensures that with talkback, when user
+ swipes right on the last item in current page, they are taken to the next page. And, doing
+ the same on the last page, takes them to the next section e.g. apps list in single pane
+ picker.
+ -->
+ <com.android.launcher3.widget.picker.WidgetRecommendationsView
+ android:id="@+id/widget_recommendations_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_gravity="center"
+ android:background="@drawable/widgets_surface_background"
+ android:importantForAccessibility="yes"
+ launcher:pageIndicator="@+id/widget_recommendations_page_indicator" />
+</merge>
\ No newline at end of file
diff --git a/res/layout/widget_recommendations_table.xml b/res/layout/widget_recommendations_table.xml
new file mode 100644
index 0000000..e3f0562
--- /dev/null
+++ b/res/layout/widget_recommendations_table.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?><!--
+ ~ 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.
+ -->
+<com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:paddingHorizontal="@dimen/widget_recommendations_table_horizontal_padding"
+ android:paddingVertical="@dimen/widget_recommendations_table_vertical_padding" />
diff --git a/res/layout/widgets_full_sheet_paged_view.xml b/res/layout/widgets_full_sheet_paged_view.xml
index 069d4bc..1d37043 100644
--- a/res/layout/widgets_full_sheet_paged_view.xml
+++ b/res/layout/widgets_full_sheet_paged_view.xml
@@ -73,15 +73,18 @@
<include layout="@layout/widgets_search_bar" />
</FrameLayout>
- <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
- android:id="@+id/recommended_widget_table"
+ <!-- Shown when there are recommendations to display -->
+ <LinearLayout
+ android:id="@+id/widget_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
- android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
android:background="@drawable/widgets_surface_background"
- android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
- android:visibility="gone" />
+ android:orientation="vertical"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin"
+ android:visibility="gone">
+ <include layout="@layout/widget_recommendations" />
+ </LinearLayout>
<com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
android:id="@+id/tabs"
diff --git a/res/layout/widgets_full_sheet_recyclerview.xml b/res/layout/widgets_full_sheet_recyclerview.xml
index 25bbad4..dca08ff 100644
--- a/res/layout/widgets_full_sheet_recyclerview.xml
+++ b/res/layout/widgets_full_sheet_recyclerview.xml
@@ -56,14 +56,17 @@
<include layout="@layout/widgets_search_bar" />
</FrameLayout>
- <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
- android:id="@+id/recommended_widget_table"
+ <!-- Shown when there are recommendations to display -->
+ <LinearLayout
+ android:id="@+id/widget_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:background="@drawable/widgets_surface_background"
- android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
- android:visibility="gone" />
+ android:orientation="vertical"
+ android:visibility="gone">
+ <include layout="@layout/widget_recommendations" />
+ </LinearLayout>
</com.android.launcher3.views.StickyHeaderLayout>
</merge>
\ No newline at end of file
diff --git a/res/layout/widgets_two_pane_sheet.xml b/res/layout/widgets_two_pane_sheet.xml
index f692e24..8e45740f 100644
--- a/res/layout/widgets_two_pane_sheet.xml
+++ b/res/layout/widgets_two_pane_sheet.xml
@@ -118,13 +118,16 @@
android:background="@drawable/widgets_surface_background"
android:importantForAccessibility="yes"
android:id="@+id/right_pane">
- <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
- android:id="@+id/recommended_widget_table"
+ <!-- Shown when there are recommendations to display -->
+ <LinearLayout
+ android:id="@+id/widget_recommendations_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
- android:paddingHorizontal=
- "@dimen/widget_list_horizontal_margin_two_pane"
- android:visibility="gone" />
+ android:background="@drawable/widgets_surface_background"
+ android:orientation="vertical"
+ android:visibility="gone">
+ <include layout="@layout/widget_recommendations" />
+ </LinearLayout>
</LinearLayout>
</ScrollView>
</FrameLayout>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index 4d0ac38..dd58cee 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -32,7 +32,4 @@
<!-- Widget picker-->
<dimen name="widget_list_horizontal_margin">49dp</dimen>
<dimen name="widget_list_horizontal_margin_two_pane">24dp</dimen>
-
-<!-- Bottom sheet-->
- <dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
</resources>
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 2b0382d..3c79588 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -38,9 +38,6 @@
<!-- Widget picker-->
<dimen name="widget_list_horizontal_margin">30dp</dimen>
-<!-- Bottom sheet-->
- <dimen name="bottom_sheet_extra_top_padding">300dp</dimen>
-
<!-- Folder spaces -->
<dimen name="folder_footer_horiz_padding">24dp</dimen>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index c7190b6..c101762 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -182,9 +182,8 @@
<dimen name="widget_apps_tabs_vertical_padding">6dp</dimen>
<dimen name="widget_picker_landscape_tablet_left_right_margin">117dp</dimen>
<dimen name="widget_picker_two_panels_left_right_margin">0dp</dimen>
-
- <dimen name="recommended_widgets_table_vertical_padding">8dp</dimen>
-
+ <dimen name="widget_recommendations_table_vertical_padding">8dp</dimen>
+ <dimen name="widget_recommendations_table_horizontal_padding">16dp</dimen>
<!-- Bottom margin for the search and recommended widgets container without work profile -->
<dimen name="search_and_recommended_widgets_container_bottom_margin">16dp</dimen>
<!-- Bottom margin for the search and recommended widgets container with work profile -->
@@ -454,7 +453,6 @@
<dimen name="padded_rounded_button_padding">8dp</dimen>
<!-- Bottom sheet related parameters -->
- <dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
<dimen name="bottom_sheet_handle_area_height">36dp</dimen>
<dimen name="bottom_sheet_handle_width">32dp</dimen>
<dimen name="bottom_sheet_handle_height">4dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 31c098c..379cdda 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -71,7 +71,7 @@
<!-- Widget suggestions header title in the full widgets picker for large screen devices
in landscape mode. [CHAR_LIMIT=50] -->
<string name="suggested_widgets_header_title">Suggestions</string>
- <string name="productivity_widget_recommendation_category_label">Boost your day</string>
+ <string name="productivity_widget_recommendation_category_label">Your Daily Essentials</string>
<string name="news_widget_recommendation_category_label">News For You</string>
<string name="social_and_entertainment_widget_recommendation_category_label">Your Chill Zone</string>
<string name="fitness_widget_recommendation_category_label">Reach Your Fitness Goals</string>
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 563dfe2..4afa8e0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -416,15 +416,18 @@
gridVisualizationPaddingY = res.getDimensionPixelSize(
R.dimen.grid_visualization_vertical_cell_spacing);
- // Tablet portrait mode uses a single pane widget picker and extra padding may be applied on
- // top to avoid making it look too elongated.
- final boolean applyExtraTopPadding = isTablet
- && !isLandscape
- && (aspectRatio > MIN_ASPECT_RATIO_FOR_EXTRA_TOP_PADDING);
- bottomSheetTopPadding = mInsets.top // statusbar height
- + (applyExtraTopPadding ? res.getDimensionPixelSize(
- R.dimen.bottom_sheet_extra_top_padding) : 0)
- + (isTablet ? 0 : edgeMarginPx); // phones need edgeMarginPx additional padding
+ {
+ // In large screens, in portrait mode, a bottom sheet can appear too elongated, so, we
+ // apply additional padding.
+ final boolean applyExtraTopPadding = isTablet
+ && !isLandscape
+ && (aspectRatio > MIN_ASPECT_RATIO_FOR_EXTRA_TOP_PADDING);
+ final int derivedTopPadding = heightPx / 6;
+ bottomSheetTopPadding = mInsets.top // statusbar height
+ + (applyExtraTopPadding ? derivedTopPadding : 0)
+ + (isTablet ? 0 : edgeMarginPx); // phones need edgeMarginPx additional padding
+ }
+
bottomSheetOpenDuration = res.getInteger(R.integer.config_bottomSheetOpenDuration);
bottomSheetCloseDuration = res.getInteger(R.integer.config_bottomSheetCloseDuration);
if (isTablet) {
diff --git a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
index df369c6..1b5abaa 100644
--- a/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
+++ b/src/com/android/launcher3/pageindicators/PageIndicatorDots.java
@@ -367,7 +367,7 @@
mNumPages = numMarkers;
// If the last page gets removed we want to go to the previous page.
- if (mNumPages == mActivePage) {
+ if (mNumPages > 0 && mNumPages == mActivePage) {
mActivePage--;
CURRENT_POSITION.set(this, (float) mActivePage);
}
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index f1d837c..fb463f7 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -222,6 +222,7 @@
}
/** Returns the recommended widgets mapped by their category. */
+ @NonNull
public Map<WidgetRecommendationCategory, List<WidgetItem>> getCategorizedRecommendedWidgets() {
Map<ComponentKey, WidgetItem> allWidgetItems = mAllWidgets.stream()
.filter(entry -> entry instanceof WidgetsListContentEntry)
@@ -232,7 +233,8 @@
Function.identity()
));
return mRecommendedWidgets.stream()
- .filter(itemInfo -> itemInfo instanceof PendingAddWidgetInfo)
+ .filter(itemInfo -> itemInfo instanceof PendingAddWidgetInfo
+ && ((PendingAddWidgetInfo) itemInfo).recommendationCategory != null)
.collect(Collectors.groupingBy(
it -> ((PendingAddWidgetInfo) it).recommendationCategory,
Collectors.collectingAndThen(
diff --git a/src/com/android/launcher3/widget/BaseWidgetSheet.java b/src/com/android/launcher3/widget/BaseWidgetSheet.java
index 145ad80..54ce973 100644
--- a/src/com/android/launcher3/widget/BaseWidgetSheet.java
+++ b/src/com/android/launcher3/widget/BaseWidgetSheet.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget;
import static com.android.app.animation.Interpolators.EMPHASIZED;
+import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.LauncherPrefs.WIDGETS_EDUCATION_TIP_SEEN;
@@ -62,8 +63,10 @@
protected final Rect mInsets = new Rect();
- @Px protected int mContentHorizontalMargin;
- @Px protected int mWidgetCellHorizontalPadding;
+ @Px
+ protected int mContentHorizontalMargin;
+ @Px
+ protected int mWidgetCellHorizontalPadding;
protected int mNavBarScrimHeight;
private final Paint mNavBarScrimPaint;
@@ -196,7 +199,7 @@
DeviceProfile deviceProfile = mActivityContext.getDeviceProfile();
int widthUsed;
if (deviceProfile.isTablet) {
- widthUsed = Math.max(2 * getTabletMargin(deviceProfile),
+ widthUsed = Math.max(2 * getTabletHorizontalMargin(deviceProfile),
2 * (mInsets.left + mInsets.right));
} else if (mInsets.bottom > 0) {
widthUsed = mInsets.left + mInsets.right;
@@ -212,7 +215,11 @@
MeasureSpec.getSize(heightMeasureSpec));
}
- private int getTabletMargin(DeviceProfile deviceProfile) {
+ private int getTabletHorizontalMargin(DeviceProfile deviceProfile) {
+ // All bottom-sheets showing widgets will be full-width across all devices.
+ if (enableCategorizedWidgetSuggestions()) {
+ return 0;
+ }
if (deviceProfile.isLandscape && !deviceProfile.isTwoPanels) {
return getResources().getDimensionPixelSize(
R.dimen.widget_picker_landscape_tablet_left_right_margin);
diff --git a/src/com/android/launcher3/widget/WidgetCell.java b/src/com/android/launcher3/widget/WidgetCell.java
index c75f9d1..94c630a 100644
--- a/src/com/android/launcher3/widget/WidgetCell.java
+++ b/src/com/android/launcher3/widget/WidgetCell.java
@@ -367,6 +367,16 @@
}
}
+ /**
+ * Shows or hides the long description displayed below each widget.
+ *
+ * @param show a flag that shows the long description of the widget if {@code true}, hides it if
+ * {@code false}.
+ */
+ public void showDescription(boolean show) {
+ mWidgetDescription.setVisibility(show ? VISIBLE : GONE);
+ }
+
@Override
public boolean onTouchEvent(MotionEvent ev) {
super.onTouchEvent(ev);
diff --git a/src/com/android/launcher3/widget/WidgetsBottomSheet.java b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
index c347939..ceb0072 100644
--- a/src/com/android/launcher3/widget/WidgetsBottomSheet.java
+++ b/src/com/android/launcher3/widget/WidgetsBottomSheet.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget;
+import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_BOTTOM_WIDGETS_TRAY;
import android.content.Context;
@@ -187,7 +188,13 @@
mWidgetCellHorizontalPadding)
.forEach(row -> {
TableRow tableRow = new TableRow(getContext());
- tableRow.setGravity(Gravity.TOP);
+ if (enableCategorizedWidgetSuggestions()) {
+ // Vertically center align items, so that even if they don't fill bounds,
+ // they can look organized when placed together in a row.
+ tableRow.setGravity(Gravity.CENTER_VERTICAL);
+ } else {
+ tableRow.setGravity(Gravity.TOP);
+ }
row.forEach(widgetItem -> {
WidgetCell widget = addItemCell(tableRow);
widget.applyFromCellItem(widgetItem);
diff --git a/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
new file mode 100644
index 0000000..738d74e
--- /dev/null
+++ b/src/com/android/launcher3/widget/picker/WidgetRecommendationsView.java
@@ -0,0 +1,258 @@
+/*
+ * 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.picker;
+
+import static com.android.launcher3.widget.util.WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.annotation.Px;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.PagedView;
+import com.android.launcher3.R;
+import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.pageindicators.PageIndicatorDots;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+/**
+ * A {@link PagedView} that displays widget recommendations in categories with dots as paged
+ * indicators.
+ */
+public final class WidgetRecommendationsView extends PagedView<PageIndicatorDots> {
+ private @Px float mAvailableHeight = Float.MAX_VALUE;
+
+ private static final int MAX_CATEGORIES = 3;
+ private TextView mRecommendationPageTitle;
+ private final List<String> mCategoryTitles = new ArrayList<>();
+
+ @Nullable
+ private OnLongClickListener mWidgetCellOnLongClickListener;
+ @Nullable
+ private OnClickListener mWidgetCellOnClickListener;
+
+ public WidgetRecommendationsView(Context context) {
+ this(context, /* attrs= */ null);
+ }
+
+ public WidgetRecommendationsView(Context context, AttributeSet attrs) {
+ this(context, attrs, /* defStyleAttr= */ 0);
+ }
+
+ public WidgetRecommendationsView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ @Override
+ public void initParentViews(View parent) {
+ super.initParentViews(parent);
+ mRecommendationPageTitle = parent.findViewById(R.id.recommendations_page_title);
+ }
+
+ /** Sets a {@link android.view.View.OnLongClickListener} for all widget cells in this table. */
+ public void setWidgetCellLongClickListener(OnLongClickListener onLongClickListener) {
+ mWidgetCellOnLongClickListener = onLongClickListener;
+ }
+
+ /** Sets a {@link android.view.View.OnClickListener} for all widget cells in this table. */
+ public void setWidgetCellOnClickListener(OnClickListener widgetCellOnClickListener) {
+ mWidgetCellOnClickListener = widgetCellOnClickListener;
+ }
+
+ /**
+ * Displays all the provided recommendations in a single table if they fit.
+ *
+ * @param recommendedWidgets list of widgets to be displayed in recommendation section.
+ * @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 {@code false} if no recommendations could fit in the available space.
+ */
+ public boolean setRecommendations(
+ List<WidgetItem> recommendedWidgets, final @Px float availableHeight,
+ final @Px int availableWidth, final @Px int cellPadding) {
+ this.mAvailableHeight = availableHeight;
+ removeAllViews();
+
+ maybeDisplayInTable(recommendedWidgets, availableWidth, cellPadding);
+ updateTitleAndIndicator();
+ return getChildCount() > 0;
+ }
+
+ /**
+ * Displays the recommendations grouped by categories as pages.
+ * <p>In case of a single category, no title is displayed for it.</p>
+ *
+ * @param recommendations a map of widget items per recommendation category
+ * @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 {@code false} if no recommendations could fit in the available space.
+ */
+ public boolean setRecommendations(
+ Map<WidgetRecommendationCategory, List<WidgetItem>> recommendations,
+ final @Px float availableHeight, final @Px int availableWidth,
+ final @Px int cellPadding) {
+ this.mAvailableHeight = availableHeight;
+ Context context = getContext();
+ removeAllViews();
+
+ int displayedCategories = 0;
+
+ // 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.
+ if (maybeDisplayInTable(entry.getValue(), availableWidth, cellPadding)) {
+ mCategoryTitles.add(
+ context.getResources().getString(entry.getKey().categoryTitleRes));
+ displayedCategories++;
+ }
+
+ if (displayedCategories == MAX_CATEGORIES) {
+ break;
+ }
+ }
+
+ updateTitleAndIndicator();
+ return getChildCount() > 0;
+ }
+
+ /** Displays the page title and paging indicator if there are multiple pages. */
+ private void updateTitleAndIndicator() {
+ int titleAndIndicatorVisibility = getPageCount() > 1 ? View.VISIBLE : View.GONE;
+ mRecommendationPageTitle.setVisibility(titleAndIndicatorVisibility);
+ mPageIndicator.setVisibility(titleAndIndicatorVisibility);
+ }
+
+ @Override
+ protected void notifyPageSwitchListener(int prevPage) {
+ if (getPageCount() > 1) {
+ // Since the title is outside the paging scroll, we update the title on page switch.
+ mRecommendationPageTitle.setText(mCategoryTitles.get(getNextPage()));
+ super.notifyPageSwitchListener(prevPage);
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected boolean canScroll(float absVScroll, float absHScroll) {
+ // Allow only horizontal scroll.
+ return (absHScroll > absVScroll) && super.canScroll(absVScroll, absHScroll);
+ }
+
+ @Override
+ protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+ super.onScrollChanged(l, t, oldl, oldt);
+ mPageIndicator.setScroll(l, mMaxScroll);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ boolean hasMultiplePages = getChildCount() > 0;
+
+ if (hasMultiplePages) {
+ int finalWidth = MeasureSpec.getSize(widthMeasureSpec);
+ int desiredHeight = 0;
+
+ for (int i = 0; i < getChildCount(); i++) {
+ View child = getChildAt(i);
+ measureChild(child, widthMeasureSpec, heightMeasureSpec);
+ if (mAvailableHeight == Float.MAX_VALUE) {
+ // When we are not limited by height, use currentPage's height. This is the case
+ // when the paged layout is placed in a scrollable container. We cannot use
+ // height
+ // of tallest child in such case, as it will display a scrollbar even for
+ // smaller
+ // pages that don't have more content.
+ if (i == mCurrentPage) {
+ int parentHeight = MeasureSpec.getSize(heightMeasureSpec);
+ desiredHeight = Math.max(parentHeight, child.getMeasuredHeight());
+ }
+ } else {
+ // Use height of tallest child when we are limited to a certain height.
+ desiredHeight = Math.max(desiredHeight, child.getMeasuredHeight());
+ }
+ }
+
+ int finalHeight = resolveSizeAndState(desiredHeight, heightMeasureSpec, 0);
+ setMeasuredDimension(finalWidth, finalHeight);
+ } else {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ /**
+ * Groups the provided recommendations into rows and displays them in a table if at least one
+ * fits.
+ * <p>Returns false if none of the recommendations could fit.</p>
+ */
+ private boolean maybeDisplayInTable(List<WidgetItem> recommendedWidgets,
+ final @Px int availableWidth, final @Px int cellPadding) {
+ Context context = getContext();
+ DeviceProfile deviceProfile = Launcher.getLauncher(context).getDeviceProfile();
+ LayoutInflater inflater = LayoutInflater.from(context);
+
+ List<ArrayList<WidgetItem>> rows = groupWidgetItemsUsingRowPxWithoutReordering(
+ recommendedWidgets,
+ context,
+ deviceProfile,
+ availableWidth,
+ cellPadding);
+
+ WidgetsRecommendationTableLayout recommendationsTable =
+ (WidgetsRecommendationTableLayout) inflater.inflate(
+ R.layout.widget_recommendations_table,
+ /* root=*/ this,
+ /* attachToRoot=*/ false);
+ recommendationsTable.setWidgetCellOnClickListener(mWidgetCellOnClickListener);
+ recommendationsTable.setWidgetCellLongClickListener(mWidgetCellOnLongClickListener);
+
+ boolean displayedAtLeastOne = recommendationsTable.setRecommendedWidgets(rows,
+ mAvailableHeight);
+ if (displayedAtLeastOne) {
+ addView(recommendationsTable);
+ }
+
+ return displayedAtLeastOne;
+ }
+
+ /** Returns location of a widget cell for displaying the "touch and hold" education tip. */
+ public View getViewForEducationTip() {
+ if (getChildCount() > 0) {
+ // first page (a table layout) -> first item (a widget cell).
+ return ((ViewGroup) getChildAt(0)).getChildAt(0);
+ }
+ return null;
+ }
+}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index 4e704fd..237078e 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -17,6 +17,7 @@
import static android.view.View.MeasureSpec.makeMeasureSpec;
+import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.LauncherPrefs.WIDGETS_EDUCATION_DIALOG_SEEN;
@@ -45,6 +46,7 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
+import android.widget.LinearLayout;
import android.widget.TextView;
import android.window.BackEvent;
@@ -65,7 +67,6 @@
import com.android.launcher3.anim.PendingAnimation;
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.UserManagerState;
-import com.android.launcher3.model.WidgetItem;
import com.android.launcher3.pm.UserCache;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
@@ -76,7 +77,6 @@
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
import com.android.launcher3.widget.picker.search.SearchModeListener;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
-import com.android.launcher3.widget.util.WidgetsTableUtils;
import com.android.launcher3.workprofile.PersonalWorkPagedView;
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
@@ -162,12 +162,13 @@
@Nullable PersonalWorkPagedView mViewPager;
private boolean mIsInSearchMode;
private boolean mIsNoWidgetsViewNeeded;
- @Px private int mMaxSpanPerRow;
+ @Px protected int mMaxSpanPerRow;
protected DeviceProfile mDeviceProfile;
protected TextView mNoWidgetsView;
protected StickyHeaderLayout mSearchScrollView;
- protected WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
+ protected WidgetRecommendationsView mWidgetRecommendationsView;
+ protected LinearLayout mWidgetRecommendationsContainer;
protected View mTabBar;
protected View mSearchBarContainer;
protected WidgetsSearchBar mSearchBar;
@@ -221,9 +222,14 @@
setupViews();
- mRecommendedWidgetsTable = mSearchScrollView.findViewById(R.id.recommended_widget_table);
- mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
- mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+ mWidgetRecommendationsContainer = mSearchScrollView.findViewById(
+ R.id.widget_recommendations_container);
+ mWidgetRecommendationsView = mSearchScrollView.findViewById(
+ R.id.widget_recommendations_view);
+ mWidgetRecommendationsView.initParentViews(mWidgetRecommendationsContainer);
+ mWidgetRecommendationsView.setWidgetCellLongClickListener(this);
+ mWidgetRecommendationsView.setWidgetCellOnClickListener(this);
+
mHeaderTitle = mSearchScrollView.findViewById(R.id.title);
onRecommendedWidgetsBound();
@@ -551,7 +557,7 @@
protected void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
mIsInSearchMode = isInSearchMode;
if (isInSearchMode) {
- mRecommendedWidgetsTable.setVisibility(GONE);
+ mWidgetRecommendationsContainer.setVisibility(GONE);
if (mHasWorkProfile) {
mViewPager.setVisibility(GONE);
mTabBar.setVisibility(GONE);
@@ -580,40 +586,44 @@
if (mIsInSearchMode) {
return;
}
- List<WidgetItem> recommendedWidgets =
- mActivityContext.getPopupDataProvider().getRecommendedWidgets();
- mHasRecommendedWidgets = recommendedWidgets.size() > 0;
- if (mHasRecommendedWidgets) {
- float noWidgetsViewHeight = 0;
- if (mIsNoWidgetsViewNeeded) {
- // Make sure recommended section leaves enough space for noWidgetsView.
- Rect noWidgetsViewTextBounds = new Rect();
- mNoWidgetsView.getPaint()
- .getTextBounds(mNoWidgetsView.getText().toString(), /* start= */ 0,
- mNoWidgetsView.getText().length(), noWidgetsViewTextBounds);
- noWidgetsViewHeight = noWidgetsViewTextBounds.height();
- }
- if (!isTwoPane()) {
- doMeasure(
- makeMeasureSpec(mActivityContext.getDeviceProfile().availableWidthPx,
- MeasureSpec.EXACTLY),
- makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
- MeasureSpec.EXACTLY));
- }
- float maxTableHeight = getMaxTableHeight(noWidgetsViewHeight);
- List<ArrayList<WidgetItem>> recommendedWidgetsInTable =
- WidgetsTableUtils.groupWidgetItemsUsingRowPxWithoutReordering(
- recommendedWidgets,
- mActivityContext,
- mActivityContext.getDeviceProfile(),
- mMaxSpanPerRow,
- mWidgetCellHorizontalPadding);
- mRecommendedWidgetsTable.setRecommendedWidgets(
- recommendedWidgetsInTable, maxTableHeight);
+ if (enableCategorizedWidgetSuggestions()) {
+ mHasRecommendedWidgets = mWidgetRecommendationsView.setRecommendations(
+ mActivityContext.getPopupDataProvider().getCategorizedRecommendedWidgets(),
+ /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
+ /* availableWidth= */ mMaxSpanPerRow,
+ /* cellPadding= */ mWidgetCellHorizontalPadding
+ );
} else {
- mRecommendedWidgetsTable.setVisibility(GONE);
+ mHasRecommendedWidgets = mWidgetRecommendationsView.setRecommendations(
+ mActivityContext.getPopupDataProvider().getRecommendedWidgets(),
+ /* availableHeight= */ getMaxAvailableHeightForRecommendations(),
+ /* availableWidth= */ mMaxSpanPerRow,
+ /* cellPadding= */ mWidgetCellHorizontalPadding
+ );
}
+ mWidgetRecommendationsContainer.setVisibility(mHasRecommendedWidgets ? VISIBLE : GONE);
+ }
+
+ @Px
+ private float getMaxAvailableHeightForRecommendations() {
+ float noWidgetsViewHeight = 0;
+ if (mIsNoWidgetsViewNeeded) {
+ // Make sure recommended section leaves enough space for noWidgetsView.
+ Rect noWidgetsViewTextBounds = new Rect();
+ mNoWidgetsView.getPaint()
+ .getTextBounds(mNoWidgetsView.getText().toString(), /* start= */ 0,
+ mNoWidgetsView.getText().length(), noWidgetsViewTextBounds);
+ noWidgetsViewHeight = noWidgetsViewTextBounds.height();
+ }
+ if (!isTwoPane()) {
+ doMeasure(
+ makeMeasureSpec(mActivityContext.getDeviceProfile().availableWidthPx,
+ MeasureSpec.EXACTLY),
+ makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
+ MeasureSpec.EXACTLY));
+ }
+ return getMaxTableHeight(noWidgetsViewHeight);
}
/** b/209579563: "Widgets" header should be focused first. */
@@ -622,7 +632,8 @@
return mHeaderTitle;
}
- protected float getMaxTableHeight(float noWidgetsViewHeight) {
+ @Px
+ protected float getMaxTableHeight(@Px float noWidgetsViewHeight) {
return (mContent.getMeasuredHeight()
- mTabsHeight - getHeaderViewHeight()
- noWidgetsViewHeight)
@@ -699,7 +710,9 @@
private static int getWidgetSheetId(BaseActivity activity) {
boolean isTwoPane = (activity.getDeviceProfile().isTablet
- && activity.getDeviceProfile().isLandscape
+ // Enables two pane picker for tablets in all orientations when the
+ // enableCategorizedWidgetSuggestions flag is on.
+ && (activity.getDeviceProfile().isLandscape || enableCategorizedWidgetSuggestions())
&& !activity.getDeviceProfile().isTwoPanels)
// Enables two pane picker for unfolded foldables if the flag is on.
|| (activity.getDeviceProfile().isTwoPanels && enableUnfoldedTwoPanePicker());
@@ -813,28 +826,40 @@
public void onDeviceProfileChanged(DeviceProfile dp) {
super.onDeviceProfileChanged(dp);
- if (mDeviceProfile.isLandscape != dp.isLandscape && dp.isTablet && !dp.isTwoPanels) {
- handleClose(false);
- show(BaseActivity.fromContext(getContext()), false);
- } else if (!isTwoPane()) {
- reset();
- resetExpandedHeaders();
- }
-
- // When folding/unfolding the foldables, we need to switch between the regular widget picker
- // and the two pane picker, so we rebuild the picker with the correct layout.
- if (mDeviceProfile.isTwoPanels != dp.isTwoPanels && enableUnfoldedTwoPanePicker()) {
+ if (shouldRecreateLayout(/*oldDp=*/ mDeviceProfile, /*newDp=*/ dp)) {
SparseArray<Parcelable> widgetsState = new SparseArray<>();
saveHierarchyState(widgetsState);
handleClose(false);
WidgetsFullSheet sheet = show(BaseActivity.fromContext(getContext()), false);
sheet.restoreHierarchyState(widgetsState);
sheet.restorePreviousAdapterHolderType(getCurrentAdapterHolderType());
+ } else if (!isTwoPane()) {
+ reset();
+ resetExpandedHeaders();
}
mDeviceProfile = dp;
}
+ /**
+ * Indicates if layout should be re-created on device profile change - so that a different
+ * layout can be displayed.
+ */
+ private static boolean shouldRecreateLayout(DeviceProfile oldDp, DeviceProfile newDp) {
+ // When folding/unfolding the foldables, we need to switch between the regular widget picker
+ // and the two pane picker, so we rebuild the picker with the correct layout.
+ boolean isFoldUnFold =
+ oldDp.isTwoPanels != newDp.isTwoPanels && enableUnfoldedTwoPanePicker();
+ // In tablets, on orientation change we switch between single and two pane picker unless the
+ // categorized suggestions flag was on. With the categorized suggestions feature, we use a
+ // two pane picker across all orientations.
+ boolean useDifferentLayoutOnOrientationChange =
+ (!enableCategorizedWidgetSuggestions() && (newDp.isTablet && !newDp.isTwoPanels
+ && oldDp.isLandscape != newDp.isLandscape));
+
+ return isFoldUnFold || useDifferentLayoutOnOrientationChange;
+ }
+
@Override
public void onBackInvoked() {
if (mIsInSearchMode) {
@@ -855,9 +880,8 @@
}
@Nullable private View getViewToShowEducationTip() {
- if (mRecommendedWidgetsTable.getVisibility() == VISIBLE
- && mRecommendedWidgetsTable.getChildCount() > 0) {
- return ((ViewGroup) mRecommendedWidgetsTable.getChildAt(0)).getChildAt(0);
+ if (mWidgetRecommendationsContainer.getVisibility() == VISIBLE) {
+ return mWidgetRecommendationsView.getViewForEducationTip();
}
AdapterHolder adapterHolder = mAdapters.get(mIsInSearchMode
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index c7d2aa3..f10ab48 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -15,6 +15,8 @@
*/
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
+
import android.content.Context;
import android.graphics.Bitmap;
import android.util.Log;
@@ -147,7 +149,13 @@
tableRow = (TableRow) table.getChildAt(i);
} else {
tableRow = new TableRow(table.getContext());
- tableRow.setGravity(Gravity.TOP);
+ if (enableCategorizedWidgetSuggestions()) {
+ // Vertically center align items, so that even if they don't fill bounds, they
+ // can look organized when placed together in a row.
+ tableRow.setGravity(Gravity.CENTER_VERTICAL);
+ } else {
+ tableRow.setGravity(Gravity.TOP);
+ }
table.addView(tableRow);
}
if (tableRow.getChildCount() > widgetItems.size()) {
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
index 06cc65e..ce1f4e0 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRecommendationTableLayout.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.LauncherSettings.Favorites.CONTAINER_WIDGETS_PREDICTION;
import android.content.Context;
@@ -61,7 +62,7 @@
super(context, attrs);
// There are 1 row for title, 1 row for dimension and 2 rows for description.
mWidgetsRecommendationTableVerticalPadding = 2 * getResources()
- .getDimensionPixelSize(R.dimen.recommended_widgets_table_vertical_padding);
+ .getDimensionPixelSize(R.dimen.widget_recommendations_table_vertical_padding);
mWidgetCellVerticalPadding = 2 * getResources()
.getDimensionPixelSize(R.dimen.widget_cell_vertical_padding);
mWidgetCellTextViewsHeight = 4 * getResources().getDimension(R.dimen.widget_cell_font_size);
@@ -84,17 +85,20 @@
* <p>If the content can't fit {@code recommendationTableMaxHeight}, this view will remove a
* last row from the {@code recommendedWidgets} until it fits or only one row left. If the only
* row still doesn't fit, we scale down the preview image.
+ *
+ * <p>Returns {@code false} if none of the widgets could fit</p>
*/
- public void setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
+ public boolean setRecommendedWidgets(List<ArrayList<WidgetItem>> recommendedWidgets,
float recommendationTableMaxHeight) {
mRecommendationTableMaxHeight = recommendationTableMaxHeight;
RecommendationTableData data = fitRecommendedWidgetsToTableSpace(/* previewScale= */ 1f,
recommendedWidgets);
bindData(data);
+ return !data.mRecommendationTable.isEmpty();
}
private void bindData(RecommendationTableData data) {
- if (data.mRecommendationTable.size() == 0) {
+ if (data.mRecommendationTable.isEmpty()) {
setVisibility(GONE);
return;
}
@@ -104,12 +108,20 @@
for (int i = 0; i < data.mRecommendationTable.size(); i++) {
List<WidgetItem> widgetItems = data.mRecommendationTable.get(i);
TableRow tableRow = new TableRow(getContext());
- tableRow.setGravity(Gravity.TOP);
-
+ if (enableCategorizedWidgetSuggestions()) {
+ // Vertically center align items, so that even if they don't fill bounds, they can
+ // look organized when placed together in a row.
+ tableRow.setGravity(Gravity.CENTER_VERTICAL);
+ } else {
+ tableRow.setGravity(Gravity.TOP);
+ }
for (WidgetItem widgetItem : widgetItems) {
WidgetCell widgetCell = addItemCell(tableRow);
widgetCell.applyFromCellItem(widgetItem, data.mPreviewScale);
widgetCell.showBadge();
+ if (enableCategorizedWidgetSuggestions()) {
+ widgetCell.showDescription(false);
+ }
}
addView(tableRow);
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
index 744c45b..165b2fe 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsTwoPaneSheet.java
@@ -15,6 +15,7 @@
*/
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.Flags.enableCategorizedWidgetSuggestions;
import static com.android.launcher3.Flags.enableUnfoldedTwoPanePicker;
import android.content.Context;
@@ -109,9 +110,15 @@
mWidgetsListTableViewHolderBinder =
new WidgetsListTableViewHolderBinder(mActivityContext, layoutInflater, this, this);
- mRecommendedWidgetsTable = mContent.findViewById(R.id.recommended_widget_table);
- mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
- mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+
+ mWidgetRecommendationsContainer = mContent.findViewById(
+ R.id.widget_recommendations_container);
+ mWidgetRecommendationsView = mContent.findViewById(
+ R.id.widget_recommendations_view);
+ mWidgetRecommendationsView.initParentViews(mWidgetRecommendationsContainer);
+ mWidgetRecommendationsView.setWidgetCellLongClickListener(this);
+ mWidgetRecommendationsView.setWidgetCellOnClickListener(this);
+
mHeaderTitle = mContent.findViewById(R.id.title);
mRightPane = mContent.findViewById(R.id.right_pane);
mRightPane.setOutlineProvider(mViewOutlineProviderRightPane);
@@ -213,7 +220,7 @@
mSuggestedWidgetsHeader.setExpanded(true);
resetExpandedHeaders();
mRightPane.removeAllViews();
- mRightPane.addView(mRecommendedWidgetsTable);
+ mRightPane.addView(mWidgetRecommendationsContainer);
mRightPaneScrollView.setScrollY(0);
mRightPane.setAccessibilityPaneTitle(suggestionsRightPaneTitle);
mSuggestedWidgetsPackageUserKey = PackageUserKey.fromPackageItemInfo(packageItemInfo);
@@ -224,7 +231,8 @@
}
@Override
- protected float getMaxTableHeight(float noWidgetsViewHeight) {
+ @Px
+ protected float getMaxTableHeight(@Px float noWidgetsViewHeight) {
return Float.MAX_VALUE;
}
@@ -308,15 +316,25 @@
if (mSuggestedWidgetsHeader != null) {
mSuggestedWidgetsHeader.setExpanded(false);
}
+
+ WidgetsListContentEntry contentEntryToBind;
+ if (enableCategorizedWidgetSuggestions()) {
+ // Setting max span size enables row to understand how to fit more than one item
+ // in a row.
+ contentEntryToBind = contentEntry.withMaxSpanSize(mMaxSpanPerRow);
+ } else {
+ contentEntryToBind = contentEntry;
+ }
+
WidgetsRowViewHolder widgetsRowViewHolder =
mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
- contentEntry,
+ contentEntryToBind,
ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
Collections.EMPTY_LIST);
widgetsRowViewHolder.mDataCallback = data -> {
mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
- contentEntry,
+ contentEntryToBind,
ViewHolderBinder.POSITION_FIRST | ViewHolderBinder.POSITION_LAST,
Collections.singletonList(data));
};