Merge "Implement two pane widget picker" into tm-qpr-dev
diff --git a/res/drawable/widget_suggestions.xml b/res/drawable/widget_suggestions.xml
new file mode 100644
index 0000000..b090a68
--- /dev/null
+++ b/res/drawable/widget_suggestions.xml
@@ -0,0 +1,27 @@
+<!--
+Copyright (C) 2023 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.
+-->
+<vector xmlns:android="http://schemas.android.com/apk/res/android"
+ android:width="24dp"
+ android:height="24dp"
+ android:viewportWidth="24"
+ android:viewportHeight="24"
+ android:tint="@color/widget_picker_background_selected"
+ android:gravity="center"
+ >
+ <path
+ android:fillColor="@android:color/white"
+ android:pathData="M16.65,13 L11,7.35 16.65,1.7 22.3,7.35ZM3,11V3H11V11ZM13,21V13H21V21ZM3,21V13H11V21ZM5,9H9V5H5ZM16.675,10.2 L19.5,7.375 16.675,4.55 13.85,7.375ZM15,19H19V15H15ZM5,19H9V15H5ZM9,9ZM13.85,7.375ZM9,15ZM15,15Z"/>
+</vector>
diff --git a/res/drawable/widget_suggestions_icon.xml b/res/drawable/widget_suggestions_icon.xml
new file mode 100644
index 0000000..919b5e4
--- /dev/null
+++ b/res/drawable/widget_suggestions_icon.xml
@@ -0,0 +1,30 @@
+<!--
+Copyright (C) 2023 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.
+-->
+<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
+ <item>
+ <shape android:shape="oval">
+ <size
+ android:width="48dp"
+ android:height="48dp" />
+ <solid android:color="@color/surface"/>
+ </shape>
+ </item>
+ <item
+ android:width="24dp"
+ android:height="24dp"
+ android:drawable="@drawable/widget_suggestions"
+ android:gravity="center" />
+</layer-list>
diff --git a/res/layout/widgets_full_sheet_large_screen.xml b/res/layout/widgets_full_sheet_large_screen.xml
new file mode 100644
index 0000000..3dbe6f5
--- /dev/null
+++ b/res/layout/widgets_full_sheet_large_screen.xml
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2022 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.WidgetsFullSheet xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical"
+ android:theme="?attr/widgetsTheme">
+
+ <androidx.constraintlayout.widget.ConstraintLayout
+ android:id="@+id/container"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:background="@drawable/bg_widgets_full_sheet"
+ android:focusable="true"
+ android:importantForAccessibility="no">
+
+ <FrameLayout
+ android:id="@+id/recycler_view_container"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toStartOf="@id/right_pane"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/title"
+ app:layout_constraintWidth_percent="0.33">
+
+ <TextView
+ android:id="@+id/no_widgets_text"
+ style="@style/PrimaryHeadline"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:gravity="center"
+ android:textSize="18sp"
+ android:visibility="gone"
+ tools:text="No widgets available" />
+
+ <TextView
+ android:id="@+id/fast_scroller_popup"
+ style="@style/FastScrollerPopup"
+ android:layout_marginEnd="@dimen/fastscroll_popup_margin" />
+
+ <!-- Fast scroller popup -->
+ <com.android.launcher3.views.RecyclerViewFastScroller
+ android:id="@+id/fast_scroller"
+ android:layout_width="@dimen/fastscroll_width"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/fastscroll_end_margin" />
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/search_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:clipToPadding="false"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ android:visibility="gone" />
+ </FrameLayout>
+
+ <ScrollView
+ android:id="@+id/right_pane"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toEndOf="@id/recycler_view_container"
+ app:layout_constraintTop_toBottomOf="@id/title"
+ app:layout_constraintBottom_toBottomOf="parent"
+ android:paddingEnd="16dp"
+ android:paddingStart="8dp"
+ android:layout_marginTop="26dp"
+ app:layout_constraintWidth_percent="0.67">
+
+ <com.android.launcher3.widget.picker.WidgetsRecommendationTableLayout
+ android:id="@+id/recommended_widget_table"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="@drawable/widgets_surface_background"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ android:paddingVertical="@dimen/recommended_widgets_table_vertical_padding"
+ android:visibility="gone" />
+ </ScrollView>
+
+ <View
+ android:id="@+id/collapse_handle"
+ android:layout_width="@dimen/bottom_sheet_handle_width"
+ android:layout_height="@dimen/bottom_sheet_handle_height"
+ android:layout_marginTop="@dimen/bottom_sheet_handle_margin"
+ android:background="@drawable/bg_rounded_corner_bottom_sheet_handle"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent" />
+
+ <TextView
+ android:id="@+id/title"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/collapse_handle"
+ android:layout_marginTop="24dp"
+ android:gravity="center_horizontal"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ android:text="@string/widget_button_text"
+ android:textColor="?android:attr/textColorSecondary"
+ android:textSize="24sp" />
+
+ </androidx.constraintlayout.widget.ConstraintLayout>
+</com.android.launcher3.widget.picker.WidgetsFullSheet>
diff --git a/res/layout/widgets_full_sheet_paged_view_large_screen.xml b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
new file mode 100644
index 0000000..6634345
--- /dev/null
+++ b/res/layout/widgets_full_sheet_paged_view_large_screen.xml
@@ -0,0 +1,122 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2023 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"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <FrameLayout
+ android:id="@+id/widgets_full_sheet_paged_view_large_screen"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toStartOf="@id/scrollView"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/title"
+ app:layout_constraintWidth_percent="0.33">
+ <com.android.launcher3.widget.picker.WidgetPagedView
+ android:id="@+id/widgets_view_pager"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false"
+ android:descendantFocusability="afterDescendants"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ launcher:pageIndicator="@+id/tabs" >
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/primary_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false" />
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/work_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:clipToPadding="false" />
+
+ </com.android.launcher3.widget.picker.WidgetPagedView>
+
+ <!-- SearchAndRecommendationsView without the tab layout as well -->
+ <com.android.launcher3.views.StickyHeaderLayout
+ android:id="@+id/search_and_recommendations_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/search_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorBackground"
+ android:clipToPadding="false"
+ android:elevation="0.1dp"
+ android:paddingBottom="8dp"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ launcher:layout_sticky="true">
+
+ <include layout="@layout/widgets_search_bar" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/suggestions_header"
+ android:layout_marginTop="8dp"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ android:orientation="horizontal">
+ </LinearLayout>
+
+ <com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip
+ android:id="@+id/tabs"
+ android:layout_width="match_parent"
+ android:layout_height="64dp"
+ android:gravity="center_horizontal"
+ android:orientation="horizontal"
+ android:paddingVertical="8dp"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ android:background="?android:attr/colorBackground"
+ style="@style/TextHeadline"
+ launcher:layout_sticky="true">
+
+ <Button
+ android:id="@+id/tab_personal"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
+ android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
+ android:layout_weight="1"
+ android:background="@drawable/all_apps_tabs_background"
+ android:text="@string/widgets_full_sheet_personal_tab"
+ android:textColor="@color/all_apps_tab_text"
+ android:textSize="14sp"
+ style="?android:attr/borderlessButtonStyle" />
+
+ <Button
+ android:id="@+id/tab_work"
+ android:layout_width="0dp"
+ android:layout_height="match_parent"
+ android:layout_marginEnd="@dimen/widget_tabs_button_horizontal_padding"
+ android:layout_marginVertical="@dimen/widget_apps_tabs_vertical_padding"
+ android:layout_weight="1"
+ android:background="@drawable/all_apps_tabs_background"
+ android:text="@string/widgets_full_sheet_work_tab"
+ android:textColor="@color/all_apps_tab_text"
+ android:textSize="14sp"
+ style="?android:attr/borderlessButtonStyle" />
+
+ </com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip>
+ </com.android.launcher3.views.StickyHeaderLayout>
+ </FrameLayout>
+</merge>
diff --git a/res/layout/widgets_full_sheet_recyclerview_large_screen.xml b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
new file mode 100644
index 0000000..212cd55
--- /dev/null
+++ b/res/layout/widgets_full_sheet_recyclerview_large_screen.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?><!-- Copyright (C) 2021 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"
+ xmlns:app="http://schemas.android.com/apk/res-auto">
+
+ <FrameLayout
+ android:id="@+id/widgets_full_sheet_recyclerview_large_screen"
+ android:layout_width="0dp"
+ android:layout_height="0dp"
+ app:layout_constraintEnd_toStartOf="@id/scrollView"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintBottom_toBottomOf="parent"
+ app:layout_constraintTop_toBottomOf="@id/title"
+ app:layout_constraintWidth_percent="0.33">
+
+ <com.android.launcher3.widget.picker.WidgetsRecyclerView
+ android:id="@+id/primary_widgets_list_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ android:clipToPadding="false" />
+
+ <!-- SearchAndRecommendationsView without the tab layout as well -->
+ <com.android.launcher3.views.StickyHeaderLayout
+ android:id="@+id/search_and_recommendations_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="vertical">
+
+ <FrameLayout
+ android:id="@+id/search_bar_container"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:background="?android:attr/colorBackground"
+ android:clipToPadding="false"
+ android:elevation="0.1dp"
+ android:paddingBottom="8dp"
+ android:paddingHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ launcher:layout_sticky="true">
+
+ <include layout="@layout/widgets_search_bar" />
+ </FrameLayout>
+
+ <LinearLayout
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:id="@+id/suggestions_header"
+ android:layout_marginTop="8dp"
+ android:layout_marginHorizontal="@dimen/widget_list_horizontal_margin_large_screen"
+ android:orientation="horizontal">
+ </LinearLayout>
+ </com.android.launcher3.views.StickyHeaderLayout>
+ </FrameLayout>
+</merge>
diff --git a/res/values-sw720dp-land/dimens.xml b/res/values-sw720dp-land/dimens.xml
index 6362960..b89910d 100644
--- a/res/values-sw720dp-land/dimens.xml
+++ b/res/values-sw720dp-land/dimens.xml
@@ -31,6 +31,7 @@
<!-- Widget picker-->
<dimen name="widget_list_horizontal_margin">49dp</dimen>
+ <dimen name="widget_list_horizontal_margin_large_screen">24dp</dimen>
<!-- Bottom sheet-->
<dimen name="bottom_sheet_extra_top_padding">0dp</dimen>
diff --git a/res/values-v31/colors.xml b/res/values-v31/colors.xml
index 63a5454..f87d9fc 100644
--- a/res/values-v31/colors.xml
+++ b/res/values-v31/colors.xml
@@ -64,4 +64,6 @@
<color name="all_apps_button_color_light">@android:color/system_neutral2_700</color>
<color name="all_apps_button_color_dark">@android:color/system_neutral2_200</color>
+
+ <color name="widget_picker_background_selected">@android:color/system_accent2_100</color>
</resources>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index a9638f9..9d2aa06 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -192,6 +192,7 @@
<dimen name="widget_list_header_view_vertical_padding">20dp</dimen>
<dimen name="widget_list_entry_spacing">2dp</dimen>
<dimen name="widget_list_horizontal_margin">16dp</dimen>
+ <dimen name="widget_list_horizontal_margin_large_screen">24dp</dimen>
<dimen name="widget_preview_shadow_blur">0.5dp</dimen>
<dimen name="widget_preview_key_shadow_distance">1dp</dimen>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index a2ebf16..190a3a5 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -63,6 +63,9 @@
<!-- Accessibility spoken message announced when a widget gets added to the home screen using a
button in a dialog. [CHAR_LIMIT=none] -->
<string name="added_to_home_screen_accessibility_text"><xliff:g id="widget_name" example="Calendar month view">%1$s</xliff:g> widget added to home screen</string>
+ <!-- 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>
<!-- Label for showing the number of widgets an app has in the full widgets picker.
[CHAR_LIMIT=25][ICU SYNTAX] -->
<string name="widgets_count">
diff --git a/src/com/android/launcher3/FastScrollRecyclerView.java b/src/com/android/launcher3/FastScrollRecyclerView.java
index 3504b24..e1a216e 100644
--- a/src/com/android/launcher3/FastScrollRecyclerView.java
+++ b/src/com/android/launcher3/FastScrollRecyclerView.java
@@ -63,6 +63,9 @@
public void bindFastScrollbar() {
ViewGroup parent = (ViewGroup) getParent().getParent();
+ if (parent.findViewById(R.id.fast_scroller) == null) {
+ parent = (ViewGroup) parent.getParent();
+ }
mScrollbar = parent.findViewById(R.id.fast_scroller);
mScrollbar.setRecyclerView(this, parent.findViewById(R.id.fast_scroller_popup));
onUpdateScrollbar(0);
diff --git a/src/com/android/launcher3/popup/PopupDataProvider.java b/src/com/android/launcher3/popup/PopupDataProvider.java
index 80ffecc..69c96ff 100644
--- a/src/com/android/launcher3/popup/PopupDataProvider.java
+++ b/src/com/android/launcher3/popup/PopupDataProvider.java
@@ -238,6 +238,15 @@
.collect(Collectors.toList());
}
+ /** Gets the WidgetsListContentEntry for the currently selected header. */
+ public WidgetsListContentEntry getSelectedAppWidgets(PackageUserKey packageUserKey) {
+ return (WidgetsListContentEntry) mAllWidgets.stream()
+ .filter(row -> row instanceof WidgetsListContentEntry
+ && row.mPkgItem.packageName.equals(packageUserKey.mPackageName))
+ .findAny()
+ .orElse(null);
+ }
+
/**
* Returns a list of notifications that are relevant to given ItemInfo.
*/
diff --git a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
index c78ecf5..bb80e9b 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsFullSheet.java
@@ -20,6 +20,7 @@
import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
import static com.android.launcher3.allapps.AllAppsTransitionController.SWIPE_ALL_APPS_TO_HOME_MIN_SCALE;
+import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER;
import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_WIDGETSTRAY_SEARCHED;
import static com.android.launcher3.testing.shared.TestProtocol.NORMAL_STATE_ORDINAL;
@@ -45,14 +46,18 @@
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.Button;
+import android.widget.LinearLayout;
+import android.widget.ScrollView;
import android.widget.TextView;
import androidx.annotation.FloatRange;
+import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.recyclerview.widget.DefaultItemAnimator;
import androidx.recyclerview.widget.RecyclerView;
+import com.android.launcher3.DeviceProfile;
import com.android.launcher3.Launcher;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
@@ -62,7 +67,9 @@
import com.android.launcher3.compat.AccessibilityManagerCompat;
import com.android.launcher3.model.UserManagerState;
import com.android.launcher3.model.WidgetItem;
+import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.pm.UserCache;
+import com.android.launcher3.util.PackageUserKey;
import com.android.launcher3.views.ArrowTipView;
import com.android.launcher3.views.RecyclerViewFastScroller;
import com.android.launcher3.views.SpringRelativeLayout;
@@ -71,6 +78,8 @@
import com.android.launcher3.widget.BaseWidgetSheet;
import com.android.launcher3.widget.LauncherWidgetHolder.ProviderChangedListener;
import com.android.launcher3.widget.model.WidgetsListBaseEntry;
+import com.android.launcher3.widget.model.WidgetsListContentEntry;
+import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.picker.search.SearchModeListener;
import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
import com.android.launcher3.widget.util.WidgetsTableUtils;
@@ -78,6 +87,7 @@
import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
import java.util.ArrayList;
+import java.util.Collections;
import java.util.List;
import java.util.function.Predicate;
import java.util.stream.IntStream;
@@ -93,6 +103,9 @@
private static final long EDUCATION_TIP_DELAY_MS = 200;
private static final long EDUCATION_DIALOG_DELAY_MS = 500;
private static final float VERTICAL_START_POSITION = 0.3f;
+ private static final int PERSONAL_TAB = 0;
+ private static final int WORK_TAB = 1;
+ private static final String SUGGESTIONS_PACKAGE_NAME = "widgets_list_suggestions_entry";
// The widget recommendation table can easily take over the entire screen on devices with small
// resolution or landscape on phone. This ratio defines the max percentage of content area that
// the table can display.
@@ -169,14 +182,23 @@
private StickyHeaderLayout mSearchScrollView;
private WidgetsRecommendationTableLayout mRecommendedWidgetsTable;
+ private LinearLayout mSuggestedWidgetsContainer;
+ private WidgetsListHeader mSuggestedWidgetsHeader;
private View mTabBar;
private View mSearchBarContainer;
private WidgetsSearchBar mSearchBar;
private TextView mHeaderTitle;
+ private ScrollView mRightPane;
+ private WidgetsListTableViewHolderBinder mWidgetsListTableViewHolderBinder;
+ private final boolean mIsTwoPane;
+
+ private int mOrientation;
private @Nullable WidgetsRecyclerView mCurrentTouchEventRecyclerView;
public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
+ DeviceProfile dp = Launcher.getLauncher(context).getDeviceProfile();
+ mIsTwoPane = dp.isTablet && dp.isLandscape && LARGE_SCREEN_WIDGET_PICKER.get();
mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
@@ -205,9 +227,16 @@
LayoutInflater layoutInflater = LayoutInflater.from(getContext());
int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
: R.layout.widgets_full_sheet_recyclerview;
+ if (mIsTwoPane) {
+ contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view_large_screen
+ : R.layout.widgets_full_sheet_recyclerview_large_screen;
+ }
layoutInflater.inflate(contentLayoutRes, mContent, true);
RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
+ if (mIsTwoPane) {
+ fastScroller.setVisibility(GONE);
+ }
mAdapters.get(AdapterHolder.PRIMARY).setup(findViewById(R.id.primary_widgets_list_view));
mAdapters.get(AdapterHolder.SEARCH).setup(findViewById(R.id.search_widgets_list_view));
if (mHasWorkProfile) {
@@ -230,15 +259,61 @@
mSearchScrollView = findViewById(R.id.search_and_recommendations_container);
mSearchScrollView.setCurrentRecyclerView(findViewById(R.id.primary_widgets_list_view));
- mRecommendedWidgetsTable = mSearchScrollView.findViewById(R.id.recommended_widget_table);
+ mRecommendedWidgetsTable = mIsTwoPane
+ ? mContent.findViewById(R.id.recommended_widget_table)
+ : mSearchScrollView.findViewById(R.id.recommended_widget_table);
+
mRecommendedWidgetsTable.setWidgetCellLongClickListener(this);
mRecommendedWidgetsTable.setWidgetCellOnClickListener(this);
+ // Add suggested widgets.
+ if (mIsTwoPane) {
+ mSuggestedWidgetsContainer = mSearchScrollView.findViewById(R.id.suggestions_header);
+
+ // Inflate the suggestions header.
+ mSuggestedWidgetsHeader = (WidgetsListHeader) layoutInflater.inflate(
+ R.layout.widgets_list_row_header, mSuggestedWidgetsContainer, false);
+ mSuggestedWidgetsHeader.setExpanded(true);
+ mSuggestedWidgetsHeader.setBackground(
+ new WidgetsListDrawableFactory(getContext()).createHeaderBackgroundDrawable());
+
+ PackageItemInfo packageItemInfo = new PackageItemInfo(
+ /* packageName= */ SUGGESTIONS_PACKAGE_NAME,
+ Process.myUserHandle()) {
+ @Override
+ public boolean usingLowResIcon() {
+ return false;
+ }
+ };
+ packageItemInfo.title = getContext().getString(R.string.suggested_widgets_header_title);
+ WidgetsListHeaderEntry widgetsListHeaderEntry = new WidgetsListHeaderEntry(
+ packageItemInfo,
+ getContext().getString(R.string.suggested_widgets_header_title),
+ mActivityContext.getPopupDataProvider().getRecommendedWidgets())
+ .withWidgetListShown();
+
+ mSuggestedWidgetsHeader.applyFromItemInfoWithIcon(widgetsListHeaderEntry);
+ mSuggestedWidgetsHeader.setIcon(
+ getContext().getDrawable(R.drawable.widget_suggestions_icon));
+ mSuggestedWidgetsHeader.setOnExpandChangeListener(isExpanded -> {
+ mSuggestedWidgetsHeader.setExpanded(isExpanded);
+ resetExpandedHeaders();
+ mRightPane.removeAllViews();
+ mRightPane.addView(mRecommendedWidgetsTable);
+ });
+ mSuggestedWidgetsContainer.addView(mSuggestedWidgetsHeader);
+ }
+
mTabBar = mSearchScrollView.findViewById(R.id.tabs);
mSearchBarContainer = mSearchScrollView.findViewById(R.id.search_bar_container);
mSearchBar = mSearchScrollView.findViewById(R.id.widgets_search_bar);
- mHeaderTitle = mSearchScrollView.findViewById(R.id.title);
-
+ mHeaderTitle = mIsTwoPane
+ ? mContent.findViewById(R.id.title)
+ : mSearchScrollView.findViewById(R.id.title);
+ mRightPane = mIsTwoPane ? mContent.findViewById(R.id.right_pane) : null;
+ mWidgetsListTableViewHolderBinder = new WidgetsListTableViewHolderBinder(
+ layoutInflater, this, this,
+ new WidgetsListDrawableFactory(getContext()));
onRecommendedWidgetsBound();
onWidgetsBound();
@@ -260,6 +335,13 @@
@Override
public void onActivePageChanged(int currentActivePage) {
+
+ // if the current active page changes to personal or work we set suggestions
+ // to be the selected widget
+ if (mIsTwoPane && (currentActivePage == PERSONAL_TAB || currentActivePage == WORK_TAB)) {
+ mSuggestedWidgetsHeader.callOnClick();
+ }
+
AdapterHolder currentAdapterHolder = mAdapters.get(currentActivePage);
WidgetsRecyclerView currentRecyclerView =
mAdapters.get(currentActivePage).mWidgetsRecyclerView;
@@ -428,6 +510,11 @@
View content = mHasWorkProfile
? mViewPager
: mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
+
+ if (mIsTwoPane && mRightPane != null) {
+ content = mRightPane;
+ }
+
int maxHorizontalSpans = computeMaxHorizontalSpans(content,
mWidgetSheetContentHorizontalPadding);
if (mMaxSpansPerRow != maxHorizontalSpans) {
@@ -520,6 +607,9 @@
public void onSearchResults(List<WidgetsListBaseEntry> entries) {
mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
updateRecyclerViewVisibility(mAdapters.get(AdapterHolder.SEARCH));
+ if (mIsTwoPane) {
+ mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.selectFirstHeaderEntry();
+ }
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
}
@@ -527,6 +617,9 @@
mIsInSearchMode = isInSearchMode;
if (isInSearchMode) {
mRecommendedWidgetsTable.setVisibility(GONE);
+ if (mIsTwoPane) {
+ mSuggestedWidgetsContainer.setVisibility(GONE);
+ }
if (mHasWorkProfile) {
mViewPager.setVisibility(GONE);
mTabBar.setVisibility(GONE);
@@ -538,6 +631,10 @@
mNoWidgetsView.setVisibility(GONE);
} else {
mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.setVisibility(GONE);
+ if (mIsTwoPane) {
+ mSuggestedWidgetsContainer.setVisibility(VISIBLE);
+ mSuggestedWidgetsHeader.callOnClick();
+ }
// Visibility of recommended widgets, recycler views and headers are handled in methods
// below.
onRecommendedWidgetsBound();
@@ -572,7 +669,7 @@
MeasureSpec.EXACTLY),
makeMeasureSpec(mActivityContext.getDeviceProfile().availableHeightPx,
MeasureSpec.EXACTLY));
- float maxTableHeight = (mContent.getMeasuredHeight()
+ float maxTableHeight = mIsTwoPane ? Float.MAX_VALUE : (mContent.getMeasuredHeight()
- mTabsHeight - getHeaderViewHeight()
- noWidgetsViewHeight) * RECOMMENDATION_TABLE_HEIGHT_RATIO;
@@ -626,7 +723,7 @@
@Override
public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
- // Disable swipe down when recycler view is scrolling
+ // Disable swipe down when recycler view is scrolling or scroll view is scrolling
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
mNoIntercept = false;
WidgetsRecyclerView recyclerView = getRecyclerView();
@@ -636,6 +733,8 @@
mNoIntercept = true;
} else if (getPopupContainer().isEventOverView(recyclerView, ev)) {
mNoIntercept = !recyclerView.shouldContainerScroll(ev, getPopupContainer());
+ } else if (mIsTwoPane && getPopupContainer().isEventOverView(mRightPane, ev)) {
+ mNoIntercept = mRightPane.getScrollY() > 0;
}
if (mSearchBar.isSearchBarFocused()
@@ -649,7 +748,11 @@
/** Shows the {@link WidgetsFullSheet} on the launcher. */
public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
- .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
+ .inflate(LARGE_SCREEN_WIDGET_PICKER.get()
+ && launcher.getDeviceProfile().isTablet
+ && launcher.getDeviceProfile().isLandscape
+ ? R.layout.widgets_full_sheet_large_screen
+ : R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
sheet.attachToContainer();
sheet.mIsOpen = true;
sheet.open(animate);
@@ -746,6 +849,13 @@
if (mIsInSearchMode) {
mSearchBar.reset();
}
+
+ // Checks the orientation of the screen
+ if (LARGE_SCREEN_WIDGET_PICKER.get() && mOrientation != newConfig.orientation) {
+ mOrientation = newConfig.orientation;
+ handleClose(false);
+ show(Launcher.getLauncher(getContext()), false);
+ }
}
@Override
@@ -835,16 +945,44 @@
AdapterHolder(int adapterType) {
mAdapterType = adapterType;
-
Context context = getContext();
LauncherAppState apps = LauncherAppState.getInstance(context);
+
+ HeaderChangeListener headerChangeListener = new HeaderChangeListener() {
+ @Override
+ public void onHeaderChanged(@NonNull PackageUserKey selectedHeader) {
+ WidgetsListContentEntry contentEntry = mActivityContext.getPopupDataProvider()
+ .getSelectedAppWidgets(selectedHeader);
+
+ if (contentEntry == null || mRightPane == null) {
+ return;
+ }
+
+ if (mSuggestedWidgetsHeader != null) {
+ mSuggestedWidgetsHeader.setExpanded(false);
+ }
+ WidgetsRowViewHolder widgetsRowViewHolder =
+ mWidgetsListTableViewHolderBinder.newViewHolder(mRightPane);
+ mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
+ contentEntry,
+ 0, Collections.EMPTY_LIST);
+ widgetsRowViewHolder.mDataCallback = data -> {
+ mWidgetsListTableViewHolderBinder.bindViewHolder(widgetsRowViewHolder,
+ contentEntry,
+ 0, Collections.singletonList(data));
+ };
+ mRightPane.removeAllViews();
+ mRightPane.addView(widgetsRowViewHolder.itemView);
+ }
+ };
mWidgetsListAdapter = new WidgetsListAdapter(
context,
LayoutInflater.from(context),
apps.getIconCache(),
this::getEmptySpaceHeight,
/* iconClickListener= */ WidgetsFullSheet.this,
- /* iconLongClickListener= */ WidgetsFullSheet.this);
+ /* iconLongClickListener= */ WidgetsFullSheet.this,
+ mIsTwoPane ? headerChangeListener : null);
mWidgetsListAdapter.setHasStableIds(true);
switch (mAdapterType) {
case PRIMARY:
@@ -871,8 +1009,10 @@
mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
mWidgetsRecyclerView.setItemAnimator(mWidgetsListItemAnimator);
mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
- mWidgetsRecyclerView.setEdgeEffectFactory(
- ((SpringRelativeLayout) mContent).createEdgeEffectFactory());
+ if (!mIsTwoPane) {
+ mWidgetsRecyclerView.setEdgeEffectFactory(
+ ((SpringRelativeLayout) mContent).createEdgeEffectFactory());
+ }
// Recycler view binds to fast scroller when it is attached to screen. Make sure
// search recycler view is bound to fast scroller if user is in search mode at the time
// of attachment.
@@ -882,4 +1022,15 @@
mWidgetsListAdapter.setMaxHorizontalSpansPerRow(mMaxSpansPerRow);
}
}
+
+ /**
+ * This is a listener for when the selected header gets changed in the left pane.
+ */
+ public interface HeaderChangeListener {
+ /**
+ * Sets the right pane to have the widgets for the currently selected header from
+ * the left pane.
+ */
+ void onHeaderChanged(@NonNull PackageUserKey selectedHeader);
+ }
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
index e6b9dca..dc1d2dc 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListAdapter.java
@@ -88,6 +88,7 @@
private final SparseArray<ViewHolderBinder> mViewHolderBinders = new SparseArray<>();
private final WidgetListBaseRowEntryComparator mRowComparator =
new WidgetListBaseRowEntryComparator();
+ @Nullable private final WidgetsFullSheet.HeaderChangeListener mHeaderChangeListener;
private final List<WidgetsListBaseEntry> mAllEntries = new ArrayList<>();
private ArrayList<WidgetsListBaseEntry> mVisibleEntries = new ArrayList<>();
@@ -105,7 +106,9 @@
public WidgetsListAdapter(Context context, LayoutInflater layoutInflater,
IconCache iconCache, IntSupplier emptySpaceHeightProvider,
- OnClickListener iconClickListener, OnLongClickListener iconLongClickListener) {
+ OnClickListener iconClickListener, OnLongClickListener iconLongClickListener,
+ WidgetsFullSheet.HeaderChangeListener headerChangeListener) {
+ mHeaderChangeListener = headerChangeListener;
mContext = context;
mDiffReporter = new WidgetsDiffReporter(iconCache, this);
WidgetsListDrawableFactory listDrawableFactory = new WidgetsListDrawableFactory(context);
@@ -196,9 +199,11 @@
getOffsetForPosition(previousPositionForPackageUserKey);
List<WidgetsListBaseEntry> newVisibleEntries = mAllEntries.stream()
- .filter(entry -> ((mFilter == null || mFilter.test(entry))
+ .filter(entry -> (((mFilter == null || mFilter.test(entry))
&& mHeaderAndSelectedContentFilter.test(entry))
|| entry instanceof WidgetListSpaceEntry)
+ && (mHeaderChangeListener == null
+ || !(entry instanceof WidgetsListContentEntry)))
.map(entry -> {
if (entry instanceof WidgetsListBaseEntry.Header<?>
&& matchesKey(entry, mWidgetsContentVisiblePackageUserKey)) {
@@ -225,7 +230,6 @@
}
}
-
/** Returns whether {@code entry} matches {@code key}. */
private static boolean isHeaderForPackageUserKey(
@NonNull WidgetsListBaseEntry entry, @Nullable PackageUserKey key) {
@@ -258,7 +262,6 @@
@Override
public void onBindViewHolder(ViewHolder holder, int pos, List<Object> payloads) {
ViewHolderBinder viewHolderBinder = mViewHolderBinders.get(getItemViewType(pos));
- WidgetsListBaseEntry entry = mVisibleEntries.get(pos);
// The first entry has an empty space, count from second entries.
int listPos = (pos > 1) ? POSITION_DEFAULT : POSITION_FIRST;
@@ -268,6 +271,23 @@
viewHolderBinder.bindViewHolder(holder, mVisibleEntries.get(pos), listPos, payloads);
}
+ /**
+ * Selects the first visible header. This is used in search as we want to always select the
+ * first header in the new list that gets generated as we search.
+ */
+ void selectFirstHeaderEntry() {
+ WidgetsListSearchHeaderEntry firstEntry = null;
+ for (WidgetsListBaseEntry entry: mVisibleEntries) {
+ if (entry instanceof WidgetsListSearchHeaderEntry) {
+ firstEntry = (WidgetsListSearchHeaderEntry) entry;
+ break;
+ }
+ }
+ if (firstEntry != null) {
+ onHeaderClicked(true, PackageUserKey.fromPackageItemInfo(firstEntry.mPkgItem));
+ }
+ }
+
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (DEBUG) {
@@ -318,6 +338,9 @@
// Ignore invalid clicks, such as collapsing a package that isn't currently expanded.
if (!showWidgets && !packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) return;
+ if (mHeaderChangeListener != null
+ && packageUserKey.equals(mWidgetsContentVisiblePackageUserKey)) return;
+
if (showWidgets) {
mWidgetsContentVisiblePackageUserKey = packageUserKey;
ActivityContext.lookupContext(mContext)
@@ -331,6 +354,10 @@
mPendingClickHeader = packageUserKey;
updateVisibleEntries();
+
+ if (mHeaderChangeListener != null && mWidgetsContentVisiblePackageUserKey != null) {
+ mHeaderChangeListener.onHeaderChanged(mWidgetsContentVisiblePackageUserKey);
+ }
}
/**
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
index 48df04f..8c91d43 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListHeader.java
@@ -15,13 +15,17 @@
*/
package com.android.launcher3.widget.picker;
+import static com.android.launcher3.config.FeatureFlags.LARGE_SCREEN_WIDGET_PICKER;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.GradientDrawable;
import android.os.Bundle;
import android.util.AttributeSet;
+import android.util.TypedValue;
import android.view.View;
import android.view.accessibility.AccessibilityNodeInfo;
import android.widget.CheckBox;
@@ -35,12 +39,14 @@
import com.android.launcher3.DeviceProfile;
import com.android.launcher3.LauncherAppState;
import com.android.launcher3.R;
+import com.android.launcher3.Utilities;
import com.android.launcher3.icons.IconCache.ItemInfoUpdateReceiver;
import com.android.launcher3.icons.PlaceHolderIconDrawable;
import com.android.launcher3.icons.cache.HandlerRunnable;
import com.android.launcher3.model.data.ItemInfoWithIcon;
import com.android.launcher3.model.data.PackageItemInfo;
import com.android.launcher3.util.PluralMessageFormat;
+import com.android.launcher3.util.Themes;
import com.android.launcher3.views.ActivityContext;
import com.android.launcher3.widget.model.WidgetsListHeaderEntry;
import com.android.launcher3.widget.model.WidgetsListSearchHeaderEntry;
@@ -55,19 +61,19 @@
*/
public final class WidgetsListHeader extends LinearLayout implements ItemInfoUpdateReceiver {
- private boolean mEnableIconUpdateAnimation = false;
+ private final int mIconSize;
+ private final boolean mIsTwoPane;
@Nullable private HandlerRunnable mIconLoadRequest;
@Nullable private Drawable mIconDrawable;
- private final int mIconSize;
-
+ @Nullable private WidgetsListDrawableState mListDrawableState;
private ImageView mAppIcon;
private TextView mTitle;
private TextView mSubtitle;
-
+ private GradientDrawable mBackground;
private CheckBox mExpandToggle;
+ private boolean mEnableIconUpdateAnimation = false;
private boolean mIsExpanded = false;
- @Nullable private WidgetsListDrawableState mListDrawableState;
public WidgetsListHeader(Context context) {
this(context, /* attrs= */ null);
@@ -86,6 +92,11 @@
R.styleable.WidgetsListRowHeader, defStyleAttr, /* defStyleRes= */ 0);
mIconSize = a.getDimensionPixelSize(R.styleable.WidgetsListRowHeader_appIconSize,
grid.iconSizePx);
+
+ mIsTwoPane = grid.isLandscape && grid.isTablet && LARGE_SCREEN_WIDGET_PICKER.get();
+ if (mIsTwoPane) {
+ setLargeScreenTheme();
+ }
}
@Override
@@ -95,6 +106,9 @@
mTitle = findViewById(R.id.app_title);
mSubtitle = findViewById(R.id.app_subtitle);
mExpandToggle = findViewById(R.id.toggle);
+ if (mIsTwoPane) {
+ mExpandToggle.setVisibility(GONE);
+ }
setAccessibilityDelegate(new AccessibilityDelegate() {
@Override
@@ -132,7 +146,7 @@
@Nullable OnExpansionChangeListener onExpandChangeListener) {
// Use the entire touch area of this view to expand / collapse an app widgets section.
setOnClickListener(view -> {
- setExpanded(!mIsExpanded);
+ setExpanded(mIsTwoPane || !mIsExpanded);
if (onExpandChangeListener != null) {
onExpandChangeListener.onExpansionChange(mIsExpanded);
}
@@ -144,8 +158,38 @@
public void setExpanded(boolean isExpanded) {
this.mIsExpanded = isExpanded;
mExpandToggle.setChecked(isExpanded);
+ if (mIsTwoPane) {
+ if (Utilities.isDarkTheme(getContext())) {
+ if (mIsExpanded) {
+ mTitle.setTextColor(Color.BLACK);
+ mSubtitle.setTextColor(Color.BLACK);
+ } else {
+ mTitle.setTextColor(Color.WHITE);
+ mSubtitle.setTextColor(Themes.getAttrColor(getContext(),
+ android.R.attr.textColorSecondary));
+ }
+ }
+ setLargeScreenTheme();
+ }
}
+ /**
+ * Sets the style for the header when we are using large screens in landscape.
+ */
+ private void setLargeScreenTheme() {
+ if (mBackground == null) {
+ mBackground = new GradientDrawable();
+ mBackground.setCornerRadius((int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
+ 28,
+ getContext().getResources().getDisplayMetrics()));
+ }
+ mBackground.setColor(mIsExpanded
+ ? getResources().getColor(R.color.widget_picker_background_selected)
+ : Color.TRANSPARENT);
+ this.setBackground(mBackground);
+ }
+
+
/** Sets the {@link WidgetsListDrawableState} and refreshes the background drawable. */
@UiThread
public void setListDrawableState(WidgetsListDrawableState state) {
@@ -163,7 +207,7 @@
@UiThread
private void applyIconAndLabel(WidgetsListHeaderEntry entry) {
PackageItemInfo info = entry.mPkgItem;
- setIcon(info);
+ setIcon(info.newIcon(getContext()));
setTitles(entry);
setExpanded(entry.isWidgetListShown());
@@ -172,9 +216,7 @@
verifyHighRes();
}
- private void setIcon(PackageItemInfo info) {
- Drawable icon;
- icon = info.newIcon(getContext());
+ void setIcon(Drawable icon) {
applyDrawables(icon);
mIconDrawable = icon;
if (mIconDrawable != null) {
@@ -239,7 +281,7 @@
@UiThread
private void applyIconAndLabel(WidgetsListSearchHeaderEntry entry) {
PackageItemInfo info = entry.mPkgItem;
- setIcon(info);
+ setIcon(info.newIcon(getContext()));
setTitles(entry);
setExpanded(entry.isWidgetListShown());
@@ -265,7 +307,7 @@
// Optimization: Starting in N, pre-uploads the bitmap to RenderThread.
info.bitmap.icon.prepareToDraw();
- setIcon((PackageItemInfo) info);
+ setIcon(info.newIcon(getContext()));
mEnableIconUpdateAnimation = false;
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
index 2b27fc2..9e659fe 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListSearchHeaderViewHolderBinder.java
@@ -56,6 +56,7 @@
WidgetsListSearchHeaderEntry data, @ListPosition int position, List<Object> payloads) {
WidgetsListHeader widgetsListHeader = viewHolder.mWidgetsListHeader;
widgetsListHeader.applyFromItemInfoWithIcon(data);
+ widgetsListHeader.setSelected(data.isWidgetListShown());
widgetsListHeader.setExpanded(data.isWidgetListShown());
widgetsListHeader.setListDrawableState(
WidgetsListDrawableState.obtain(
diff --git a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
index 05e26ad..8500b9a 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsListTableViewHolderBinder.java
@@ -110,13 +110,8 @@
// When preview loads, notify adapter to rebind the item and possibly animate
widget.applyFromCellItem(widgetItem, 1f,
- bitmap -> {
- if (holder.getBindingAdapter() != null) {
- holder.getBindingAdapter().notifyItemChanged(
- holder.getBindingAdapterPosition(),
- Pair.create(widgetItem, bitmap));
- }
- }, holder.previewCache.get(widgetItem));
+ bitmap -> holder.onPreviewLoaded(Pair.create(widgetItem, bitmap)),
+ holder.previewCache.get(widgetItem));
}
}
}
diff --git a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
index fe2d84b..7411459 100644
--- a/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
+++ b/src/com/android/launcher3/widget/picker/WidgetsRowViewHolder.java
@@ -16,6 +16,7 @@
package com.android.launcher3.widget.picker;
import android.graphics.Bitmap;
+import android.util.Pair;
import android.view.View;
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
@@ -25,16 +26,33 @@
import java.util.HashMap;
import java.util.Map;
+import java.util.function.Consumer;
/** A {@link ViewHolder} for showing widgets of an app in the full widget picker. */
public final class WidgetsRowViewHolder extends ViewHolder {
public final WidgetsListTableView tableContainer;
public final Map<WidgetItem, Bitmap> previewCache = new HashMap<>();
+ Consumer<Pair<WidgetItem, Bitmap>> mDataCallback;
public WidgetsRowViewHolder(View v) {
super(v);
tableContainer = v.findViewById(R.id.widgets_table);
}
+
+ /**
+ * When the preview is loaded we callback to notify that the preview loaded and we rebind the
+ * view.
+ *
+ * @param data is the payload which is needed when binding the view.
+ */
+ public void onPreviewLoaded(Pair<WidgetItem, Bitmap> data) {
+ if (mDataCallback != null) {
+ mDataCallback.accept(data);
+ }
+ if (getBindingAdapter() != null) {
+ getBindingAdapter().notifyItemChanged(getBindingAdapterPosition(), data);
+ }
+ }
}
diff --git a/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
index 4e0bdda..230b27f 100644
--- a/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
+++ b/tests/src/com/android/launcher3/widget/picker/WidgetsListAdapterTest.java
@@ -86,7 +86,7 @@
mTestProfile.numColumns = 5;
mUserHandle = Process.myUserHandle();
mAdapter = new WidgetsListAdapter(mContext, mMockLayoutInflater,
- mIconCache, () -> 0, null, null);
+ mIconCache, () -> 0, null, null, null);
mAdapter.registerAdapterDataObserver(mListener);
doAnswer(invocation -> ((ComponentWithLabel) invocation.getArgument(0))