blob: 0925f391f18cc2aa3a4e3e4c618bfe6148150079 [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
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 */
16package com.android.wallpaper.picker.individual;
17
Chuck Liao7dc21572020-05-14 18:37:36 +080018import static com.android.wallpaper.picker.WallpaperPickerDelegate.PREVIEW_WALLPAPER_REQUEST_CODE;
Chuck Liao69630f12020-03-05 19:01:25 +080019import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
Chuck Liao7dc21572020-05-14 18:37:36 +080020import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
chihhangchuang22aa0cc2020-03-25 19:12:42 +080021import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
Chuck Liao8ec38e02020-02-26 20:59:32 +080022import static com.android.wallpaper.widget.BottomActionBar.BottomAction.ROTATION;
23
Jon Miranda16ea1b12017-12-12 14:52:48 -080024import android.app.Activity;
25import android.app.ProgressDialog;
“Chuck7ef99722020-03-22 04:34:03 +080026import android.app.WallpaperManager;
Jon Miranda16ea1b12017-12-12 14:52:48 -080027import android.content.Context;
Chihhang Chuang85f099a2020-06-16 18:04:39 +080028import android.content.DialogInterface;
chihhangchuangc687d912020-05-04 14:33:05 +080029import android.content.Intent;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070030import android.content.res.Configuration;
Jon Miranda16ea1b12017-12-12 14:52:48 -080031import android.content.res.Resources.NotFoundException;
32import android.graphics.Point;
Chuck Liaoba401232020-03-13 20:11:04 +080033import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080034import android.os.Build.VERSION;
35import android.os.Build.VERSION_CODES;
36import android.os.Bundle;
37import android.os.Handler;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070038import android.service.wallpaper.WallpaperService;
Jon Miranda16ea1b12017-12-12 14:52:48 -080039import android.util.Log;
40import android.view.LayoutInflater;
41import android.view.View;
42import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080043import android.widget.Toast;
44
Santiago Etchebehereb1854472019-06-06 17:44:54 -070045import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080046import androidx.annotation.Nullable;
Chuck Liaof6b4b192020-08-07 02:31:32 +080047import androidx.core.widget.ContentLoadingProgressBar;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080048import androidx.fragment.app.DialogFragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080049import androidx.recyclerview.widget.GridLayoutManager;
50import androidx.recyclerview.widget.RecyclerView;
51import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
52import androidx.recyclerview.widget.RecyclerView.ViewHolder;
53
Jon Miranda16ea1b12017-12-12 14:52:48 -080054import com.android.wallpaper.R;
55import com.android.wallpaper.asset.Asset;
56import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070057import com.android.wallpaper.model.Category;
58import com.android.wallpaper.model.CategoryProvider;
59import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080060import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080061import com.android.wallpaper.model.WallpaperCategory;
62import com.android.wallpaper.model.WallpaperInfo;
63import com.android.wallpaper.model.WallpaperReceiver;
64import com.android.wallpaper.model.WallpaperRotationInitializer;
65import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
66import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080067import com.android.wallpaper.module.FormFactorChecker;
68import com.android.wallpaper.module.FormFactorChecker.FormFactor;
69import com.android.wallpaper.module.Injector;
70import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070071import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080072import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080073import com.android.wallpaper.module.WallpaperChangedNotifier;
74import com.android.wallpaper.module.WallpaperPersister;
75import com.android.wallpaper.module.WallpaperPersister.Destination;
76import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080077import com.android.wallpaper.module.WallpaperSetter;
Jon Miranda16ea1b12017-12-12 14:52:48 -080078import com.android.wallpaper.picker.BaseActivity;
chihhangchuang3efb6832020-04-17 02:06:25 +080079import com.android.wallpaper.picker.BottomActionBarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080080import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Chuck Liaob17d1502020-09-07 21:55:30 +080081import com.android.wallpaper.picker.FragmentTransactionChecker;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080082import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Chuck Liao7dc21572020-05-14 18:37:36 +080083import com.android.wallpaper.picker.PreviewActivity;
Jon Miranda16ea1b12017-12-12 14:52:48 -080084import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080085import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080086import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
87import com.android.wallpaper.picker.StartRotationDialogFragment;
88import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080089import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080090import com.android.wallpaper.picker.WallpapersUiContainer;
91import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
92import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070093import com.android.wallpaper.util.SizeCalculator;
Chuck Liao8ec38e02020-02-26 20:59:32 +080094import com.android.wallpaper.widget.BottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +080095import com.android.wallpaper.widget.WallpaperInfoView;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080096import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
97import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070098
Jon Miranda16ea1b12017-12-12 14:52:48 -080099import com.bumptech.glide.Glide;
100import com.bumptech.glide.MemoryCategory;
101
102import java.util.ArrayList;
103import java.util.Date;
104import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800105import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800106import java.util.Random;
107
108/**
109 * Displays the Main UI for picking an individual wallpaper image.
110 */
chihhangchuang3efb6832020-04-17 02:06:25 +0800111public class IndividualPickerFragment extends BottomActionBarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800112 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
113 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800114 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
115 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800116
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800117 /**
118 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
119 * such as "my photos" or "daily rotation".
120 */
121 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
122 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
123
Jon Miranda16ea1b12017-12-12 14:52:48 -0800124 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800125 private static final int UNUSED_REQUEST_CODE = 1;
126 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
127 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
128 private static final String PROGRESS_DIALOG_NO_TITLE = null;
129 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
130 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
131 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700132 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800133
Chuck Liao8ec38e02020-02-26 20:59:32 +0800134 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800135 * An interface for updating the thumbnail with the specific wallpaper.
136 */
137 public interface ThumbnailUpdater {
138 /**
139 * Updates the thumbnail with the specific wallpaper.
140 */
141 void updateThumbnail(WallpaperInfo wallpaperInfo);
142
143 /**
144 * Restores to the thumbnails of the wallpapers which were applied.
145 */
146 void restoreThumbnails();
147 }
148
“Chuck7ef99722020-03-22 04:34:03 +0800149 /**
150 * An interface for receiving the destination of the new applied wallpaper.
151 */
152 public interface WallpaperDestinationCallback {
153 /**
154 * Called when the destination of the wallpaper is set.
155 *
156 * @param destination the destination which a wallpaper may be set.
157 * See {@link Destination} for more details.
158 */
159 void onDestinationSet(@Destination int destination);
160 }
161
Chuck Liaob3829fb2020-04-01 00:47:50 +0800162 /**
163 * The listener which will be notified when the wallpaper is selected.
164 */
165 public interface WallpaperSelectedListener {
166 /**
167 * Called when the wallpaper is selected.
168 *
169 * @param position the position of the selected wallpaper
170 */
171 void onWallpaperSelected(int position);
172 }
173
Chuck Liaof6b4b192020-08-07 02:31:32 +0800174 /**
175 * Interface to be implemented by a Fragment hosting a {@link IndividualPickerFragment}
176 */
177 public interface IndividualPickerFragmentHost {
178 /**
179 * Sets the title in the toolbar.
180 */
181 void setToolbarTitle(CharSequence title);
182
183 /**
184 * Moves to the previous fragment.
185 */
186 void moveToPreviousFragment();
187 }
188
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800189 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800190 WallpaperPreferences mWallpaperPreferences;
191 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800192 RecyclerView mImageGrid;
193 IndividualAdapter mAdapter;
194 WallpaperCategory mCategory;
195 WallpaperRotationInitializer mWallpaperRotationInitializer;
196 List<WallpaperInfo> mWallpapers;
197 Point mTileSizePx;
198 WallpapersUiContainer mWallpapersUiContainer;
199 @FormFactor
200 int mFormFactor;
201 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800202
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800203 Handler mHandler;
204 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800205 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800206
207 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
208 new WallpaperChangedNotifier.Listener() {
209 @Override
210 public void onWallpaperChanged() {
211 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
212 return;
213 }
214
215 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
216 mAdapter.mSelectedAdapterPosition);
217
218 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
219 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
220 if (selectedViewHolder instanceof SelectableHolder) {
221 ((SelectableHolder) selectedViewHolder).setSelectionState(
222 SelectableHolder.SELECTION_STATE_DESELECTED);
223 }
224 } else {
225 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
226 }
227 }
228 };
229 PackageStatusNotifier.Listener mAppStatusListener;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800230 BottomActionBar mBottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +0800231 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800232 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800233
chihhangchuangc687d912020-05-04 14:33:05 +0800234 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800235 private ProgressDialog mProgressDialog;
236 private boolean mTestingMode;
237 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800238 private SetIndividualHolder mPendingSetIndividualHolder;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800239 private ContentLoadingProgressBar mLoading;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800240
241 /**
242 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
243 * committing fragment transactions.
244 */
245 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
246 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
247
Jon Miranda16ea1b12017-12-12 14:52:48 -0800248 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800249
250 /**
251 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
252 * invocation of the fragment.
253 */
254 private boolean mWasUpdateRunnableRun;
255
256 /**
257 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
258 * mode.
259 */
260 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
261 @Override
262 public void run() {
263 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
264 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
265 if (viewHolder instanceof DesktopRotationHolder) {
266 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
267 } else { // viewHolder is null
268 // If the rotation tile is unavailable (because user has scrolled down, causing the
269 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
270 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
271 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
272 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
273 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
274 }
275 }
276 };
277
Chuck Liao69630f12020-03-05 19:01:25 +0800278 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800279 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800280 private WallpaperManager mWallpaperManager;
281 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800282 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800283
Jon Miranda16ea1b12017-12-12 14:52:48 -0800284 public static IndividualPickerFragment newInstance(String collectionId) {
285 Bundle args = new Bundle();
286 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
287
288 IndividualPickerFragment fragment = new IndividualPickerFragment();
289 fragment.setArguments(args);
290 return fragment;
291 }
292
“Chuck7ef99722020-03-22 04:34:03 +0800293 /**
294 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
295 * would be set.
296 *
297 * @param wallpaperDestination the destination a wallpaper would be set.
298 * It will be either {@link Destination#DEST_HOME_SCREEN}
299 * or {@link Destination#DEST_LOCK_SCREEN}.
300 */
301 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
302 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800303 if (mWallpapers != null) {
304 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800305 }
306 }
307
Jon Miranda16ea1b12017-12-12 14:52:48 -0800308 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
309 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
310 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
311 getActivity());
312 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
313 @Override
314 public void onDrawableLoaded() {
315 if (getActivity() == null) {
316 return;
317 }
318
319 // Schedule the next update of the thumbnail.
320 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
321 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
322 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
323 }
324 });
325 }
326
327 @Override
328 public void onCreate(Bundle savedInstanceState) {
329 super.onCreate(savedInstanceState);
330
331 Injector injector = InjectorProvider.getInjector();
332 Context appContext = getContext().getApplicationContext();
333 mWallpaperPreferences = injector.getPreferences(appContext);
334
335 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
336 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
337
“Chuck7ef99722020-03-22 04:34:03 +0800338 mWallpaperManager = WallpaperManager.getInstance(appContext);
339
Jon Miranda16ea1b12017-12-12 14:52:48 -0800340 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
341
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700342 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
343
chihhangchuangc687d912020-05-04 14:33:05 +0800344 mUserEventLogger = injector.getUserEventLogger(appContext);
345
“Chuckffd832c2020-03-22 02:15:58 +0800346 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800347 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800348 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800349 injector.getPreferences(appContext),
350 injector.getUserEventLogger(appContext),
351 false);
352
Jon Miranda16ea1b12017-12-12 14:52:48 -0800353 mWallpapers = new ArrayList<>();
354 mRandom = new Random();
355 mHandler = new Handler();
356
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700357 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
358 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
359 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
360 Glide.get(getContext()).clearMemory();
361 }
362
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700363 CategoryProvider categoryProvider = injector.getCategoryProvider(appContext);
364 categoryProvider.fetchCategories(new CategoryReceiver() {
365 @Override
366 public void onCategoryReceived(Category category) {
367 // Do nothing.
368 }
369
370 @Override
371 public void doneFetchingCategories() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800372 Category category = categoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700373 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800374 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800375 return;
376 }
377 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700378 if (mCategory == null) {
379 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
380
381 // The absence of this category in the CategoryProvider indicates a broken
382 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800383 getIndividualPickerFragmentHost().moveToPreviousFragment();
384 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
385 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700386 return;
387 }
388 onCategoryLoaded();
389 }
390 }, false);
391 }
392
Chuck Liaof6b4b192020-08-07 02:31:32 +0800393
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700394 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800395 if (getIndividualPickerFragmentHost() == null) {
396 return;
397 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800398 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700399 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao58e4a1c2020-05-22 11:35:35 +0800400 // Avoids the "rotation" action is not shown correctly
401 // in a rare case : onCategoryLoaded() is called after onBottomActionBarReady().
402 if (isRotationEnabled() && mBottomActionBar != null
403 && !mBottomActionBar.areActionsShown(ROTATION)) {
404 mBottomActionBar.showActions(ROTATION);
405 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700406 fetchWallpapers(false);
407
408 if (mCategory.supportsThirdParty()) {
409 mAppStatusListener = (packageName, status) -> {
410 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
411 mCategory.containsThirdParty(packageName)) {
412 fetchWallpapers(true);
413 }
414 };
415 mPackageStatusNotifier.addListener(mAppStatusListener,
416 WallpaperService.SERVICE_INTERFACE);
417 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700418
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700419 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700420 }
421
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800422 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700423 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800424 mIsWallpapersReceived = false;
425 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800426 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
427 @Override
428 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800429 mIsWallpapersReceived = true;
430 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800431 for (WallpaperInfo wallpaper : wallpapers) {
432 mWallpapers.add(wallpaper);
433 }
434
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700435 // Wallpapers may load after the adapter is initialized, in which case we have
436 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800437 if (mAdapter != null) {
438 mAdapter.notifyDataSetChanged();
439 }
440
441 if (mWallpapersUiContainer != null) {
442 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700443 } else {
444 if (wallpapers.isEmpty()) {
445 // If there are no more wallpapers and we're on phone, just finish the
446 // Activity.
447 Activity activity = getActivity();
448 if (activity != null
449 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
450 activity.finish();
451 }
452 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800453 }
454 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700455 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800456 }
457
Chuck Liaof6b4b192020-08-07 02:31:32 +0800458 void updateLoading() {
459 if (mLoading == null) {
460 return;
461 }
462
463 if (mIsWallpapersReceived) {
464 mLoading.hide();
465 } else {
466 mLoading.show();
467 }
468 }
469
Jon Miranda16ea1b12017-12-12 14:52:48 -0800470 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700471 public void onSaveInstanceState(@NonNull Bundle outState) {
472 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700473 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700474 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
475 }
476
477 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800478 public View onCreateView(LayoutInflater inflater, ViewGroup container,
479 Bundle savedInstanceState) {
480 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
481
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700482 mTileSizePx = SizeCalculator.getIndividualTileSize(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800483
484 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
485 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
486 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
487 updateImageGridPadding(false /* addExtraBottomSpace */);
488 mImageGrid.setScrollBarSize(gridPaddingPx);
489 }
Chuck Liaoba401232020-03-13 20:11:04 +0800490 mImageGrid.addItemDecoration(new GridPaddingDecoration(
491 getResources().getDimensionPixelSize(R.dimen.grid_padding)));
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +0800492 mImageGrid.setAccessibilityDelegateCompat(
493 new WallpaperPickerRecyclerViewAccessibilityDelegate(
494 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Chuck Liaof6b4b192020-08-07 02:31:32 +0800495 mLoading = view.findViewById(R.id.loading_indicator);
496 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700497 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800498 setUpBottomSheet();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800499 return view;
500 }
501
502 @Override
503 public void onClickTryAgain(@Destination int unused) {
504 if (mPendingSetIndividualHolder != null) {
505 mPendingSetIndividualHolder.setWallpaper();
506 }
507 }
508
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800509 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800510 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
511 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
512 R.dimen.current_wallpaper_bottom_sheet_layout_height);
513 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
514 // Only left and top may be set in order for the GridMarginDecoration to work properly.
515 mImageGrid.setPadding(
516 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
517 }
518
Chuck Liaof6b4b192020-08-07 02:31:32 +0800519 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
520 return (IndividualPickerFragmentHost) getParentFragment();
521 }
522
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700523 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700524 // Skip if mImageGrid been initialized yet
525 if (mImageGrid == null) {
526 return;
527 }
528 // Skip if category hasn't loaded yet
529 if (mCategory == null) {
530 return;
531 }
532 // Skip if the adapter was already created
533 if (mAdapter != null) {
534 return;
535 }
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700536 setUpImageGrid();
537 }
538
539 /**
540 * Create the adapter and assign it to mImageGrid.
541 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
542 */
543 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800544 mAdapter = new IndividualAdapter(mWallpapers);
545 mImageGrid.setAdapter(mAdapter);
546 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
547 }
548
Jon Miranda16ea1b12017-12-12 14:52:48 -0800549 /**
550 * Enables and populates the "Currently set" wallpaper BottomSheet.
551 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800552 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800553 mImageGrid.addOnScrollListener(new OnScrollListener() {
554 @Override
555 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
556 if (mCurrentWallpaperBottomSheetPresenter == null) {
557 return;
558 }
559
560 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
561 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
562 }
563 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
564 @Override
565 public void run() {
566 if (dy > 0) {
567 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
568 } else {
569 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
570 }
571 }
572 };
573 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
574 }
575 });
576 }
577
chihhangchuang3efb6832020-04-17 02:06:25 +0800578 @Override
579 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Chuck Liao342f4ab2020-05-26 18:28:09 +0800580 mBottomActionBar = bottomActionBar;
581 if (isRotationEnabled()) {
582 mBottomActionBar.showActionsOnly(ROTATION);
Ching-Sung Li073812b2020-04-07 21:19:21 +0800583 }
Chuck Liao342f4ab2020-05-26 18:28:09 +0800584 mBottomActionBar.setActionClickListener(ROTATION, unused -> {
585 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
586 startRotationDialogFragment.setTargetFragment(
587 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
588 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
589 });
590 mBottomActionBar.setActionClickListener(APPLY, unused -> {
591 mBottomActionBar.disableActions();
592 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
593 mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
594 });
595
596 mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
597 .inflate(R.layout.wallpaper_info_view, /* root= */ null);
598 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
599 mBottomActionBar.setActionClickListener(EDIT, unused -> {
600 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
601 mSelectedWallpaperInfo.showPreview(getActivity(),
602 new PreviewActivity.PreviewActivityIntentFactory(),
603 PREVIEW_WALLPAPER_REQUEST_CODE);
604 });
605 mBottomActionBar.show();
Ching-Sung Li073812b2020-04-07 21:19:21 +0800606 }
607
Jon Miranda16ea1b12017-12-12 14:52:48 -0800608 @Override
609 public void onResume() {
610 super.onResume();
611
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700612 WallpaperPreferences preferences = InjectorProvider.getInjector()
613 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800614 preferences.setLastAppActiveTimestamp(new Date().getTime());
615
616 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
617 // PreviewFragment.
618 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
619
620 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
621 // shown earlier when this fragment's hosting activity didn't allow committing fragment
622 // transactions.
623 if (mStagedStartRotationErrorDialogFragment != null) {
624 mStagedStartRotationErrorDialogFragment.show(
625 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
626 mStagedStartRotationErrorDialogFragment = null;
627 }
628
629 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
630 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
631 // committing fragment transactions.
632 if (mStagedSetWallpaperErrorDialogFragment != null) {
633 mStagedSetWallpaperErrorDialogFragment.show(
634 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
635 mStagedSetWallpaperErrorDialogFragment = null;
636 }
637
Chuck Liao5a4243b2020-05-20 23:56:39 +0800638 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
639 // Must be resuming from a previously stopped state, so re-schedule the update of the
640 // daily wallpapers tile thumbnail.
641 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800642 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800643 }
644
645 @Override
646 public void onStop() {
647 super.onStop();
648 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
649 }
650
651 @Override
652 public void onDestroy() {
653 super.onDestroy();
654 if (mProgressDialog != null) {
655 mProgressDialog.dismiss();
656 }
657 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700658 if (mAppStatusListener != null) {
659 mPackageStatusNotifier.removeListener(mAppStatusListener);
660 }
Chuck Liao69630f12020-03-05 19:01:25 +0800661 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800662 }
663
664 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800665 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800666 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
667 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
668 // still holds a reference to the destroyed Fragment and is calling
669 // onStartRotationDialogDismissed on that destroyed Fragment.
670 if (mBottomActionBar != null) {
671 mBottomActionBar.deselectAction(ROTATION);
672 }
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800673 }
674
675 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800676 public void retryStartRotation(@NetworkPreference int networkPreference) {
677 startRotation(networkPreference);
678 }
679
chihhangchuang7feb3752020-04-24 02:48:56 +0800680 @Override
681 public boolean onBackPressed() {
682 if (mSelectedWallpaperInfo != null) {
683 onWallpaperSelected(null, 0);
684 return true;
685 }
686 return false;
687 }
688
Jon Miranda16ea1b12017-12-12 14:52:48 -0800689 public void setCurrentWallpaperBottomSheetPresenter(
690 CurrentWallpaperBottomSheetPresenter presenter) {
691 mCurrentWallpaperBottomSheetPresenter = presenter;
692 }
693
694 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
695 mWallpapersUiContainer = uiContainer;
696 }
697
Chuck Liaob3829fb2020-04-01 00:47:50 +0800698 public void setOnWallpaperSelectedListener(
699 WallpaperSelectedListener wallpaperSelectedListener) {
700 mWallpaperSelectedListener = wallpaperSelectedListener;
701 }
702
703 /**
704 * Resizes the layout's height.
705 */
706 public void resizeLayout(int height) {
707 mImageGrid.getLayoutParams().height = height;
708 mImageGrid.requestLayout();
709 }
710
711 /**
712 * Scrolls to the specific item.
713 *
714 * @param position the position of the item
715 */
716 public void scrollToPosition(int position) {
717 ((GridLayoutManager) mImageGrid.getLayoutManager())
718 .scrollToPositionWithOffset(position, /* offset= */ 0);
719 }
720
Jon Miranda16ea1b12017-12-12 14:52:48 -0800721 /**
722 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
723 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
724 * constantly keeps the UI thread alive and blocks a test forever.
725 *
726 * @param testingMode
727 */
728 void setTestingMode(boolean testingMode) {
729 mTestingMode = testingMode;
730 }
731
Jon Miranda16ea1b12017-12-12 14:52:48 -0800732 @Override
733 public void startRotation(@NetworkPreference final int networkPreference) {
734 if (!isRotationEnabled()) {
735 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
736 return;
737 }
738
739 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
740 // causes Espresso to hang once the dialog is shown.
741 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
742 int themeResId;
743 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
744 themeResId = R.style.ProgressDialogThemePreL;
745 } else {
746 themeResId = R.style.LightDialogTheme;
747 }
748 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
749
750 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
751 mProgressDialog.setMessage(
752 getResources().getString(R.string.start_rotation_progress_message));
753 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
754 mProgressDialog.show();
755 }
756
757 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
758 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
759 }
760
761 final Context appContext = getActivity().getApplicationContext();
762
763 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
764 appContext,
765 networkPreference,
766 new Listener() {
767 @Override
768 public void onFirstWallpaperInRotationSet() {
769 if (mProgressDialog != null) {
770 mProgressDialog.dismiss();
771 }
772
773 // The fragment may be detached from its containing activity if the user exits the
774 // app before the first wallpaper image in rotation finishes downloading.
775 Activity activity = getActivity();
776
Jon Miranda16ea1b12017-12-12 14:52:48 -0800777
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700778 if (mWallpaperRotationInitializer.startRotation(appContext)) {
779 if (activity != null
780 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
781 try {
782 Toast.makeText(getActivity(),
783 R.string.wallpaper_set_successfully_message,
784 Toast.LENGTH_SHORT).show();
785 } catch (NotFoundException e) {
786 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800787 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800788
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700789 activity.setResult(Activity.RESULT_OK);
790 activity.finish();
791 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
792 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
793 }
794 } else { // Failed to start rotation.
795 showStartRotationErrorDialog(networkPreference);
796
797 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
798 DesktopRotationHolder rotationViewHolder =
799 (DesktopRotationHolder)
800 mImageGrid.findViewHolderForAdapterPosition(
801 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
802 rotationViewHolder.setSelectionState(
803 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800804 }
805 }
806 }
807
808 @Override
809 public void onError() {
810 if (mProgressDialog != null) {
811 mProgressDialog.dismiss();
812 }
813
814 showStartRotationErrorDialog(networkPreference);
815
816 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
817 DesktopRotationHolder rotationViewHolder =
818 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
819 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
820 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
821 }
822 }
823 });
824 }
825
826 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800827 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800828 if (activity != null) {
829 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
830 StartRotationErrorDialogFragment.newInstance(networkPreference);
831 startRotationErrorDialogFragment.setTargetFragment(
832 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
833
834 if (activity.isSafeToCommitFragmentTransaction()) {
835 startRotationErrorDialogFragment.show(
836 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
837 } else {
838 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
839 }
840 }
841 }
842
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800843 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800844 Activity activity = getActivity();
Chuck Liaoe2fe0302020-06-29 21:15:35 +0800845 return activity == null ? 1 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800846 }
847
848 /**
849 * Returns whether rotation is enabled for this category.
850 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800851 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700852 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800853 }
854
855 @Override
856 public void onCurrentWallpaperRefreshed() {
857 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
858 }
859
Chuck Liao69630f12020-03-05 19:01:25 +0800860 @Override
861 public void onSet(int destination) {
862 if (mSelectedWallpaperInfo == null) {
863 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
864 return;
865 }
866
“Chuckffd832c2020-03-22 02:15:58 +0800867 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800868 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
869 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
870 destination, 0, null, mSetWallpaperCallback);
871 } else {
872 mWallpaperSetter.setCurrentWallpaper(
873 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
874 }
“Chuck7ef99722020-03-22 04:34:03 +0800875 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800876 }
877
878 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
879 new WallpaperPersister.SetWallpaperCallback() {
880 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800881 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800882 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800883 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
884 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800885 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
886 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800887 }
888
889 @Override
890 public void onError(@Nullable Throwable throwable) {
891 Log.e(TAG, "Can't apply the wallpaper.");
892 mBottomActionBar.enableActions();
893 }
894 };
895
896 @Override
897 public void onDialogDismissed(boolean withItemSelected) {
898 if (!withItemSelected) {
899 mBottomActionBar.enableActions();
900 }
901 }
902
Jon Miranda16ea1b12017-12-12 14:52:48 -0800903 /**
904 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
905 */
906 private void showSetWallpaperErrorDialog() {
907 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
908 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
909 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
910
911 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
912 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
913 } else {
914 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
915 }
916 }
917
Ching-Sung Li073812b2020-04-07 21:19:21 +0800918 void updateBottomActions(boolean hasWallpaperSelected) {
chihhangchuang803ea9a2020-04-21 13:03:10 +0800919 if (hasWallpaperSelected) {
Chuck Liao7dc21572020-05-14 18:37:36 +0800920 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800921 } else {
chihhangchuang08abb582020-04-27 17:20:31 +0800922 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800923 }
Chuck Liao69630f12020-03-05 19:01:25 +0800924 }
925
926 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800927 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800928 if (thumbnailUpdater == null) {
929 return;
930 }
931
932 if (selectedWallpaperInfo != null) {
933 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
934 } else {
935 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800936 }
937 }
938
“Chuck7ef99722020-03-22 04:34:03 +0800939 private void onWallpaperDestinationSet(int destination) {
940 WallpaperDestinationCallback wallpaperDestinationCallback =
941 (WallpaperDestinationCallback) getParentFragment();
942 if (wallpaperDestinationCallback == null) {
943 return;
944 }
945
946 wallpaperDestinationCallback.onDestinationSet(destination);
947 }
948
Ching-Sung Li073812b2020-04-07 21:19:21 +0800949 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800950 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800951 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800952 return;
953 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800954 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +0800955 updateActivatedStatus(mSelectedWallpaperInfo == null
956 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800957 // Update new selected wallpaper.
958 updateActivatedStatus(newSelectedWallpaperInfo == null
959 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
960
961 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
962 updateBottomActions(mSelectedWallpaperInfo != null);
963 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +0800964 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +0800965 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +0800966 WallpaperInfoHelper.loadExploreIntent(
967 getContext(),
968 mSelectedWallpaperInfo,
969 (actionLabel, exploreIntent) ->
970 mWallpaperInfoView.populateWallpaperInfo(
971 mSelectedWallpaperInfo, actionLabel, exploreIntent,
972 v -> onExploreClicked(exploreIntent)));
Chuck Liaob3829fb2020-04-01 00:47:50 +0800973 }
974
975 if (mWallpaperSelectedListener != null) {
976 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800977 }
Chuck Liaof40063f2020-03-03 18:35:24 +0800978 }
979
chihhangchuangc687d912020-05-04 14:33:05 +0800980 private void onExploreClicked(Intent exploreIntent) {
981 if (getContext() == null) {
982 return;
983 }
984 Context context = getContext();
985 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
986 mSelectedWallpaperInfo.getActionLabelRes(context));
987
988 startActivity(exploreIntent);
989 }
990
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800991 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +0800992 if (wallpaperInfo == null) {
993 return;
994 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800995 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800996 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800997 ? index + 1 : index;
998 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
999 if (holder != null) {
1000 holder.itemView.setActivated(isActivated);
1001 } else {
1002 // Item is not visible, make sure the item is re-bound when it becomes visible.
1003 mAdapter.notifyItemChanged(index);
1004 }
1005 }
1006
“Chuckffd832c2020-03-22 02:15:58 +08001007 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1008 if (wallpaperInfo == null) {
1009 return;
1010 }
1011 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001012 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001013 ? index + 1 : index;
1014 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1015 if (holder != null) {
1016 holder.itemView.findViewById(R.id.check_circle)
1017 .setVisibility(isApplied ? View.VISIBLE : View.GONE);
1018 } else {
1019 // Item is not visible, make sure the item is re-bound when it becomes visible.
1020 mAdapter.notifyItemChanged(index);
1021 }
1022 }
1023
Chuck Liao11f4a762020-04-08 13:24:43 +08001024 private void refreshAppliedWallpaper() {
1025 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1026 showCheckMarkAndBorderForAppliedWallpaper(false);
1027
1028 // Update to the new applied wallpaper.
1029 String appliedWallpaperId = getAppliedWallpaperId();
1030 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1031 .stream()
1032 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1033 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1034 .findFirst();
1035 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1036
1037 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1038 showCheckMarkAndBorderForAppliedWallpaper(true);
1039 }
1040
1041 private String getAppliedWallpaperId() {
1042 WallpaperPreferences prefs =
1043 InjectorProvider.getInjector().getPreferences(getContext());
1044 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1045 boolean isDestinationBoth =
1046 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1047
1048 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1049 return wallpaperInfo != null
1050 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1051 } else {
1052 return prefs.getLockWallpaperRemoteId();
1053 }
1054 }
1055
1056 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1057 updateAppliedStatus(mAppliedWallpaperInfo, show);
1058 if (mSelectedWallpaperInfo == null) {
1059 updateActivatedStatus(mAppliedWallpaperInfo, show);
1060 }
1061 }
1062
Chuck Liao5a4243b2020-05-20 23:56:39 +08001063 private boolean shouldShowRotationTile() {
1064 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001065 }
1066
1067 /**
1068 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1069 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001070 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1071 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1072 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1073 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001074
1075 private final List<WallpaperInfo> mWallpapers;
1076
1077 private int mPendingSelectedAdapterPosition;
1078 private int mSelectedAdapterPosition;
1079
1080 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1081 mWallpapers = wallpapers;
1082 mPendingSelectedAdapterPosition = -1;
1083 mSelectedAdapterPosition = -1;
1084 }
1085
1086 @Override
1087 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1088 switch (viewType) {
1089 case ITEM_VIEW_TYPE_ROTATION:
1090 return createRotationHolder(parent);
1091 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1092 return createIndividualHolder(parent);
1093 case ITEM_VIEW_TYPE_MY_PHOTOS:
1094 return createMyPhotosHolder(parent);
1095 default:
1096 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1097 return null;
1098 }
1099 }
1100
1101 @Override
1102 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001103 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001104 return ITEM_VIEW_TYPE_ROTATION;
1105 }
1106
1107 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1108 if (mCategory.supportsCustomPhotos()
1109 && !isRotationEnabled()
1110 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1111 return ITEM_VIEW_TYPE_MY_PHOTOS;
1112 }
1113
1114 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1115 }
1116
1117 @Override
1118 public void onBindViewHolder(ViewHolder holder, int position) {
1119 int viewType = getItemViewType(position);
1120
1121 switch (viewType) {
1122 case ITEM_VIEW_TYPE_ROTATION:
1123 onBindRotationHolder(holder, position);
1124 break;
1125 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1126 onBindIndividualHolder(holder, position);
1127 break;
1128 case ITEM_VIEW_TYPE_MY_PHOTOS:
1129 ((MyPhotosViewHolder) holder).bind();
1130 break;
1131 default:
1132 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1133 }
1134 }
1135
1136 @Override
1137 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001138 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001139 ? mWallpapers.size() + 1
1140 : mWallpapers.size();
1141 }
1142
1143 private ViewHolder createRotationHolder(ViewGroup parent) {
1144 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001145 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
1146 SelectionAnimator selectionAnimator =
1147 new CheckmarkSelectionAnimator(getActivity(), view);
1148 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1149 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001150 }
1151
1152 private ViewHolder createIndividualHolder(ViewGroup parent) {
1153 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1154 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1155
1156 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1157 SelectionAnimator selectionAnimator =
1158 new CheckmarkSelectionAnimator(getActivity(), view);
1159 return new SetIndividualHolder(
1160 getActivity(), mTileSizePx.y, view,
1161 selectionAnimator,
1162 new OnSetListener() {
1163 @Override
1164 public void onPendingWallpaperSet(int adapterPosition) {
1165 // Deselect and hide loading indicator for any previously pending tile.
1166 if (mPendingSelectedAdapterPosition != -1) {
1167 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1168 mPendingSelectedAdapterPosition);
1169 if (oldViewHolder instanceof SelectableHolder) {
1170 ((SelectableHolder) oldViewHolder).setSelectionState(
1171 SelectableHolder.SELECTION_STATE_DESELECTED);
1172 }
1173 }
1174
1175 if (mSelectedAdapterPosition != -1) {
1176 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1177 mSelectedAdapterPosition);
1178 if (oldViewHolder instanceof SelectableHolder) {
1179 ((SelectableHolder) oldViewHolder).setSelectionState(
1180 SelectableHolder.SELECTION_STATE_DESELECTED);
1181 }
1182 }
1183
1184 mPendingSelectedAdapterPosition = adapterPosition;
1185 }
1186
1187 @Override
1188 public void onWallpaperSet(int adapterPosition) {
1189 // No-op -- UI handles a new wallpaper being set by reacting to the
1190 // WallpaperChangedNotifier.
1191 }
1192
1193 @Override
1194 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1195 showSetWallpaperErrorDialog();
1196 mPendingSetIndividualHolder = holder;
1197 }
1198 });
1199 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001200 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001201 }
1202 }
1203
1204 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1205 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1206 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1207
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001208 return new MyPhotosViewHolder(getActivity(),
1209 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1210 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001211 }
1212
1213 /**
1214 * Marks the tile at the given position as selected with a visual indication. Also updates the
1215 * "currently selected" BottomSheet to reflect the newly selected tile.
1216 */
1217 private void updateSelectedTile(int newlySelectedPosition) {
1218 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1219 // succession.
1220 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1221 return;
1222 }
1223
1224 if (mCurrentWallpaperBottomSheetPresenter != null) {
1225 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1226 IndividualPickerFragment.this);
1227
1228 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1229 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1230 }
1231 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1232 @Override
1233 public void run() {
1234 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1235 }
1236 };
1237 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1238 }
1239
1240 // User may have switched to another category, thus detaching this fragment, so check here.
1241 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1242 // still occurs in the UI after the user selects that other category.
1243 if (getActivity() == null) {
1244 return;
1245 }
1246
1247 // Update the newly selected wallpaper ViewHolder and the old one so that if
1248 // selection UI state applies (desktop UI), it is updated.
1249 if (mSelectedAdapterPosition >= 0) {
1250 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1251 mSelectedAdapterPosition);
1252 if (oldViewHolder instanceof SelectableHolder) {
1253 ((SelectableHolder) oldViewHolder).setSelectionState(
1254 SelectableHolder.SELECTION_STATE_DESELECTED);
1255 }
1256 }
1257
1258 // Animate selection of newly selected tile.
1259 ViewHolder newViewHolder = mImageGrid
1260 .findViewHolderForAdapterPosition(newlySelectedPosition);
1261 if (newViewHolder instanceof SelectableHolder) {
1262 ((SelectableHolder) newViewHolder).setSelectionState(
1263 SelectableHolder.SELECTION_STATE_SELECTED);
1264 }
1265
1266 mSelectedAdapterPosition = newlySelectedPosition;
1267
1268 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1269 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1270 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1271 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1272 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1273 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1274
1275 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1276 }
1277
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001278 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001279 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1280 String collectionId = mCategory.getCollectionId();
1281 ((DesktopRotationHolder) holder).bind(collectionId);
1282
1283 if (mWallpaperPreferences.getWallpaperPresentationMode()
1284 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1285 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1286 mSelectedAdapterPosition = position;
1287 }
1288
1289 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1290 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1291 mWasUpdateRunnableRun = true;
1292 }
1293 }
1294 }
1295
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001296 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001297 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001298 ? position - 1 : position;
1299 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1300 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001301 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001302 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1303 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1304 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001305
“Chuckffd832c2020-03-22 02:15:58 +08001306 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001307 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001308 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001309 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001310
Chuck Liao342f4ab2020-05-26 18:28:09 +08001311 holder.itemView.setActivated(
1312 (isWallpaperApplied && !hasUserSelectedWallpaper) || isWallpaperSelected);
1313 holder.itemView.findViewById(R.id.check_circle).setVisibility(
1314 isWallpaperApplied ? View.VISIBLE : View.GONE);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001315 }
1316 }
Chuck Liaoba401232020-03-13 20:11:04 +08001317
1318 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1319
1320 private int mPadding;
1321
1322 GridPaddingDecoration(int padding) {
1323 mPadding = padding;
1324 }
1325
1326 @Override
1327 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1328 RecyclerView.State state) {
1329 int position = parent.getChildAdapterPosition(view);
1330 if (position >= 0) {
1331 outRect.left = mPadding;
1332 outRect.right = mPadding;
1333 }
1334 }
1335 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001336}