blob: 65c381953ecfef547f57efe486562fb8cd9491e2 [file] [log] [blame]
Chuck Liao1e0501f2020-02-17 18:20:54 +08001/*
2 * Copyright (C) 2020 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 */
16package com.android.wallpaper.picker;
17
18import android.app.Activity;
Chuck Liaof646e052020-09-24 12:24:59 +080019import android.app.AlertDialog;
Chuck Liao1e0501f2020-02-17 18:20:54 +080020import android.content.Intent;
21import android.graphics.Point;
22import android.graphics.PorterDuff;
23import android.graphics.Rect;
24import android.net.Uri;
25import android.os.Bundle;
26import android.provider.Settings;
27import android.util.DisplayMetrics;
28import android.util.Log;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.widget.ImageView;
33import android.widget.ProgressBar;
34import android.widget.TextView;
35
36import androidx.annotation.NonNull;
37import androidx.annotation.Nullable;
Chuck Liao1e0501f2020-02-17 18:20:54 +080038import androidx.cardview.widget.CardView;
39import androidx.fragment.app.Fragment;
40import androidx.recyclerview.widget.GridLayoutManager;
41import androidx.recyclerview.widget.RecyclerView;
42
43import com.android.wallpaper.R;
44import com.android.wallpaper.asset.Asset;
45import com.android.wallpaper.model.Category;
Chuck Liaoddf2b522021-04-15 00:36:25 +080046import com.android.wallpaper.model.CategoryProvider;
Chuck Liao1e0501f2020-02-17 18:20:54 +080047import com.android.wallpaper.module.InjectorProvider;
48import com.android.wallpaper.module.UserEventLogger;
Chuck Liaof6b4b192020-08-07 02:31:32 +080049import com.android.wallpaper.util.DeepLinkUtils;
Chuck Liao1e0501f2020-02-17 18:20:54 +080050import com.android.wallpaper.util.DisplayMetricsRetriever;
Tianguang Zhangd3e4f632021-04-14 00:30:15 +020051import com.android.wallpaper.util.ResourceUtils;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070052import com.android.wallpaper.util.SizeCalculator;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080053import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
54import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Chuck Liao1e0501f2020-02-17 18:20:54 +080055
56import com.bumptech.glide.Glide;
57
58import java.util.ArrayList;
59import java.util.List;
60
61/**
62 * Displays the UI which contains the categories of the wallpaper.
63 */
Chuck Liao58aca1c2021-03-17 01:20:55 +080064public class CategorySelectorFragment extends AppbarFragment {
Chuck Liao1e0501f2020-02-17 18:20:54 +080065
66 // The number of ViewHolders that don't pertain to category tiles.
67 // Currently 2: one for the metadata section and one for the "Select wallpaper" header.
68 private static final int NUM_NON_CATEGORY_VIEW_HOLDERS = 0;
69 private static final int SETTINGS_APP_INFO_REQUEST_CODE = 1;
70 private static final String TAG = "CategorySelectorFragment";
71
72 /**
73 * Interface to be implemented by an Fragment hosting a {@link CategorySelectorFragment}
74 */
75 public interface CategorySelectorFragmentHost {
76
77 /**
78 * Requests to show the Android custom photo picker for the sake of picking a photo
79 * to set as the device's wallpaper.
80 */
81 void requestCustomPhotoPicker(MyPhotosStarter.PermissionChangedListener listener);
82
83 /**
84 * Shows the wallpaper page of the specific category.
85 *
Chuck Liaoa63f1bf2020-06-11 01:35:42 +080086 * @param category the wallpaper's {@link Category}
Chuck Liao1e0501f2020-02-17 18:20:54 +080087 */
Chuck Liaoa63f1bf2020-06-11 01:35:42 +080088 void show(Category category);
Chuck Liao0088bca2020-05-28 16:03:23 +080089
Chuck Liao58aca1c2021-03-17 01:20:55 +080090
Chuck Liao0088bca2020-05-28 16:03:23 +080091 /**
Chuck Liao58aca1c2021-03-17 01:20:55 +080092 * Indicates if the host has toolbar to show the title. If it does, we should set the title
93 * there.
94 */
95 boolean isHostToolbarShown();
96
97 /**
98 * Sets the title in the host's toolbar.
Chuck Liao0088bca2020-05-28 16:03:23 +080099 */
100 void setToolbarTitle(CharSequence title);
Chuck Liaof6b4b192020-08-07 02:31:32 +0800101
102 /**
103 * Fetches the wallpaper categories.
104 */
105 void fetchCategories();
Chuck Liao81145b22020-09-03 09:52:25 +0800106
107 /**
Chuck Liao3abf15b2020-12-17 22:33:02 +0800108 * Cleans up the listeners which will be notified when there's a package event.
109 */
110 void cleanUp();
Chuck Liao1e0501f2020-02-17 18:20:54 +0800111 }
112
Chuck Liaoddf2b522021-04-15 00:36:25 +0800113 private final CategoryProvider mCategoryProvider;
114
Chuck Liao1e0501f2020-02-17 18:20:54 +0800115 private RecyclerView mImageGrid;
116 private CategoryAdapter mAdapter;
117 private ArrayList<Category> mCategories = new ArrayList<>();
118 private Point mTileSizePx;
119 private boolean mAwaitingCategories;
120
121 public CategorySelectorFragment() {
122 mAdapter = new CategoryAdapter(mCategories);
Chuck Liaoddf2b522021-04-15 00:36:25 +0800123 mCategoryProvider = InjectorProvider.getInjector().getCategoryProvider(getContext());
Chuck Liao1e0501f2020-02-17 18:20:54 +0800124 }
125
126 @Nullable
127 @Override
128 public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container,
129 @Nullable Bundle savedInstanceState) {
130 View view = inflater.inflate(R.layout.fragment_category_selector, container,
131 /* attachToRoot= */ false);
Chuck Liao1e0501f2020-02-17 18:20:54 +0800132 mImageGrid = view.findViewById(R.id.category_grid);
Chuck Liao1e6cb682021-04-22 00:51:11 +0800133 mImageGrid.addItemDecoration(new GridPaddingDecoration(getResources().getDimensionPixelSize(
134 R.dimen.grid_item_category_padding_horizontal)));
Chuck Liao1e0501f2020-02-17 18:20:54 +0800135
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700136 mTileSizePx = SizeCalculator.getCategoryTileSize(getActivity());
Chuck Liao1e0501f2020-02-17 18:20:54 +0800137
138 mImageGrid.setAdapter(mAdapter);
139
Chuck Liaoddf2b522021-04-15 00:36:25 +0800140 GridLayoutManager gridLayoutManager = new GridLayoutManager(getActivity(),
141 getNumColumns() * CategorySpanSizeLookup.DEFAULT_CATEGORY_SPAN_SIZE);
142 gridLayoutManager.setSpanSizeLookup(new CategorySpanSizeLookup(mAdapter));
Chuck Liao1e0501f2020-02-17 18:20:54 +0800143 mImageGrid.setLayoutManager(gridLayoutManager);
Wesley.CW Wang27ff4262020-06-12 19:19:57 +0800144 mImageGrid.setAccessibilityDelegateCompat(
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +0800145 new WallpaperPickerRecyclerViewAccessibilityDelegate(
146 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Chuck Liao58aca1c2021-03-17 01:20:55 +0800147
148 if (getCategorySelectorFragmentHost().isHostToolbarShown()) {
149 view.findViewById(R.id.header_bar).setVisibility(View.GONE);
150 getCategorySelectorFragmentHost().setToolbarTitle(getText(R.string.wallpaper_title));
151 } else {
152 setUpToolbar(view);
153 setTitle(getText(R.string.wallpaper_title));
154 }
Chuck Liao1e0501f2020-02-17 18:20:54 +0800155
Chuck Liaof6b4b192020-08-07 02:31:32 +0800156 if (!DeepLinkUtils.isDeepLink(getActivity().getIntent())) {
157 getCategorySelectorFragmentHost().fetchCategories();
158 }
159
Chihhang Chuange6c73222021-04-14 22:36:41 +0800160 // For nav bar edge-to-edge effect.
161 view.findViewById(R.id.category_grid).setOnApplyWindowInsetsListener((v, windowInsets) -> {
162 v.setPadding(
163 v.getPaddingLeft(),
164 v.getPaddingTop(),
165 v.getPaddingRight(),
166 windowInsets.getSystemWindowInsetBottom());
167 return windowInsets.consumeSystemWindowInsets();
168 });
Chuck Liao1e0501f2020-02-17 18:20:54 +0800169 return view;
170 }
171
Chuck Liao3abf15b2020-12-17 22:33:02 +0800172 @Override
173 public void onDestroyView() {
174 getCategorySelectorFragmentHost().cleanUp();
175 super.onDestroyView();
176 }
177
Chuck Liao1e0501f2020-02-17 18:20:54 +0800178 /**
179 * Inserts the given category into the categories list in priority order.
180 */
181 void addCategory(Category category, boolean loading) {
182 // If not previously waiting for categories, enter the waiting state by showing the loading
183 // indicator.
184 if (loading && !mAwaitingCategories) {
185 mAdapter.notifyItemChanged(getNumColumns());
186 mAdapter.notifyItemInserted(getNumColumns());
187 mAwaitingCategories = true;
188 }
189 // Not add existing category to category list
190 if (mCategories.indexOf(category) >= 0) {
191 updateCategory(category);
192 return;
193 }
194
195 int priority = category.getPriority();
196
197 int index = 0;
198 while (index < mCategories.size() && priority >= mCategories.get(index).getPriority()) {
199 index++;
200 }
201
202 mCategories.add(index, category);
203 if (mAdapter != null) {
204 // Offset the index because of the static metadata element at beginning of RecyclerView.
205 mAdapter.notifyItemInserted(index + NUM_NON_CATEGORY_VIEW_HOLDERS);
206 }
207 }
208
209 void removeCategory(Category category) {
210 int index = mCategories.indexOf(category);
211 if (index >= 0) {
212 mCategories.remove(index);
213 mAdapter.notifyItemRemoved(index + NUM_NON_CATEGORY_VIEW_HOLDERS);
214 }
215 }
216
217 void updateCategory(Category category) {
218 int index = mCategories.indexOf(category);
219 if (index >= 0) {
220 mCategories.remove(index);
221 mCategories.add(index, category);
222 mAdapter.notifyItemChanged(index + NUM_NON_CATEGORY_VIEW_HOLDERS);
223 }
224 }
225
226 void clearCategories() {
227 mCategories.clear();
228 mAdapter.notifyDataSetChanged();
229 }
230
231 /**
232 * Notifies the CategoryFragment that no further categories are expected so it may hide
233 * the loading indicator.
234 */
235 void doneFetchingCategories() {
236 if (mAwaitingCategories) {
237 mAdapter.notifyItemRemoved(mAdapter.getItemCount() - 1);
238 mAwaitingCategories = false;
239 }
240 }
241
242 void notifyDataSetChanged() {
243 mAdapter.notifyDataSetChanged();
244 }
245
246 private int getNumColumns() {
247 Activity activity = getActivity();
Chuck Liaoe2fe0302020-06-29 21:15:35 +0800248 return activity == null ? 1 : SizeCalculator.getNumCategoryColumns(activity);
Chuck Liao1e0501f2020-02-17 18:20:54 +0800249 }
250
251
252 private CategorySelectorFragmentHost getCategorySelectorFragmentHost() {
Chuck Liao8cbe49f2021-03-15 20:28:04 +0800253 Fragment parentFragment = getParentFragment();
254 if (parentFragment != null) {
255 return (CategorySelectorFragmentHost) parentFragment;
256 } else {
257 return (CategorySelectorFragmentHost) getActivity();
258 }
Chuck Liao1e0501f2020-02-17 18:20:54 +0800259 }
260
261 /**
262 * ViewHolder subclass for a category tile in the RecyclerView.
263 */
264 private class CategoryHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
265 private Category mCategory;
266 private ImageView mImageView;
267 private ImageView mOverlayIconView;
268 private TextView mTitleView;
269
270 CategoryHolder(View itemView) {
271 super(itemView);
272 itemView.setOnClickListener(this);
273
274 mImageView = itemView.findViewById(R.id.image);
275 mOverlayIconView = itemView.findViewById(R.id.overlay_icon);
276 mTitleView = itemView.findViewById(R.id.category_title);
277
278 CardView categoryView = itemView.findViewById(R.id.category);
279 categoryView.getLayoutParams().height = mTileSizePx.y;
280 }
281
282 @Override
283 public void onClick(View view) {
284 final UserEventLogger eventLogger =
285 InjectorProvider.getInjector().getUserEventLogger(getActivity());
286 eventLogger.logCategorySelected(mCategory.getCollectionId());
287
288 if (mCategory.supportsCustomPhotos()) {
289 getCategorySelectorFragmentHost().requestCustomPhotoPicker(
290 new MyPhotosStarter.PermissionChangedListener() {
291 @Override
292 public void onPermissionsGranted() {
293 drawThumbnailAndOverlayIcon();
294 }
295
296 @Override
297 public void onPermissionsDenied(boolean dontAskAgain) {
298 // No-op
299 }
300 });
301 return;
302 }
303
Chuck Liaoa63f1bf2020-06-11 01:35:42 +0800304 getCategorySelectorFragmentHost().show(mCategory);
Chuck Liao1e0501f2020-02-17 18:20:54 +0800305 }
306
307 /**
308 * Binds the given category to this CategoryHolder.
309 */
310 private void bindCategory(Category category) {
311 mCategory = category;
312 mTitleView.setText(category.getTitle());
313 drawThumbnailAndOverlayIcon();
314 }
315
316 /**
317 * Draws the CategoryHolder's thumbnail and overlay icon.
318 */
319 private void drawThumbnailAndOverlayIcon() {
320 mOverlayIconView.setImageDrawable(mCategory.getOverlayIcon(
321 getActivity().getApplicationContext()));
322
323 // Size the overlay icon according to the category.
324 int overlayIconDimenDp = mCategory.getOverlayIconSizeDp();
325 DisplayMetrics metrics = DisplayMetricsRetriever.getInstance().getDisplayMetrics(
326 getResources(), getActivity().getWindowManager().getDefaultDisplay());
327 int overlayIconDimenPx = (int) (overlayIconDimenDp * metrics.density);
328 mOverlayIconView.getLayoutParams().width = overlayIconDimenPx;
329 mOverlayIconView.getLayoutParams().height = overlayIconDimenPx;
330
331 Asset thumbnail = mCategory.getThumbnail(getActivity().getApplicationContext());
332 if (thumbnail != null) {
333 thumbnail.loadDrawable(getActivity(), mImageView,
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200334 ResourceUtils.getColorAttr(
335 getActivity(),
336 android.R.attr.colorSecondary
337 ));
Chuck Liao1e0501f2020-02-17 18:20:54 +0800338 } else {
339 // TODO(orenb): Replace this workaround for b/62584914 with a proper way of
340 // unloading the ImageView such that no incorrect image is improperly loaded upon
341 // rapid scroll.
342 Object nullObj = null;
343 Glide.with(getActivity())
344 .asDrawable()
345 .load(nullObj)
346 .into(mImageView);
347
348 }
349 }
350 }
351
Chuck Liaoddf2b522021-04-15 00:36:25 +0800352 private class FeaturedCategoryHolder extends CategoryHolder {
353
354 FeaturedCategoryHolder(View itemView) {
355 super(itemView);
356 CardView categoryView = itemView.findViewById(R.id.category);
357 categoryView.getLayoutParams().height =
358 SizeCalculator.getFeaturedCategoryTileSize(getActivity()).y;
359 }
360 }
361
362 private class MyPhotosCategoryHolder extends CategoryHolder {
363
364 MyPhotosCategoryHolder(View itemView) {
365 super(itemView);
366 // Reuse the height of featured category since My Photos category & featured category
367 // have the same height in current UI design.
368 CardView categoryView = itemView.findViewById(R.id.category);
Tianguang Zhangbae57642021-04-19 19:46:33 +0200369 int height = SizeCalculator.getFeaturedCategoryTileSize(getActivity()).y;
370 categoryView.getLayoutParams().height = height;
371 // Use the height as the card corner radius for the "My photos" category
372 // for a stadium border.
373 categoryView.setRadius(height);
Chuck Liaoddf2b522021-04-15 00:36:25 +0800374 }
375 }
376
Chuck Liao1e0501f2020-02-17 18:20:54 +0800377 /**
378 * ViewHolder subclass for the loading indicator ("spinner") shown when categories are being
379 * fetched.
380 */
381 private class LoadingIndicatorHolder extends RecyclerView.ViewHolder {
382 private LoadingIndicatorHolder(View view) {
383 super(view);
384 ProgressBar progressBar = view.findViewById(R.id.loading_indicator);
385 progressBar.getIndeterminateDrawable().setColorFilter(
Tianguang Zhangd3e4f632021-04-14 00:30:15 +0200386 ResourceUtils.getColorAttr(
387 getActivity(),
388 android.R.attr.colorAccent
389 ), PorterDuff.Mode.SRC_IN);
Chuck Liao1e0501f2020-02-17 18:20:54 +0800390 }
391 }
392
393 /**
394 * RecyclerView Adapter subclass for the category tiles in the RecyclerView.
395 */
396 private class CategoryAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
397 implements MyPhotosStarter.PermissionChangedListener {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800398 private static final int ITEM_VIEW_TYPE_MY_PHOTOS = 1;
399 private static final int ITEM_VIEW_TYPE_FEATURED_CATEGORY = 2;
Chuck Liao1e0501f2020-02-17 18:20:54 +0800400 private static final int ITEM_VIEW_TYPE_CATEGORY = 3;
401 private static final int ITEM_VIEW_TYPE_LOADING_INDICATOR = 4;
402 private List<Category> mCategories;
403
404 private CategoryAdapter(List<Category> categories) {
405 mCategories = categories;
406 }
407
408 @Override
409 public int getItemViewType(int position) {
410 if (mAwaitingCategories && position == getItemCount() - 1) {
411 return ITEM_VIEW_TYPE_LOADING_INDICATOR;
412 }
413
Chuck Liaoddf2b522021-04-15 00:36:25 +0800414 if (position == 0) {
415 return ITEM_VIEW_TYPE_MY_PHOTOS;
416 }
417
418 if (mCategoryProvider.isFeaturedCategory(mCategories.get(position))) {
419 return ITEM_VIEW_TYPE_FEATURED_CATEGORY;
420 }
421
Chuck Liao1e0501f2020-02-17 18:20:54 +0800422 return ITEM_VIEW_TYPE_CATEGORY;
423 }
424
425 @Override
426 public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
427 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
428 View view;
429
430 switch (viewType) {
431 case ITEM_VIEW_TYPE_LOADING_INDICATOR:
432 view = layoutInflater.inflate(R.layout.grid_item_loading_indicator,
433 parent, /* attachToRoot= */ false);
434 return new LoadingIndicatorHolder(view);
Chuck Liaoddf2b522021-04-15 00:36:25 +0800435 case ITEM_VIEW_TYPE_MY_PHOTOS:
436 view = layoutInflater.inflate(R.layout.grid_item_category,
437 parent, /* attachToRoot= */ false);
438 return new MyPhotosCategoryHolder(view);
439 case ITEM_VIEW_TYPE_FEATURED_CATEGORY:
440 view = layoutInflater.inflate(R.layout.grid_item_category,
441 parent, /* attachToRoot= */ false);
442 return new FeaturedCategoryHolder(view);
Chuck Liao1e0501f2020-02-17 18:20:54 +0800443 case ITEM_VIEW_TYPE_CATEGORY:
444 view = layoutInflater.inflate(R.layout.grid_item_category,
445 parent, /* attachToRoot= */ false);
446 return new CategoryHolder(view);
447 default:
448 Log.e(TAG, "Unsupported viewType " + viewType + " in CategoryAdapter");
449 return null;
450 }
451 }
452
453 @Override
454 public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
455 int viewType = getItemViewType(position);
456
457 switch (viewType) {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800458 case ITEM_VIEW_TYPE_MY_PHOTOS:
459 case ITEM_VIEW_TYPE_FEATURED_CATEGORY:
Chuck Liao1e0501f2020-02-17 18:20:54 +0800460 case ITEM_VIEW_TYPE_CATEGORY:
461 // Offset position to get category index to account for the non-category view
462 // holders.
463 Category category = mCategories.get(position - NUM_NON_CATEGORY_VIEW_HOLDERS);
464 ((CategoryHolder) holder).bindCategory(category);
465 break;
466 case ITEM_VIEW_TYPE_LOADING_INDICATOR:
467 // No op.
468 break;
469 default:
470 Log.e(TAG, "Unsupported viewType " + viewType + " in CategoryAdapter");
471 }
472 }
473
474 @Override
475 public int getItemCount() {
476 // Add to size of categories to account for the metadata related views.
477 // Add 1 more for the loading indicator if not yet done loading.
478 int size = mCategories.size() + NUM_NON_CATEGORY_VIEW_HOLDERS;
479 if (mAwaitingCategories) {
480 size += 1;
481 }
482
483 return size;
484 }
485
486 @Override
487 public void onPermissionsGranted() {
488 notifyDataSetChanged();
489 }
490
491 @Override
492 public void onPermissionsDenied(boolean dontAskAgain) {
493 if (!dontAskAgain) {
494 return;
495 }
496
497 String permissionNeededMessage =
498 getString(R.string.permission_needed_explanation_go_to_settings);
499 AlertDialog dialog = new AlertDialog.Builder(getActivity(), R.style.LightDialogTheme)
500 .setMessage(permissionNeededMessage)
501 .setPositiveButton(android.R.string.ok, null /* onClickListener */)
502 .setNegativeButton(
503 R.string.settings_button_label,
504 (dialogInterface, i) -> {
505 Intent appInfoIntent =
506 new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
507 Uri uri = Uri.fromParts("package",
508 getActivity().getPackageName(), /* fragment= */ null);
509 appInfoIntent.setData(uri);
510 startActivityForResult(
511 appInfoIntent, SETTINGS_APP_INFO_REQUEST_CODE);
512 })
513 .create();
514 dialog.show();
515 }
516 }
517
518 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
519
Chuck Liao1e6cb682021-04-22 00:51:11 +0800520 private final int mPadding;
Chuck Liao1e0501f2020-02-17 18:20:54 +0800521
522 GridPaddingDecoration(int padding) {
523 mPadding = padding;
524 }
525
526 @Override
527 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
528 RecyclerView.State state) {
529 int position = parent.getChildAdapterPosition(view) - NUM_NON_CATEGORY_VIEW_HOLDERS;
530 if (position >= 0) {
531 outRect.left = mPadding;
532 outRect.right = mPadding;
533 }
Chuck Liao1e6cb682021-04-22 00:51:11 +0800534
535 RecyclerView.ViewHolder viewHolder = parent.getChildViewHolder(view);
536 if (viewHolder instanceof MyPhotosCategoryHolder
537 || viewHolder instanceof FeaturedCategoryHolder) {
538 outRect.bottom = getResources().getDimensionPixelSize(
539 R.dimen.grid_item_featured_category_padding_bottom);
540 } else {
541 outRect.bottom = getResources().getDimensionPixelSize(
542 R.dimen.grid_item_category_padding_bottom);
543 }
Chuck Liao1e0501f2020-02-17 18:20:54 +0800544 }
545 }
546
547 /**
548 * SpanSizeLookup subclass which provides that the item in the first position spans the number
549 * of columns in the RecyclerView and all other items only take up a single span.
550 */
551 private class CategorySpanSizeLookup extends GridLayoutManager.SpanSizeLookup {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800552 private static final int DEFAULT_CATEGORY_SPAN_SIZE = 2;
553
Chuck Liao1e0501f2020-02-17 18:20:54 +0800554 CategoryAdapter mAdapter;
555
556 private CategorySpanSizeLookup(CategoryAdapter adapter) {
557 mAdapter = adapter;
558 }
559
560 @Override
561 public int getSpanSize(int position) {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800562 if (position < NUM_NON_CATEGORY_VIEW_HOLDERS || mAdapter.getItemViewType(position)
563 == CategoryAdapter.ITEM_VIEW_TYPE_LOADING_INDICATOR || mAdapter.getItemViewType(
564 position) == CategoryAdapter.ITEM_VIEW_TYPE_MY_PHOTOS) {
565 return getNumColumns() * DEFAULT_CATEGORY_SPAN_SIZE;
Chuck Liao1e0501f2020-02-17 18:20:54 +0800566 }
567
Chuck Liaoddf2b522021-04-15 00:36:25 +0800568 if (mAdapter.getItemViewType(position)
569 == CategoryAdapter.ITEM_VIEW_TYPE_FEATURED_CATEGORY) {
570 return getNumColumns() * DEFAULT_CATEGORY_SPAN_SIZE / 2;
571 }
572
573 return DEFAULT_CATEGORY_SPAN_SIZE;
Chuck Liao1e0501f2020-02-17 18:20:54 +0800574 }
575 }
Chuck Liao1e0501f2020-02-17 18:20:54 +0800576}