blob: 0a0222dd36e7af6328a3adc0fd2d70eccbb3467c [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 Liaoa7215812021-04-13 22:33:43 +080018import android.annotation.MenuRes;
Jon Miranda16ea1b12017-12-12 14:52:48 -080019import android.app.Activity;
20import android.app.ProgressDialog;
“Chuck7ef99722020-03-22 04:34:03 +080021import android.app.WallpaperManager;
Jon Miranda16ea1b12017-12-12 14:52:48 -080022import android.content.Context;
Chihhang Chuang85f099a2020-06-16 18:04:39 +080023import android.content.DialogInterface;
chihhangchuangc687d912020-05-04 14:33:05 +080024import android.content.Intent;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070025import android.content.res.Configuration;
Jon Miranda16ea1b12017-12-12 14:52:48 -080026import android.content.res.Resources.NotFoundException;
27import android.graphics.Point;
Chuck Liaoba401232020-03-13 20:11:04 +080028import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080029import android.os.Build.VERSION;
30import android.os.Build.VERSION_CODES;
31import android.os.Bundle;
32import android.os.Handler;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070033import android.service.wallpaper.WallpaperService;
Jon Miranda16ea1b12017-12-12 14:52:48 -080034import android.util.Log;
35import android.view.LayoutInflater;
Chuck Liao33f1af42021-04-13 20:52:35 +080036import android.view.MenuItem;
Jon Miranda16ea1b12017-12-12 14:52:48 -080037import android.view.View;
38import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080039import android.widget.Toast;
40
Santiago Etchebehereb1854472019-06-06 17:44:54 -070041import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080042import androidx.annotation.Nullable;
Chuck Liaof6b4b192020-08-07 02:31:32 +080043import androidx.core.widget.ContentLoadingProgressBar;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080044import androidx.fragment.app.DialogFragment;
Chuck Liao7e333722021-02-27 02:21:20 +080045import androidx.fragment.app.Fragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080046import androidx.recyclerview.widget.GridLayoutManager;
47import androidx.recyclerview.widget.RecyclerView;
48import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
49import androidx.recyclerview.widget.RecyclerView.ViewHolder;
50
Jon Miranda16ea1b12017-12-12 14:52:48 -080051import com.android.wallpaper.R;
52import com.android.wallpaper.asset.Asset;
53import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070054import com.android.wallpaper.model.Category;
55import com.android.wallpaper.model.CategoryProvider;
56import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080057import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080058import com.android.wallpaper.model.WallpaperCategory;
59import com.android.wallpaper.model.WallpaperInfo;
60import com.android.wallpaper.model.WallpaperReceiver;
61import com.android.wallpaper.model.WallpaperRotationInitializer;
62import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
63import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080064import com.android.wallpaper.module.FormFactorChecker;
65import com.android.wallpaper.module.FormFactorChecker.FormFactor;
66import com.android.wallpaper.module.Injector;
67import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070068import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080069import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080070import com.android.wallpaper.module.WallpaperChangedNotifier;
71import com.android.wallpaper.module.WallpaperPersister;
72import com.android.wallpaper.module.WallpaperPersister.Destination;
73import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080074import com.android.wallpaper.module.WallpaperSetter;
Chuck Liao58aca1c2021-03-17 01:20:55 +080075import com.android.wallpaper.picker.AppbarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080076import com.android.wallpaper.picker.BaseActivity;
77import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Chuck Liaob17d1502020-09-07 21:55:30 +080078import com.android.wallpaper.picker.FragmentTransactionChecker;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080079import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -080080import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080081import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080082import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
83import com.android.wallpaper.picker.StartRotationDialogFragment;
84import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080085import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080086import com.android.wallpaper.picker.WallpapersUiContainer;
87import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
88import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070089import com.android.wallpaper.util.SizeCalculator;
chihhangchuang1a29e752020-04-28 18:22:53 +080090import com.android.wallpaper.widget.WallpaperInfoView;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080091import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
92import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070093
Jon Miranda16ea1b12017-12-12 14:52:48 -080094import com.bumptech.glide.Glide;
95import com.bumptech.glide.MemoryCategory;
96
97import java.util.ArrayList;
98import java.util.Date;
99import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800100import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800101import java.util.Random;
102
103/**
104 * Displays the Main UI for picking an individual wallpaper image.
105 */
Chuck Liao58aca1c2021-03-17 01:20:55 +0800106public class IndividualPickerFragment extends AppbarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800107 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
108 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800109 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
110 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800111
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800112 /**
113 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
114 * such as "my photos" or "daily rotation".
115 */
116 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
117 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
118
Jon Miranda16ea1b12017-12-12 14:52:48 -0800119 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800120 private static final int UNUSED_REQUEST_CODE = 1;
121 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
122 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
123 private static final String PROGRESS_DIALOG_NO_TITLE = null;
124 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
125 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
126 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700127 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800128
Chuck Liao8ec38e02020-02-26 20:59:32 +0800129 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800130 * An interface for updating the thumbnail with the specific wallpaper.
131 */
132 public interface ThumbnailUpdater {
133 /**
134 * Updates the thumbnail with the specific wallpaper.
135 */
136 void updateThumbnail(WallpaperInfo wallpaperInfo);
137
138 /**
139 * Restores to the thumbnails of the wallpapers which were applied.
140 */
141 void restoreThumbnails();
142 }
143
“Chuck7ef99722020-03-22 04:34:03 +0800144 /**
145 * An interface for receiving the destination of the new applied wallpaper.
146 */
147 public interface WallpaperDestinationCallback {
148 /**
149 * Called when the destination of the wallpaper is set.
150 *
151 * @param destination the destination which a wallpaper may be set.
152 * See {@link Destination} for more details.
153 */
154 void onDestinationSet(@Destination int destination);
155 }
156
Chuck Liaob3829fb2020-04-01 00:47:50 +0800157 /**
158 * The listener which will be notified when the wallpaper is selected.
159 */
160 public interface WallpaperSelectedListener {
161 /**
162 * Called when the wallpaper is selected.
163 *
164 * @param position the position of the selected wallpaper
165 */
166 void onWallpaperSelected(int position);
167 }
168
Chuck Liaof6b4b192020-08-07 02:31:32 +0800169 /**
Chuck Liao7e333722021-02-27 02:21:20 +0800170 * Interface to be implemented by a Fragment(or an Activity) hosting
171 * a {@link IndividualPickerFragment}.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800172 */
173 public interface IndividualPickerFragmentHost {
174 /**
Chuck Liao58aca1c2021-03-17 01:20:55 +0800175 * Indicates if the host has toolbar to show the title. If it does, we should set the title
176 * there.
177 */
178 boolean isHostToolbarShown();
179
180 /**
181 * Sets the title in the host's toolbar.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800182 */
183 void setToolbarTitle(CharSequence title);
184
185 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800186 * Configures the menu in the toolbar.
187 *
188 * @param menuResId the resource id of the menu
189 */
190 void setToolbarMenu(@MenuRes int menuResId);
191
192 /**
193 * Removes the menu in the toolbar.
194 */
195 void removeToolbarMenu();
196
197 /**
Chuck Liaof6b4b192020-08-07 02:31:32 +0800198 * Moves to the previous fragment.
199 */
200 void moveToPreviousFragment();
201 }
202
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800203 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800204 WallpaperPreferences mWallpaperPreferences;
205 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800206 RecyclerView mImageGrid;
207 IndividualAdapter mAdapter;
208 WallpaperCategory mCategory;
209 WallpaperRotationInitializer mWallpaperRotationInitializer;
210 List<WallpaperInfo> mWallpapers;
211 Point mTileSizePx;
212 WallpapersUiContainer mWallpapersUiContainer;
213 @FormFactor
214 int mFormFactor;
215 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800216
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800217 Handler mHandler;
218 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800219 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800220
221 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
222 new WallpaperChangedNotifier.Listener() {
223 @Override
224 public void onWallpaperChanged() {
225 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
226 return;
227 }
228
229 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
230 mAdapter.mSelectedAdapterPosition);
231
232 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
233 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
234 if (selectedViewHolder instanceof SelectableHolder) {
235 ((SelectableHolder) selectedViewHolder).setSelectionState(
236 SelectableHolder.SELECTION_STATE_DESELECTED);
237 }
238 } else {
239 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
240 }
241 }
242 };
243 PackageStatusNotifier.Listener mAppStatusListener;
chihhangchuang1a29e752020-04-28 18:22:53 +0800244 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800245 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800246
chihhangchuangc687d912020-05-04 14:33:05 +0800247 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800248 private ProgressDialog mProgressDialog;
249 private boolean mTestingMode;
250 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800251 private SetIndividualHolder mPendingSetIndividualHolder;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800252 private ContentLoadingProgressBar mLoading;
Chuck Liaoddf2b522021-04-15 00:36:25 +0800253 private CategoryProvider mCategoryProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800254
255 /**
256 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
257 * committing fragment transactions.
258 */
259 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
260 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
261
Jon Miranda16ea1b12017-12-12 14:52:48 -0800262 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800263
264 /**
265 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
266 * invocation of the fragment.
267 */
268 private boolean mWasUpdateRunnableRun;
269
270 /**
271 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
272 * mode.
273 */
274 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
275 @Override
276 public void run() {
277 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
278 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
279 if (viewHolder instanceof DesktopRotationHolder) {
280 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
281 } else { // viewHolder is null
282 // If the rotation tile is unavailable (because user has scrolled down, causing the
283 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
284 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
285 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
286 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
287 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
288 }
289 }
290 };
291
Chuck Liao69630f12020-03-05 19:01:25 +0800292 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800293 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800294 private WallpaperManager mWallpaperManager;
295 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800296 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800297
Jon Miranda16ea1b12017-12-12 14:52:48 -0800298 public static IndividualPickerFragment newInstance(String collectionId) {
299 Bundle args = new Bundle();
300 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
301
302 IndividualPickerFragment fragment = new IndividualPickerFragment();
303 fragment.setArguments(args);
304 return fragment;
305 }
306
“Chuck7ef99722020-03-22 04:34:03 +0800307 /**
308 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
309 * would be set.
310 *
311 * @param wallpaperDestination the destination a wallpaper would be set.
312 * It will be either {@link Destination#DEST_HOME_SCREEN}
313 * or {@link Destination#DEST_LOCK_SCREEN}.
314 */
315 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
316 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800317 if (mWallpapers != null) {
318 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800319 }
320 }
321
Jon Miranda16ea1b12017-12-12 14:52:48 -0800322 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
323 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
324 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
325 getActivity());
326 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
327 @Override
328 public void onDrawableLoaded() {
329 if (getActivity() == null) {
330 return;
331 }
332
333 // Schedule the next update of the thumbnail.
334 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
335 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
336 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
337 }
338 });
339 }
340
341 @Override
342 public void onCreate(Bundle savedInstanceState) {
343 super.onCreate(savedInstanceState);
344
345 Injector injector = InjectorProvider.getInjector();
346 Context appContext = getContext().getApplicationContext();
347 mWallpaperPreferences = injector.getPreferences(appContext);
348
349 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
350 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
351
“Chuck7ef99722020-03-22 04:34:03 +0800352 mWallpaperManager = WallpaperManager.getInstance(appContext);
353
Jon Miranda16ea1b12017-12-12 14:52:48 -0800354 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
355
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700356 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
357
chihhangchuangc687d912020-05-04 14:33:05 +0800358 mUserEventLogger = injector.getUserEventLogger(appContext);
359
“Chuckffd832c2020-03-22 02:15:58 +0800360 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800361 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800362 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800363 injector.getPreferences(appContext),
364 injector.getUserEventLogger(appContext),
365 false);
366
Jon Miranda16ea1b12017-12-12 14:52:48 -0800367 mWallpapers = new ArrayList<>();
368 mRandom = new Random();
369 mHandler = new Handler();
370
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700371 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
372 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
373 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
374 Glide.get(getContext()).clearMemory();
375 }
376
Chuck Liaoddf2b522021-04-15 00:36:25 +0800377 mCategoryProvider = injector.getCategoryProvider(appContext);
378 mCategoryProvider.fetchCategories(new CategoryReceiver() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700379 @Override
380 public void onCategoryReceived(Category category) {
381 // Do nothing.
382 }
383
384 @Override
385 public void doneFetchingCategories() {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800386 Category category = mCategoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700387 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800388 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800389 return;
390 }
391 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700392 if (mCategory == null) {
393 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
394
395 // The absence of this category in the CategoryProvider indicates a broken
396 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800397 getIndividualPickerFragmentHost().moveToPreviousFragment();
398 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
399 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700400 return;
401 }
402 onCategoryLoaded();
403 }
404 }, false);
405 }
406
Chuck Liaof6b4b192020-08-07 02:31:32 +0800407
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700408 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800409 if (getIndividualPickerFragmentHost() == null) {
410 return;
411 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800412 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
413 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
414 } else {
415 setTitle(mCategory.getTitle());
416 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700417 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao33f1af42021-04-13 20:52:35 +0800418 if (mToolbar != null && isRotationEnabled()) {
419 setUpToolbarMenu(R.menu.individual_picker_menu);
420 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700421 fetchWallpapers(false);
422
423 if (mCategory.supportsThirdParty()) {
424 mAppStatusListener = (packageName, status) -> {
425 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
426 mCategory.containsThirdParty(packageName)) {
427 fetchWallpapers(true);
428 }
429 };
430 mPackageStatusNotifier.addListener(mAppStatusListener,
431 WallpaperService.SERVICE_INTERFACE);
432 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700433
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700434 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700435 }
436
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800437 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700438 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800439 mIsWallpapersReceived = false;
440 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800441 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
442 @Override
443 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800444 mIsWallpapersReceived = true;
445 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800446 for (WallpaperInfo wallpaper : wallpapers) {
447 mWallpapers.add(wallpaper);
448 }
449
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700450 // Wallpapers may load after the adapter is initialized, in which case we have
451 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800452 if (mAdapter != null) {
453 mAdapter.notifyDataSetChanged();
454 }
455
456 if (mWallpapersUiContainer != null) {
457 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700458 } else {
459 if (wallpapers.isEmpty()) {
460 // If there are no more wallpapers and we're on phone, just finish the
461 // Activity.
462 Activity activity = getActivity();
463 if (activity != null
464 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
465 activity.finish();
466 }
467 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800468 }
469 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700470 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800471 }
472
Chuck Liaof6b4b192020-08-07 02:31:32 +0800473 void updateLoading() {
474 if (mLoading == null) {
475 return;
476 }
477
478 if (mIsWallpapersReceived) {
479 mLoading.hide();
480 } else {
481 mLoading.show();
482 }
483 }
484
Jon Miranda16ea1b12017-12-12 14:52:48 -0800485 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700486 public void onSaveInstanceState(@NonNull Bundle outState) {
487 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700488 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700489 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
490 }
491
492 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800493 public View onCreateView(LayoutInflater inflater, ViewGroup container,
494 Bundle savedInstanceState) {
495 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
Chuck Liao58aca1c2021-03-17 01:20:55 +0800496 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
497 view.findViewById(R.id.header_bar).setVisibility(View.GONE);
Kunhung Lid0174362021-04-05 15:31:41 +0800498 setUpArrowEnabled(/* upArrow= */ true);
Chuck Liaoa7215812021-04-13 22:33:43 +0800499 if (isRotationEnabled()) {
500 getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu);
501 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800502 } else {
503 setUpToolbar(view);
Chuck Liao33f1af42021-04-13 20:52:35 +0800504 if (isRotationEnabled()) {
505 setUpToolbarMenu(R.menu.individual_picker_menu);
506 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800507 if (mCategory != null) {
508 setTitle(mCategory.getTitle());
509 }
510 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800511
Jon Miranda16ea1b12017-12-12 14:52:48 -0800512 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
513 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
514 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
515 updateImageGridPadding(false /* addExtraBottomSpace */);
516 mImageGrid.setScrollBarSize(gridPaddingPx);
517 }
Chuck Liaoba401232020-03-13 20:11:04 +0800518 mImageGrid.addItemDecoration(new GridPaddingDecoration(
519 getResources().getDimensionPixelSize(R.dimen.grid_padding)));
Chuck Liaof6b4b192020-08-07 02:31:32 +0800520 mLoading = view.findViewById(R.id.loading_indicator);
521 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700522 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800523 setUpBottomSheet();
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800524 // For nav bar edge-to-edge effect.
525 view.findViewById(R.id.wallpaper_grid).setOnApplyWindowInsetsListener((v, windowInsets) -> {
526 v.setPadding(
527 v.getPaddingLeft(),
528 v.getPaddingTop(),
529 v.getPaddingRight(),
530 windowInsets.getSystemWindowInsetBottom());
531 return windowInsets.consumeSystemWindowInsets();
532 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800533 return view;
534 }
535
536 @Override
537 public void onClickTryAgain(@Destination int unused) {
538 if (mPendingSetIndividualHolder != null) {
539 mPendingSetIndividualHolder.setWallpaper();
540 }
541 }
542
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800543 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800544 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
545 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
546 R.dimen.current_wallpaper_bottom_sheet_layout_height);
547 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
548 // Only left and top may be set in order for the GridMarginDecoration to work properly.
549 mImageGrid.setPadding(
550 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
551 }
552
Chuck Liaof6b4b192020-08-07 02:31:32 +0800553 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
Chuck Liao7e333722021-02-27 02:21:20 +0800554 Fragment parentFragment = getParentFragment();
555 if (parentFragment != null) {
556 return (IndividualPickerFragmentHost) parentFragment;
557 } else {
558 return (IndividualPickerFragmentHost) getActivity();
559 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800560 }
561
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700562 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700563 // Skip if mImageGrid been initialized yet
564 if (mImageGrid == null) {
565 return;
566 }
567 // Skip if category hasn't loaded yet
568 if (mCategory == null) {
569 return;
570 }
571 // Skip if the adapter was already created
572 if (mAdapter != null) {
573 return;
574 }
Chuck Liaoddf2b522021-04-15 00:36:25 +0800575 mTileSizePx = mCategoryProvider.isFeaturedCategory(mCategory)
576 ? SizeCalculator.getFeaturedIndividualTileSize(getActivity())
577 : SizeCalculator.getIndividualTileSize(getActivity());
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700578 setUpImageGrid();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800579 mImageGrid.setAccessibilityDelegateCompat(
580 new WallpaperPickerRecyclerViewAccessibilityDelegate(
581 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700582 }
583
584 /**
585 * Create the adapter and assign it to mImageGrid.
586 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
587 */
588 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800589 mAdapter = new IndividualAdapter(mWallpapers);
590 mImageGrid.setAdapter(mAdapter);
591 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
592 }
593
Jon Miranda16ea1b12017-12-12 14:52:48 -0800594 /**
595 * Enables and populates the "Currently set" wallpaper BottomSheet.
596 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800597 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800598 mImageGrid.addOnScrollListener(new OnScrollListener() {
599 @Override
600 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
601 if (mCurrentWallpaperBottomSheetPresenter == null) {
602 return;
603 }
604
605 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
606 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
607 }
608 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
609 @Override
610 public void run() {
611 if (dy > 0) {
612 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
613 } else {
614 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
615 }
616 }
617 };
618 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
619 }
620 });
621 }
622
chihhangchuang3efb6832020-04-17 02:06:25 +0800623 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800624 public void onResume() {
625 super.onResume();
626
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700627 WallpaperPreferences preferences = InjectorProvider.getInjector()
628 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800629 preferences.setLastAppActiveTimestamp(new Date().getTime());
630
631 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
632 // PreviewFragment.
633 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
634
635 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
636 // shown earlier when this fragment's hosting activity didn't allow committing fragment
637 // transactions.
638 if (mStagedStartRotationErrorDialogFragment != null) {
639 mStagedStartRotationErrorDialogFragment.show(
640 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
641 mStagedStartRotationErrorDialogFragment = null;
642 }
643
644 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
645 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
646 // committing fragment transactions.
647 if (mStagedSetWallpaperErrorDialogFragment != null) {
648 mStagedSetWallpaperErrorDialogFragment.show(
649 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
650 mStagedSetWallpaperErrorDialogFragment = null;
651 }
652
Chuck Liao5a4243b2020-05-20 23:56:39 +0800653 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
654 // Must be resuming from a previously stopped state, so re-schedule the update of the
655 // daily wallpapers tile thumbnail.
656 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800657 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800658 }
659
660 @Override
661 public void onStop() {
662 super.onStop();
663 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
664 }
665
666 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800667 public void onDestroyView() {
668 super.onDestroyView();
669 getIndividualPickerFragmentHost().removeToolbarMenu();
670 }
671
672 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800673 public void onDestroy() {
674 super.onDestroy();
675 if (mProgressDialog != null) {
676 mProgressDialog.dismiss();
677 }
678 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700679 if (mAppStatusListener != null) {
680 mPackageStatusNotifier.removeListener(mAppStatusListener);
681 }
Chuck Liao69630f12020-03-05 19:01:25 +0800682 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800683 }
684
685 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800686 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800687 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
688 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
689 // still holds a reference to the destroyed Fragment and is calling
690 // onStartRotationDialogDismissed on that destroyed Fragment.
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800691 }
692
693 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800694 public void retryStartRotation(@NetworkPreference int networkPreference) {
695 startRotation(networkPreference);
696 }
697
698 public void setCurrentWallpaperBottomSheetPresenter(
699 CurrentWallpaperBottomSheetPresenter presenter) {
700 mCurrentWallpaperBottomSheetPresenter = presenter;
701 }
702
703 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
704 mWallpapersUiContainer = uiContainer;
705 }
706
Chuck Liaob3829fb2020-04-01 00:47:50 +0800707 public void setOnWallpaperSelectedListener(
708 WallpaperSelectedListener wallpaperSelectedListener) {
709 mWallpaperSelectedListener = wallpaperSelectedListener;
710 }
711
712 /**
713 * Resizes the layout's height.
714 */
715 public void resizeLayout(int height) {
716 mImageGrid.getLayoutParams().height = height;
717 mImageGrid.requestLayout();
718 }
719
720 /**
721 * Scrolls to the specific item.
722 *
723 * @param position the position of the item
724 */
725 public void scrollToPosition(int position) {
726 ((GridLayoutManager) mImageGrid.getLayoutManager())
727 .scrollToPositionWithOffset(position, /* offset= */ 0);
728 }
729
Jon Miranda16ea1b12017-12-12 14:52:48 -0800730 /**
731 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
732 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
733 * constantly keeps the UI thread alive and blocks a test forever.
734 *
735 * @param testingMode
736 */
737 void setTestingMode(boolean testingMode) {
738 mTestingMode = testingMode;
739 }
740
Jon Miranda16ea1b12017-12-12 14:52:48 -0800741 @Override
742 public void startRotation(@NetworkPreference final int networkPreference) {
743 if (!isRotationEnabled()) {
744 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
745 return;
746 }
747
748 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
749 // causes Espresso to hang once the dialog is shown.
750 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
751 int themeResId;
752 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
753 themeResId = R.style.ProgressDialogThemePreL;
754 } else {
755 themeResId = R.style.LightDialogTheme;
756 }
757 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
758
759 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
760 mProgressDialog.setMessage(
761 getResources().getString(R.string.start_rotation_progress_message));
762 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
763 mProgressDialog.show();
764 }
765
766 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
767 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
768 }
769
770 final Context appContext = getActivity().getApplicationContext();
771
772 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
773 appContext,
774 networkPreference,
775 new Listener() {
776 @Override
777 public void onFirstWallpaperInRotationSet() {
778 if (mProgressDialog != null) {
779 mProgressDialog.dismiss();
780 }
781
782 // The fragment may be detached from its containing activity if the user exits the
783 // app before the first wallpaper image in rotation finishes downloading.
784 Activity activity = getActivity();
785
Jon Miranda16ea1b12017-12-12 14:52:48 -0800786
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700787 if (mWallpaperRotationInitializer.startRotation(appContext)) {
788 if (activity != null
789 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
790 try {
791 Toast.makeText(getActivity(),
792 R.string.wallpaper_set_successfully_message,
793 Toast.LENGTH_SHORT).show();
794 } catch (NotFoundException e) {
795 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800796 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800797
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700798 activity.setResult(Activity.RESULT_OK);
799 activity.finish();
800 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
801 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
802 }
803 } else { // Failed to start rotation.
804 showStartRotationErrorDialog(networkPreference);
805
806 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
807 DesktopRotationHolder rotationViewHolder =
808 (DesktopRotationHolder)
809 mImageGrid.findViewHolderForAdapterPosition(
810 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
811 rotationViewHolder.setSelectionState(
812 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800813 }
814 }
815 }
816
817 @Override
818 public void onError() {
819 if (mProgressDialog != null) {
820 mProgressDialog.dismiss();
821 }
822
823 showStartRotationErrorDialog(networkPreference);
824
825 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
826 DesktopRotationHolder rotationViewHolder =
827 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
828 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
829 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
830 }
831 }
832 });
833 }
834
835 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800836 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800837 if (activity != null) {
838 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
839 StartRotationErrorDialogFragment.newInstance(networkPreference);
840 startRotationErrorDialogFragment.setTargetFragment(
841 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
842
843 if (activity.isSafeToCommitFragmentTransaction()) {
844 startRotationErrorDialogFragment.show(
845 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
846 } else {
847 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
848 }
849 }
850 }
851
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800852 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800853 Activity activity = getActivity();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800854 if (activity == null) {
855 return 1;
856 }
857 return mCategoryProvider.isFeaturedCategory(mCategory)
858 ? SizeCalculator.getNumFeaturedIndividualColumns(activity)
859 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800860 }
861
862 /**
863 * Returns whether rotation is enabled for this category.
864 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800865 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700866 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800867 }
868
869 @Override
870 public void onCurrentWallpaperRefreshed() {
871 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
872 }
873
Chuck Liao69630f12020-03-05 19:01:25 +0800874 @Override
875 public void onSet(int destination) {
876 if (mSelectedWallpaperInfo == null) {
877 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
878 return;
879 }
880
“Chuckffd832c2020-03-22 02:15:58 +0800881 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800882 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
883 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
884 destination, 0, null, mSetWallpaperCallback);
885 } else {
886 mWallpaperSetter.setCurrentWallpaper(
887 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
888 }
“Chuck7ef99722020-03-22 04:34:03 +0800889 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800890 }
891
892 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
893 new WallpaperPersister.SetWallpaperCallback() {
894 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800895 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800896 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800897 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
898 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800899 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
900 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800901 }
902
903 @Override
904 public void onError(@Nullable Throwable throwable) {
905 Log.e(TAG, "Can't apply the wallpaper.");
Chuck Liao69630f12020-03-05 19:01:25 +0800906 }
907 };
908
909 @Override
Chuck Liao33f1af42021-04-13 20:52:35 +0800910 public boolean onMenuItemClick(MenuItem item) {
911 if (item.getItemId() == R.id.daily_rotation) {
Chuck Liaoa7215812021-04-13 22:33:43 +0800912 showRotationDialog();
Chuck Liao33f1af42021-04-13 20:52:35 +0800913 return true;
914 }
915 return super.onMenuItemClick(item);
916 }
917
Jon Miranda16ea1b12017-12-12 14:52:48 -0800918 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800919 * Popups a daily rotation dialog for the uses to confirm.
920 */
921 public void showRotationDialog() {
922 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
923 startRotationDialogFragment.setTargetFragment(
924 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
925 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
926 }
927
928 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800929 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
930 */
931 private void showSetWallpaperErrorDialog() {
932 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
933 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
934 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
935
936 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
937 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
938 } else {
939 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
940 }
941 }
942
Chuck Liao69630f12020-03-05 19:01:25 +0800943 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800944 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800945 if (thumbnailUpdater == null) {
946 return;
947 }
948
949 if (selectedWallpaperInfo != null) {
950 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
951 } else {
952 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800953 }
954 }
955
“Chuck7ef99722020-03-22 04:34:03 +0800956 private void onWallpaperDestinationSet(int destination) {
957 WallpaperDestinationCallback wallpaperDestinationCallback =
958 (WallpaperDestinationCallback) getParentFragment();
959 if (wallpaperDestinationCallback == null) {
960 return;
961 }
962
963 wallpaperDestinationCallback.onDestinationSet(destination);
964 }
965
Ching-Sung Li073812b2020-04-07 21:19:21 +0800966 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800967 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800968 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800969 return;
970 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800971 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +0800972 updateActivatedStatus(mSelectedWallpaperInfo == null
973 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800974 // Update new selected wallpaper.
975 updateActivatedStatus(newSelectedWallpaperInfo == null
976 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
977
978 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800979 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +0800980 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +0800981 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +0800982 WallpaperInfoHelper.loadExploreIntent(
983 getContext(),
984 mSelectedWallpaperInfo,
985 (actionLabel, exploreIntent) ->
986 mWallpaperInfoView.populateWallpaperInfo(
Chuck Liao9991d682021-01-20 17:45:14 +0800987 mSelectedWallpaperInfo,
988 actionLabel,
989 WallpaperInfoHelper.shouldShowExploreButton(
990 getContext(), exploreIntent),
991 v -> onExploreClicked(exploreIntent))
992 );
Chuck Liaob3829fb2020-04-01 00:47:50 +0800993 }
994
995 if (mWallpaperSelectedListener != null) {
996 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800997 }
Chuck Liaof40063f2020-03-03 18:35:24 +0800998 }
999
chihhangchuangc687d912020-05-04 14:33:05 +08001000 private void onExploreClicked(Intent exploreIntent) {
1001 if (getContext() == null) {
1002 return;
1003 }
1004 Context context = getContext();
1005 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
1006 mSelectedWallpaperInfo.getActionLabelRes(context));
1007
1008 startActivity(exploreIntent);
1009 }
1010
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001011 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +08001012 if (wallpaperInfo == null) {
1013 return;
1014 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001015 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001016 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001017 ? index + 1 : index;
1018 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1019 if (holder != null) {
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001020 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001021 thumbnail.setClipped(isActivated);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001022 } else {
1023 // Item is not visible, make sure the item is re-bound when it becomes visible.
1024 mAdapter.notifyItemChanged(index);
1025 }
1026 }
1027
“Chuckffd832c2020-03-22 02:15:58 +08001028 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1029 if (wallpaperInfo == null) {
1030 return;
1031 }
1032 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001033 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001034 ? index + 1 : index;
1035 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001036 if (holder == null) {
“Chuckffd832c2020-03-22 02:15:58 +08001037 // Item is not visible, make sure the item is re-bound when it becomes visible.
1038 mAdapter.notifyItemChanged(index);
1039 }
1040 }
1041
Chuck Liao11f4a762020-04-08 13:24:43 +08001042 private void refreshAppliedWallpaper() {
1043 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1044 showCheckMarkAndBorderForAppliedWallpaper(false);
1045
1046 // Update to the new applied wallpaper.
1047 String appliedWallpaperId = getAppliedWallpaperId();
1048 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1049 .stream()
1050 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1051 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1052 .findFirst();
1053 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1054
1055 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1056 showCheckMarkAndBorderForAppliedWallpaper(true);
1057 }
1058
1059 private String getAppliedWallpaperId() {
1060 WallpaperPreferences prefs =
1061 InjectorProvider.getInjector().getPreferences(getContext());
1062 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1063 boolean isDestinationBoth =
1064 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1065
1066 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1067 return wallpaperInfo != null
1068 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1069 } else {
1070 return prefs.getLockWallpaperRemoteId();
1071 }
1072 }
1073
1074 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1075 updateAppliedStatus(mAppliedWallpaperInfo, show);
1076 if (mSelectedWallpaperInfo == null) {
1077 updateActivatedStatus(mAppliedWallpaperInfo, show);
1078 }
1079 }
1080
Chuck Liao5a4243b2020-05-20 23:56:39 +08001081 private boolean shouldShowRotationTile() {
1082 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001083 }
1084
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001085 class EmptySelectionAnimator implements SelectionAnimator{
1086 EmptySelectionAnimator() {}
1087
1088 public boolean isSelected() {
1089 return false;
1090 }
1091
1092 /**
1093 * Sets the UI to selected immediately with no animation.
1094 */
1095 public void selectImmediately() {}
1096
1097 /**
1098 * Sets the UI to deselected immediately with no animation.
1099 */
1100 public void deselectImmediately() {}
1101
1102 /**
1103 * Sets the UI to selected with a smooth animation.
1104 */
1105 public void animateSelected() {}
1106
1107 /**
1108 * Sets the UI to deselected with a smooth animation.
1109 */
1110 public void animateDeselected() {}
1111
1112 /**
1113 * Sets the UI to show a loading indicator.
1114 */
1115 public void showLoading() {}
1116
1117 /**
1118 * Sets the UI to hide the loading indicator.
1119 */
1120 public void showNotLoading() {}
1121
1122 }
1123
Jon Miranda16ea1b12017-12-12 14:52:48 -08001124 /**
1125 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1126 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001127 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1128 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1129 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1130 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001131
1132 private final List<WallpaperInfo> mWallpapers;
1133
1134 private int mPendingSelectedAdapterPosition;
1135 private int mSelectedAdapterPosition;
1136
1137 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1138 mWallpapers = wallpapers;
1139 mPendingSelectedAdapterPosition = -1;
1140 mSelectedAdapterPosition = -1;
1141 }
1142
1143 @Override
1144 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1145 switch (viewType) {
1146 case ITEM_VIEW_TYPE_ROTATION:
1147 return createRotationHolder(parent);
1148 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1149 return createIndividualHolder(parent);
1150 case ITEM_VIEW_TYPE_MY_PHOTOS:
1151 return createMyPhotosHolder(parent);
1152 default:
1153 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1154 return null;
1155 }
1156 }
1157
1158 @Override
1159 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001160 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001161 return ITEM_VIEW_TYPE_ROTATION;
1162 }
1163
1164 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1165 if (mCategory.supportsCustomPhotos()
1166 && !isRotationEnabled()
1167 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1168 return ITEM_VIEW_TYPE_MY_PHOTOS;
1169 }
1170
1171 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1172 }
1173
1174 @Override
1175 public void onBindViewHolder(ViewHolder holder, int position) {
1176 int viewType = getItemViewType(position);
1177
1178 switch (viewType) {
1179 case ITEM_VIEW_TYPE_ROTATION:
1180 onBindRotationHolder(holder, position);
1181 break;
1182 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1183 onBindIndividualHolder(holder, position);
1184 break;
1185 case ITEM_VIEW_TYPE_MY_PHOTOS:
1186 ((MyPhotosViewHolder) holder).bind();
1187 break;
1188 default:
1189 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1190 }
1191 }
1192
1193 @Override
1194 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001195 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001196 ? mWallpapers.size() + 1
1197 : mWallpapers.size();
1198 }
1199
1200 private ViewHolder createRotationHolder(ViewGroup parent) {
1201 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001202 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001203 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Chuck Liao5a4243b2020-05-20 23:56:39 +08001204 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1205 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001206 }
1207
1208 private ViewHolder createIndividualHolder(ViewGroup parent) {
1209 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1210 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1211
1212 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001213 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001214 return new SetIndividualHolder(
1215 getActivity(), mTileSizePx.y, view,
1216 selectionAnimator,
1217 new OnSetListener() {
1218 @Override
1219 public void onPendingWallpaperSet(int adapterPosition) {
1220 // Deselect and hide loading indicator for any previously pending tile.
1221 if (mPendingSelectedAdapterPosition != -1) {
1222 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1223 mPendingSelectedAdapterPosition);
1224 if (oldViewHolder instanceof SelectableHolder) {
1225 ((SelectableHolder) oldViewHolder).setSelectionState(
1226 SelectableHolder.SELECTION_STATE_DESELECTED);
1227 }
1228 }
1229
1230 if (mSelectedAdapterPosition != -1) {
1231 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1232 mSelectedAdapterPosition);
1233 if (oldViewHolder instanceof SelectableHolder) {
1234 ((SelectableHolder) oldViewHolder).setSelectionState(
1235 SelectableHolder.SELECTION_STATE_DESELECTED);
1236 }
1237 }
1238
1239 mPendingSelectedAdapterPosition = adapterPosition;
1240 }
1241
1242 @Override
1243 public void onWallpaperSet(int adapterPosition) {
1244 // No-op -- UI handles a new wallpaper being set by reacting to the
1245 // WallpaperChangedNotifier.
1246 }
1247
1248 @Override
1249 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1250 showSetWallpaperErrorDialog();
1251 mPendingSetIndividualHolder = holder;
1252 }
1253 });
1254 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001255 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001256 }
1257 }
1258
1259 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1260 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1261 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1262
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001263 return new MyPhotosViewHolder(getActivity(),
1264 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1265 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001266 }
1267
1268 /**
1269 * Marks the tile at the given position as selected with a visual indication. Also updates the
1270 * "currently selected" BottomSheet to reflect the newly selected tile.
1271 */
1272 private void updateSelectedTile(int newlySelectedPosition) {
1273 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1274 // succession.
1275 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1276 return;
1277 }
1278
1279 if (mCurrentWallpaperBottomSheetPresenter != null) {
1280 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1281 IndividualPickerFragment.this);
1282
1283 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1284 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1285 }
1286 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1287 @Override
1288 public void run() {
1289 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1290 }
1291 };
1292 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1293 }
1294
1295 // User may have switched to another category, thus detaching this fragment, so check here.
1296 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1297 // still occurs in the UI after the user selects that other category.
1298 if (getActivity() == null) {
1299 return;
1300 }
1301
1302 // Update the newly selected wallpaper ViewHolder and the old one so that if
1303 // selection UI state applies (desktop UI), it is updated.
1304 if (mSelectedAdapterPosition >= 0) {
1305 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1306 mSelectedAdapterPosition);
1307 if (oldViewHolder instanceof SelectableHolder) {
1308 ((SelectableHolder) oldViewHolder).setSelectionState(
1309 SelectableHolder.SELECTION_STATE_DESELECTED);
1310 }
1311 }
1312
1313 // Animate selection of newly selected tile.
1314 ViewHolder newViewHolder = mImageGrid
1315 .findViewHolderForAdapterPosition(newlySelectedPosition);
1316 if (newViewHolder instanceof SelectableHolder) {
1317 ((SelectableHolder) newViewHolder).setSelectionState(
1318 SelectableHolder.SELECTION_STATE_SELECTED);
1319 }
1320
1321 mSelectedAdapterPosition = newlySelectedPosition;
1322
1323 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1324 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1325 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1326 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1327 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1328 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1329
1330 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1331 }
1332
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001333 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001334 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1335 String collectionId = mCategory.getCollectionId();
1336 ((DesktopRotationHolder) holder).bind(collectionId);
1337
1338 if (mWallpaperPreferences.getWallpaperPresentationMode()
1339 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1340 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1341 mSelectedAdapterPosition = position;
1342 }
1343
1344 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1345 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1346 mWasUpdateRunnableRun = true;
1347 }
1348 }
1349 }
1350
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001351 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001352 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001353 ? position - 1 : position;
1354 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1355 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001356 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001357 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1358 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1359 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001360
“Chuckffd832c2020-03-22 02:15:58 +08001361 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001362 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001363 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001364 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001365
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001366 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001367 thumbnail.setClipped(isWallpaperApplied);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001368 }
1369 }
Chuck Liaoba401232020-03-13 20:11:04 +08001370
1371 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1372
1373 private int mPadding;
1374
1375 GridPaddingDecoration(int padding) {
1376 mPadding = padding;
1377 }
1378
1379 @Override
1380 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1381 RecyclerView.State state) {
1382 int position = parent.getChildAdapterPosition(view);
1383 if (position >= 0) {
1384 outRect.left = mPadding;
1385 outRect.right = mPadding;
1386 }
1387 }
1388 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001389}