blob: 5747690ba61c77d1dbc7a7d49f1b270839d27ddb [file] [log] [blame]
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -07001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
Steven Ng2f5648a2021-02-08 17:18:25 +000016package com.android.launcher3.widget.picker;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070017
Sunny Goyalf3ac7032020-03-13 13:01:33 -070018import static com.android.launcher3.LauncherAnimUtils.VIEW_TRANSLATE_Y;
vadimt9f48a8e2019-12-11 11:52:37 -080019import static com.android.launcher3.testing.TestProtocol.NORMAL_STATE_ORDINAL;
20
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070021import android.animation.Animator;
22import android.animation.AnimatorListenerAdapter;
23import android.animation.PropertyValuesHolder;
24import android.content.Context;
Steven Ng391404f2021-02-17 15:58:23 +000025import android.content.pm.LauncherApps;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070026import android.graphics.Rect;
Steven Ng391404f2021-02-17 15:58:23 +000027import android.os.Process;
28import android.os.UserHandle;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070029import android.util.AttributeSet;
Sunny Goyalde753212018-05-15 13:55:57 -070030import android.util.Pair;
Steven Ng391404f2021-02-17 15:58:23 +000031import android.util.SparseArray;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070032import android.view.LayoutInflater;
33import android.view.MotionEvent;
Sunny Goyalde753212018-05-15 13:55:57 -070034import android.view.View;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070035import android.view.animation.AnimationUtils;
Sunny Goyalf3ac7032020-03-13 13:01:33 -070036import android.view.animation.Interpolator;
Steven Ng167f81b2021-02-23 10:48:46 +000037import android.widget.TextView;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070038
Steven Ng391404f2021-02-17 15:58:23 +000039import androidx.annotation.Nullable;
Tony Wickham9791bd12019-04-05 13:52:35 -070040import androidx.annotation.VisibleForTesting;
Steven Ng391404f2021-02-17 15:58:23 +000041import androidx.recyclerview.widget.RecyclerView;
Tony Wickham9791bd12019-04-05 13:52:35 -070042
Steven Nge8d92342021-02-19 21:29:18 +000043import com.android.launcher3.DeviceProfile;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070044import com.android.launcher3.Insettable;
45import com.android.launcher3.Launcher;
46import com.android.launcher3.LauncherAppState;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070047import com.android.launcher3.R;
Sunny Goyalf3ac7032020-03-13 13:01:33 -070048import com.android.launcher3.anim.PendingAnimation;
vadimt9f48a8e2019-12-11 11:52:37 -080049import com.android.launcher3.compat.AccessibilityManagerCompat;
Sunny Goyal3661bfa2018-03-01 16:29:15 -080050import com.android.launcher3.views.RecyclerViewFastScroller;
51import com.android.launcher3.views.TopRoundedCornerView;
Steven Ng2f5648a2021-02-08 17:18:25 +000052import com.android.launcher3.widget.BaseWidgetSheet;
Yogisha Dixit741fae92021-02-22 14:03:44 +000053import com.android.launcher3.widget.LauncherAppWidgetHost.ProviderChangedListener;
Steven Ng391404f2021-02-17 15:58:23 +000054import com.android.launcher3.widget.model.WidgetsListBaseEntry;
Alina Zaidi334e6592021-03-11 16:10:27 +000055import com.android.launcher3.widget.picker.search.SearchModeListener;
56import com.android.launcher3.widget.picker.search.WidgetsSearchBar;
Steven Ng391404f2021-02-17 15:58:23 +000057import com.android.launcher3.workprofile.PersonalWorkPagedView;
58import com.android.launcher3.workprofile.PersonalWorkSlidingTabStrip.OnActivePageChangedListener;
59
Alina Zaidi2e732e92021-03-12 17:39:38 +000060import java.util.ArrayList;
Steven Ng391404f2021-02-17 15:58:23 +000061import java.util.List;
62import java.util.function.Predicate;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070063
64/**
65 * Popup for showing the full list of available widgets
66 */
67public class WidgetsFullSheet extends BaseWidgetSheet
Steven Ng167f81b2021-02-23 10:48:46 +000068 implements Insettable, ProviderChangedListener, OnActivePageChangedListener,
Alina Zaidi334e6592021-03-11 16:10:27 +000069 WidgetsRecyclerView.HeaderViewDimensionsProvider, SearchModeListener {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070070
71 private static final long DEFAULT_OPEN_DURATION = 267;
72 private static final long FADE_IN_DURATION = 150;
73 private static final float VERTICAL_START_POSITION = 0.3f;
74
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070075 private final Rect mInsets = new Rect();
Steven Ng391404f2021-02-17 15:58:23 +000076 private final boolean mHasWorkProfile;
77 private final SparseArray<AdapterHolder> mAdapters = new SparseArray();
78 private final UserHandle mCurrentUser = Process.myUserHandle();
79 private final Predicate<WidgetsListBaseEntry> mPrimaryWidgetsFilter = entry ->
80 mCurrentUser.equals(entry.mPkgItem.user);
81 private final Predicate<WidgetsListBaseEntry> mWorkWidgetsFilter =
82 mPrimaryWidgetsFilter.negate();
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070083
Steven Ng391404f2021-02-17 15:58:23 +000084 @Nullable private PersonalWorkPagedView mViewPager;
Steven Ng167f81b2021-02-23 10:48:46 +000085 private int mInitialTabsHeight = 0;
Alina Zaidi334e6592021-03-11 16:10:27 +000086 private boolean mIsInSearchMode;
Steven Ng167f81b2021-02-23 10:48:46 +000087 private View mTabsView;
Steven Ngd73d6e52021-03-09 22:44:04 +000088 private TextView mNoWidgetsView;
Steven Ng167f81b2021-02-23 10:48:46 +000089 private SearchAndRecommendationViewHolder mSearchAndRecommendationViewHolder;
90 private SearchAndRecommendationsScrollController mSearchAndRecommendationsScrollController;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070091
92 public WidgetsFullSheet(Context context, AttributeSet attrs, int defStyleAttr) {
93 super(context, attrs, defStyleAttr);
Steven Ng391404f2021-02-17 15:58:23 +000094 mHasWorkProfile = context.getSystemService(LauncherApps.class).getProfiles().size() > 1;
95 mAdapters.put(AdapterHolder.PRIMARY, new AdapterHolder(AdapterHolder.PRIMARY));
96 mAdapters.put(AdapterHolder.WORK, new AdapterHolder(AdapterHolder.WORK));
Alina Zaidi334e6592021-03-11 16:10:27 +000097 mAdapters.put(AdapterHolder.SEARCH, new AdapterHolder(AdapterHolder.SEARCH));
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -070098 }
99
100 public WidgetsFullSheet(Context context, AttributeSet attrs) {
101 this(context, attrs, 0);
102 }
103
104 @Override
105 protected void onFinishInflate() {
106 super.onFinishInflate();
107 mContent = findViewById(R.id.container);
Sunny Goyal016d7e92018-03-09 11:09:20 -0800108 TopRoundedCornerView springLayout = (TopRoundedCornerView) mContent;
Steven Ng391404f2021-02-17 15:58:23 +0000109
110 LayoutInflater layoutInflater = LayoutInflater.from(getContext());
111 int contentLayoutRes = mHasWorkProfile ? R.layout.widgets_full_sheet_paged_view
112 : R.layout.widgets_full_sheet_recyclerview;
Steven Ng167f81b2021-02-23 10:48:46 +0000113 layoutInflater.inflate(contentLayoutRes, springLayout, true);
Steven Ng391404f2021-02-17 15:58:23 +0000114
Steven Ng167f81b2021-02-23 10:48:46 +0000115 RecyclerViewFastScroller fastScroller = findViewById(R.id.fast_scroller);
Steven Ng391404f2021-02-17 15:58:23 +0000116 if (mHasWorkProfile) {
117 mViewPager = findViewById(R.id.widgets_view_pager);
Steven Ng391404f2021-02-17 15:58:23 +0000118 mViewPager.initParentViews(this);
119 mViewPager.getPageIndicator().setOnActivePageChangedListener(this);
120 mViewPager.getPageIndicator().setActiveMarker(AdapterHolder.PRIMARY);
Steven Ng167f81b2021-02-23 10:48:46 +0000121 mTabsView = findViewById(R.id.tabs);
Steven Ng391404f2021-02-17 15:58:23 +0000122 findViewById(R.id.tab_personal)
123 .setOnClickListener((View view) -> mViewPager.snapToPage(0));
124 findViewById(R.id.tab_work)
125 .setOnClickListener((View view) -> mViewPager.snapToPage(1));
Steven Ng167f81b2021-02-23 10:48:46 +0000126 fastScroller.setIsRecyclerViewFirstChildInParent(false);
Steven Ng391404f2021-02-17 15:58:23 +0000127 springLayout.addSpringView(R.id.primary_widgets_list_view);
128 springLayout.addSpringView(R.id.work_widgets_list_view);
129 } else {
130 mViewPager = null;
131 springLayout.addSpringView(R.id.primary_widgets_list_view);
132 }
133
Steven Ng167f81b2021-02-23 10:48:46 +0000134 layoutInflater.inflate(R.layout.widgets_full_sheet_search_and_recommendations, springLayout,
135 true);
136 springLayout.addSpringView(R.id.search_and_recommendations_container);
137
138 mSearchAndRecommendationViewHolder = new SearchAndRecommendationViewHolder(
139 findViewById(R.id.search_and_recommendations_container));
140 mSearchAndRecommendationsScrollController = new SearchAndRecommendationsScrollController(
141 mHasWorkProfile,
142 mSearchAndRecommendationViewHolder,
143 findViewById(R.id.primary_widgets_list_view),
144 mHasWorkProfile ? findViewById(R.id.work_widgets_list_view) : null,
Alina Zaidi334e6592021-03-11 16:10:27 +0000145 findViewById(R.id.search_widgets_list_view),
Steven Ng167f81b2021-02-23 10:48:46 +0000146 mTabsView,
147 mViewPager);
148 fastScroller.setOnFastScrollChangeListener(mSearchAndRecommendationsScrollController);
149
Steven Ngd73d6e52021-03-09 22:44:04 +0000150 mNoWidgetsView = findViewById(R.id.no_widgets_text);
151
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700152 onWidgetsBound();
Alina Zaidi334e6592021-03-11 16:10:27 +0000153
154 mSearchAndRecommendationViewHolder.mSearchBar.initialize(
155 mLauncher.getPopupDataProvider().getAllWidgets(), /* searchModeListener= */ this);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700156 }
157
Steven Ng391404f2021-02-17 15:58:23 +0000158 @Override
159 public void onActivePageChanged(int currentActivePage) {
Steven Ngd73d6e52021-03-09 22:44:04 +0000160 AdapterHolder currentAdapterHolder = mAdapters.get(currentActivePage);
Alina Zaidi334e6592021-03-11 16:10:27 +0000161 WidgetsRecyclerView currentRecyclerView =
162 mAdapters.get(currentActivePage).mWidgetsRecyclerView;
Steven Ng167f81b2021-02-23 10:48:46 +0000163
Steven Ngd73d6e52021-03-09 22:44:04 +0000164 updateNoWidgetsView(currentAdapterHolder);
Alina Zaidi334e6592021-03-11 16:10:27 +0000165 attachScrollbarToRecyclerView(currentRecyclerView);
Alina Zaidi2e732e92021-03-12 17:39:38 +0000166 resetExpandedHeaders();
Alina Zaidi334e6592021-03-11 16:10:27 +0000167 }
168
169 private void attachScrollbarToRecyclerView(WidgetsRecyclerView recyclerView) {
170 recyclerView.bindFastScrollbar();
171 mSearchAndRecommendationsScrollController.setCurrentRecyclerView(recyclerView);
Steven Ng167f81b2021-02-23 10:48:46 +0000172 reset();
173 }
174
Steven Ngd73d6e52021-03-09 22:44:04 +0000175 private void updateNoWidgetsView(AdapterHolder adapterHolder) {
176 boolean isWidgetAvailable = adapterHolder.mWidgetsListAdapter.getItemCount() > 0;
177 adapterHolder.mWidgetsRecyclerView.setVisibility(isWidgetAvailable ? VISIBLE : GONE);
178
179 // Always resets the text in case this is updated by search.
180 mNoWidgetsView.setText(R.string.no_widgets_available);
181 mNoWidgetsView.setVisibility(isWidgetAvailable ? GONE : VISIBLE);
182 }
183
Alina Zaidi2e732e92021-03-12 17:39:38 +0000184 private void updateNoSearchResultsView(boolean isVisible) {
185 mNoWidgetsView.setVisibility(isVisible ? VISIBLE : GONE);
186 if (isVisible) {
187 mNoWidgetsView.setText(R.string.no_search_results);
188 }
189 }
190
Steven Ng167f81b2021-02-23 10:48:46 +0000191 private void reset() {
192 mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView.scrollToTop();
193 if (mHasWorkProfile) {
194 mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView.scrollToTop();
195 }
Alina Zaidi334e6592021-03-11 16:10:27 +0000196 mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView.scrollToTop();
Steven Ng7f7b2f72021-03-03 14:51:36 +0000197 mSearchAndRecommendationsScrollController.reset();
Steven Ng391404f2021-02-17 15:58:23 +0000198 }
199
Sunny Goyal8b37c572020-03-31 12:12:14 -0700200 @VisibleForTesting
201 public WidgetsRecyclerView getRecyclerView() {
Alina Zaidi334e6592021-03-11 16:10:27 +0000202 if (mIsInSearchMode) {
203 return mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView;
204 }
Steven Ng391404f2021-02-17 15:58:23 +0000205 if (!mHasWorkProfile || mViewPager.getCurrentPage() == AdapterHolder.PRIMARY) {
206 return mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView;
207 }
208 return mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView;
Sunny Goyal8b37c572020-03-31 12:12:14 -0700209 }
210
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700211 @Override
Sunny Goyalde753212018-05-15 13:55:57 -0700212 protected Pair<View, String> getAccessibilityTarget() {
Steven Ng391404f2021-02-17 15:58:23 +0000213 return Pair.create(getRecyclerView(), getContext().getString(
Sunny Goyalde753212018-05-15 13:55:57 -0700214 mIsOpen ? R.string.widgets_list : R.string.widgets_list_closed));
215 }
216
217 @Override
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700218 protected void onAttachedToWindow() {
219 super.onAttachedToWindow();
220 mLauncher.getAppWidgetHost().addProviderChangeListener(this);
221 notifyWidgetProvidersChanged();
222 }
223
224 @Override
225 protected void onDetachedFromWindow() {
226 super.onDetachedFromWindow();
227 mLauncher.getAppWidgetHost().removeProviderChangeListener(this);
228 }
229
230 @Override
231 public void setInsets(Rect insets) {
232 mInsets.set(insets);
233
Steven Ng391404f2021-02-17 15:58:23 +0000234 setBottomPadding(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView, insets.bottom);
235 if (mHasWorkProfile) {
236 setBottomPadding(mAdapters.get(AdapterHolder.WORK).mWidgetsRecyclerView, insets.bottom);
237 }
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700238 if (insets.bottom > 0) {
239 setupNavBarColor();
Sunny Goyalf8d56fc2018-01-31 15:18:11 -0800240 } else {
241 clearNavBarColor();
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700242 }
Sunny Goyal3661bfa2018-03-01 16:29:15 -0800243
244 ((TopRoundedCornerView) mContent).setNavBarScrimHeight(mInsets.bottom);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700245 requestLayout();
246 }
247
Steven Ng391404f2021-02-17 15:58:23 +0000248 private void setBottomPadding(RecyclerView recyclerView, int bottomPadding) {
249 recyclerView.setPadding(
250 recyclerView.getPaddingLeft(),
251 recyclerView.getPaddingTop(),
252 recyclerView.getPaddingRight(),
253 bottomPadding);
254 }
255
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700256 @Override
257 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Steven Nge8d92342021-02-19 21:29:18 +0000258 DeviceProfile deviceProfile = mLauncher.getDeviceProfile();
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700259 int widthUsed;
260 if (mInsets.bottom > 0) {
Sunny Goyal786940a2020-06-02 02:31:31 -0700261 widthUsed = mInsets.left + mInsets.right;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700262 } else {
Steven Nge8d92342021-02-19 21:29:18 +0000263 Rect padding = deviceProfile.workspacePadding;
Sunny Goyal07b69292018-01-08 14:19:34 -0800264 widthUsed = Math.max(padding.left + padding.right,
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700265 2 * (mInsets.left + mInsets.right));
266 }
267
Steven Nge8d92342021-02-19 21:29:18 +0000268 int heightUsed = mInsets.top + deviceProfile.edgeMarginPx;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700269 measureChildWithMargins(mContent, widthMeasureSpec,
270 widthUsed, heightMeasureSpec, heightUsed);
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800271 setMeasuredDimension(MeasureSpec.getSize(widthMeasureSpec),
272 MeasureSpec.getSize(heightMeasureSpec));
Steven Nge8d92342021-02-19 21:29:18 +0000273
Steven Ng11773f42021-02-25 22:54:37 +0000274 int paddingPx = 2 * getResources().getDimensionPixelOffset(
275 R.dimen.widget_cell_horizontal_padding);
276 int maxSpansPerRow = getMeasuredWidth() / (deviceProfile.cellWidthPx + paddingPx);
Steven Nge8d92342021-02-19 21:29:18 +0000277 mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
278 maxSpansPerRow);
279 if (mHasWorkProfile) {
280 mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.setMaxHorizontalSpansPerRow(
281 maxSpansPerRow);
282 }
Steven Ng7f7b2f72021-03-03 14:51:36 +0000283
284 if (mInitialTabsHeight == 0 && mTabsView != null) {
285 mInitialTabsHeight = measureHeightWithVerticalMargins(mTabsView);
286 }
287
288 mSearchAndRecommendationsScrollController.updateMarginAndPadding();
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700289 }
290
291 @Override
292 protected void onLayout(boolean changed, int l, int t, int r, int b) {
293 int width = r - l;
294 int height = b - t;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700295
296 // Content is laid out as center bottom aligned
297 int contentWidth = mContent.getMeasuredWidth();
Sunny Goyal786940a2020-06-02 02:31:31 -0700298 int contentLeft = (width - contentWidth - mInsets.left - mInsets.right) / 2 + mInsets.left;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700299 mContent.layout(contentLeft, height - mContent.getMeasuredHeight(),
300 contentLeft + contentWidth, height);
301
302 setTranslationShift(mTranslationShift);
303 }
304
305 @Override
306 public void notifyWidgetProvidersChanged() {
307 mLauncher.refreshAndBindWidgetsForPackageUser(null);
308 }
309
310 @Override
Sunny Goyal202ae0b2019-02-04 16:26:42 -0800311 public void onWidgetsBound() {
Steven Ng391404f2021-02-17 15:58:23 +0000312 List<WidgetsListBaseEntry> allWidgets = mLauncher.getPopupDataProvider().getAllWidgets();
313
314 AdapterHolder primaryUserAdapterHolder = mAdapters.get(AdapterHolder.PRIMARY);
315 primaryUserAdapterHolder.setup(findViewById(R.id.primary_widgets_list_view));
Alina Zaidi334e6592021-03-11 16:10:27 +0000316 AdapterHolder searchAdapterHolder = mAdapters.get(AdapterHolder.SEARCH);
317 searchAdapterHolder.setup(findViewById(R.id.search_widgets_list_view));
Steven Ng391404f2021-02-17 15:58:23 +0000318 primaryUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
Steven Ngd73d6e52021-03-09 22:44:04 +0000319 updateNoWidgetsView(primaryUserAdapterHolder);
320
Steven Ng391404f2021-02-17 15:58:23 +0000321 if (mHasWorkProfile) {
322 AdapterHolder workUserAdapterHolder = mAdapters.get(AdapterHolder.WORK);
323 workUserAdapterHolder.setup(findViewById(R.id.work_widgets_list_view));
324 workUserAdapterHolder.mWidgetsListAdapter.setWidgets(allWidgets);
325 onActivePageChanged(mViewPager.getCurrentPage());
326 }
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700327 }
328
Alina Zaidi334e6592021-03-11 16:10:27 +0000329 @Override
330 public void enterSearchMode() {
331 if (mIsInSearchMode) return;
332 setViewVisibilityBasedOnSearch(/*isInSearchMode= */ true);
333 attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView);
Alina Zaidi2e732e92021-03-12 17:39:38 +0000334 resetExpandedHeaders();
Alina Zaidi334e6592021-03-11 16:10:27 +0000335 }
336
337 @Override
338 public void exitSearchMode() {
Alina Zaidi2e732e92021-03-12 17:39:38 +0000339 onSearchResults(new ArrayList<>());
Alina Zaidi334e6592021-03-11 16:10:27 +0000340 setViewVisibilityBasedOnSearch(/*isInSearchMode=*/ false);
341 if (mHasWorkProfile) {
342 mViewPager.snapToPage(AdapterHolder.PRIMARY);
343 }
344 attachScrollbarToRecyclerView(mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView);
345 }
346
347 @Override
348 public void onSearchResults(List<WidgetsListBaseEntry> entries) {
349 mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.setWidgetsOnSearch(entries);
Alina Zaidi2e732e92021-03-12 17:39:38 +0000350 updateNoSearchResultsView(
351 mAdapters.get(AdapterHolder.SEARCH).mWidgetsListAdapter.getItemCount() == 0);
Alina Zaidi334e6592021-03-11 16:10:27 +0000352 }
353
354 private void setViewVisibilityBasedOnSearch(boolean isInSearchMode) {
355 mIsInSearchMode = isInSearchMode;
356 if (mHasWorkProfile) {
357 mViewPager.setVisibility(isInSearchMode ? GONE : VISIBLE);
358 mTabsView.setVisibility(isInSearchMode ? GONE : VISIBLE);
359 } else {
360 mAdapters.get(AdapterHolder.PRIMARY).mWidgetsRecyclerView
361 .setVisibility(isInSearchMode ? GONE : VISIBLE);
362 }
363 mAdapters.get(AdapterHolder.SEARCH).mWidgetsRecyclerView
364 .setVisibility(mIsInSearchMode ? VISIBLE : GONE);
Alina Zaidi2e732e92021-03-12 17:39:38 +0000365 mNoWidgetsView.setVisibility(GONE);
366 }
367
368 private void resetExpandedHeaders() {
369 mAdapters.get(AdapterHolder.PRIMARY).mWidgetsListAdapter.resetExpandedHeader();
370 mAdapters.get(AdapterHolder.WORK).mWidgetsListAdapter.resetExpandedHeader();
Alina Zaidi334e6592021-03-11 16:10:27 +0000371 }
372
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700373 private void open(boolean animate) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700374 if (animate) {
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700375 if (getPopupContainer().getInsets().bottom > 0) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700376 mContent.setAlpha(0);
377 setTranslationShift(VERTICAL_START_POSITION);
378 }
379 mOpenCloseAnimator.setValues(
380 PropertyValuesHolder.ofFloat(TRANSLATION_SHIFT, TRANSLATION_SHIFT_OPENED));
381 mOpenCloseAnimator
382 .setDuration(DEFAULT_OPEN_DURATION)
383 .setInterpolator(AnimationUtils.loadInterpolator(
384 getContext(), android.R.interpolator.linear_out_slow_in));
385 mOpenCloseAnimator.addListener(new AnimatorListenerAdapter() {
386 @Override
387 public void onAnimationEnd(Animator animation) {
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700388 mOpenCloseAnimator.removeListener(this);
389 }
390 });
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800391 post(() -> {
Sunny Goyal6639a5d2018-02-28 15:09:36 -0800392 mOpenCloseAnimator.start();
393 mContent.animate().alpha(1).setDuration(FADE_IN_DURATION);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700394 });
395 } else {
396 setTranslationShift(TRANSLATION_SHIFT_OPENED);
Sunny Goyalde753212018-05-15 13:55:57 -0700397 post(this::announceAccessibilityChanges);
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700398 }
399 }
400
401 @Override
402 protected void handleClose(boolean animate) {
403 handleClose(animate, DEFAULT_OPEN_DURATION);
404 }
405
406 @Override
407 protected boolean isOfType(int type) {
408 return (type & TYPE_WIDGETS_FULL_SHEET) != 0;
409 }
410
411 @Override
412 public boolean onControllerInterceptTouchEvent(MotionEvent ev) {
413 // Disable swipe down when recycler view is scrolling
414 if (ev.getAction() == MotionEvent.ACTION_DOWN) {
415 mNoIntercept = false;
Steven Ng391404f2021-02-17 15:58:23 +0000416 RecyclerViewFastScroller scroller = getRecyclerView().getScrollbar();
Steven Ng2f5648a2021-02-08 17:18:25 +0000417 if (scroller.getThumbOffsetY() >= 0
418 && getPopupContainer().isEventOverView(scroller, ev)) {
Sunny Goyal3661bfa2018-03-01 16:29:15 -0800419 mNoIntercept = true;
Sunny Goyaldb6cdb02018-07-13 11:03:04 -0700420 } else if (getPopupContainer().isEventOverView(mContent, ev)) {
Steven Ng391404f2021-02-17 15:58:23 +0000421 mNoIntercept = !getRecyclerView().shouldContainerScroll(ev, getPopupContainer());
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700422 }
423 }
424 return super.onControllerInterceptTouchEvent(ev);
425 }
426
Steven Ng2f5648a2021-02-08 17:18:25 +0000427 /** Shows the {@link WidgetsFullSheet} on the launcher. */
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700428 public static WidgetsFullSheet show(Launcher launcher, boolean animate) {
429 WidgetsFullSheet sheet = (WidgetsFullSheet) launcher.getLayoutInflater()
430 .inflate(R.layout.widgets_full_sheet, launcher.getDragLayer(), false);
Sunny Goyal1642f712018-09-18 11:40:35 -0700431 sheet.attachToContainer();
Sunny Goyalde753212018-05-15 13:55:57 -0700432 sheet.mIsOpen = true;
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700433 sheet.open(animate);
434 return sheet;
435 }
Sunny Goyalaeb16432017-10-16 11:46:41 -0700436
Steven Ng2f5648a2021-02-08 17:18:25 +0000437 /** Gets the {@link WidgetsRecyclerView} which shows all widgets in {@link WidgetsFullSheet}. */
Vadim Tryshev2ce6a132018-06-18 19:14:44 -0700438 @VisibleForTesting
439 public static WidgetsRecyclerView getWidgetsView(Launcher launcher) {
Steven Ng391404f2021-02-17 15:58:23 +0000440 return launcher.findViewById(R.id.primary_widgets_list_view);
Vadim Tryshev2ce6a132018-06-18 19:14:44 -0700441 }
442
Sunny Goyalaeb16432017-10-16 11:46:41 -0700443 @Override
Sunny Goyalf3ac7032020-03-13 13:01:33 -0700444 public void addHintCloseAnim(
445 float distanceToMove, Interpolator interpolator, PendingAnimation target) {
Steven Ng391404f2021-02-17 15:58:23 +0000446 target.setFloat(getRecyclerView(), VIEW_TRANSLATE_Y, -distanceToMove, interpolator);
447 target.setViewAlpha(getRecyclerView(), 0.5f, interpolator);
Tony Wickham9791bd12019-04-05 13:52:35 -0700448 }
vadimt9f48a8e2019-12-11 11:52:37 -0800449
450 @Override
451 protected void onCloseComplete() {
452 super.onCloseComplete();
453 AccessibilityManagerCompat.sendStateEventToTest(getContext(), NORMAL_STATE_ORDINAL);
454 }
Steven Ng391404f2021-02-17 15:58:23 +0000455
Steven Ng167f81b2021-02-23 10:48:46 +0000456 @Override
457 public int getHeaderViewHeight() {
Steven Ng53d13642021-03-10 22:40:55 +0000458 return measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mCollapseHandle)
459 + measureHeightWithVerticalMargins(mSearchAndRecommendationViewHolder.mHeaderTitle)
Alina Zaidi16327522021-03-15 07:00:49 +0000460 + measureHeightWithVerticalMargins(
461 (View) mSearchAndRecommendationViewHolder.mSearchBar);
Steven Ng7f7b2f72021-03-03 14:51:36 +0000462 }
463
464 /** private the height, in pixel, + the vertical margins of a given view. */
465 private static int measureHeightWithVerticalMargins(View view) {
Steven Ng53d13642021-03-10 22:40:55 +0000466 if (view.getVisibility() != VISIBLE) {
467 return 0;
468 }
Steven Ng7f7b2f72021-03-03 14:51:36 +0000469 MarginLayoutParams marginLayoutParams = (MarginLayoutParams) view.getLayoutParams();
470 return view.getMeasuredHeight() + marginLayoutParams.bottomMargin
471 + marginLayoutParams.topMargin;
Steven Ng167f81b2021-02-23 10:48:46 +0000472 }
473
Steven Ng391404f2021-02-17 15:58:23 +0000474 /** A holder class for holding adapters & their corresponding recycler view. */
475 private final class AdapterHolder {
476 static final int PRIMARY = 0;
477 static final int WORK = 1;
Alina Zaidi334e6592021-03-11 16:10:27 +0000478 static final int SEARCH = 2;
Steven Ng391404f2021-02-17 15:58:23 +0000479
480 private final int mAdapterType;
481 private final WidgetsListAdapter mWidgetsListAdapter;
482
483 private WidgetsRecyclerView mWidgetsRecyclerView;
484
485 AdapterHolder(int adapterType) {
486 mAdapterType = adapterType;
487
488 Context context = getContext();
489 LauncherAppState apps = LauncherAppState.getInstance(context);
490 mWidgetsListAdapter = new WidgetsListAdapter(
491 context,
492 LayoutInflater.from(context),
493 apps.getWidgetCache(),
494 apps.getIconCache(),
495 /* iconClickListener= */ WidgetsFullSheet.this,
496 /* iconLongClickListener= */ WidgetsFullSheet.this);
Alina Zaidi334e6592021-03-11 16:10:27 +0000497 switch (mAdapterType) {
498 case PRIMARY:
499 mWidgetsListAdapter.setFilter(mPrimaryWidgetsFilter);
500 break;
501 case WORK:
502 mWidgetsListAdapter.setFilter(mWorkWidgetsFilter);
503 break;
504 default:
505 break;
506 }
Steven Ng391404f2021-02-17 15:58:23 +0000507 }
508
509 void setup(WidgetsRecyclerView recyclerView) {
510 mWidgetsRecyclerView = recyclerView;
511 mWidgetsRecyclerView.setAdapter(mWidgetsListAdapter);
Steven Ng167f81b2021-02-23 10:48:46 +0000512 mWidgetsRecyclerView.setHeaderViewDimensionsProvider(WidgetsFullSheet.this);
Steven Ng391404f2021-02-17 15:58:23 +0000513 mWidgetsRecyclerView.setEdgeEffectFactory(
514 ((TopRoundedCornerView) mContent).createEdgeEffectFactory());
515 mWidgetsListAdapter.setApplyBitmapDeferred(false, mWidgetsRecyclerView);
516 }
517 }
Steven Ng167f81b2021-02-23 10:48:46 +0000518
519 final class SearchAndRecommendationViewHolder {
520 final View mContainer;
521 final View mCollapseHandle;
Alina Zaidi334e6592021-03-11 16:10:27 +0000522 final WidgetsSearchBar mSearchBar;
Steven Ng167f81b2021-02-23 10:48:46 +0000523 final TextView mHeaderTitle;
524
525 SearchAndRecommendationViewHolder(View searchAndRecommendationContainer) {
526 mContainer = searchAndRecommendationContainer;
527 mCollapseHandle = mContainer.findViewById(R.id.collapse_handle);
528 mSearchBar = mContainer.findViewById(R.id.widgets_search_bar);
529 mHeaderTitle = mContainer.findViewById(R.id.title);
530 }
531 }
Sunny Goyalf1fbc3f2017-10-10 15:21:15 -0700532}