blob: fda20a64cc907096d81707f40bb4b314d526233a [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";
Chuck Liaob10d2a32021-04-27 20:59:53 +0800128 private static final int MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT = 8;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800129
Chuck Liao8ec38e02020-02-26 20:59:32 +0800130 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800131 * An interface for updating the thumbnail with the specific wallpaper.
132 */
133 public interface ThumbnailUpdater {
134 /**
135 * Updates the thumbnail with the specific wallpaper.
136 */
137 void updateThumbnail(WallpaperInfo wallpaperInfo);
138
139 /**
140 * Restores to the thumbnails of the wallpapers which were applied.
141 */
142 void restoreThumbnails();
143 }
144
“Chuck7ef99722020-03-22 04:34:03 +0800145 /**
146 * An interface for receiving the destination of the new applied wallpaper.
147 */
148 public interface WallpaperDestinationCallback {
149 /**
150 * Called when the destination of the wallpaper is set.
151 *
152 * @param destination the destination which a wallpaper may be set.
153 * See {@link Destination} for more details.
154 */
155 void onDestinationSet(@Destination int destination);
156 }
157
Chuck Liaob3829fb2020-04-01 00:47:50 +0800158 /**
159 * The listener which will be notified when the wallpaper is selected.
160 */
161 public interface WallpaperSelectedListener {
162 /**
163 * Called when the wallpaper is selected.
164 *
165 * @param position the position of the selected wallpaper
166 */
167 void onWallpaperSelected(int position);
168 }
169
Chuck Liaof6b4b192020-08-07 02:31:32 +0800170 /**
Chuck Liao7e333722021-02-27 02:21:20 +0800171 * Interface to be implemented by a Fragment(or an Activity) hosting
172 * a {@link IndividualPickerFragment}.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800173 */
174 public interface IndividualPickerFragmentHost {
175 /**
Chuck Liao58aca1c2021-03-17 01:20:55 +0800176 * Indicates if the host has toolbar to show the title. If it does, we should set the title
177 * there.
178 */
179 boolean isHostToolbarShown();
180
181 /**
182 * Sets the title in the host's toolbar.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800183 */
184 void setToolbarTitle(CharSequence title);
185
186 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800187 * Configures the menu in the toolbar.
188 *
189 * @param menuResId the resource id of the menu
190 */
191 void setToolbarMenu(@MenuRes int menuResId);
192
193 /**
194 * Removes the menu in the toolbar.
195 */
196 void removeToolbarMenu();
197
198 /**
Chuck Liaof6b4b192020-08-07 02:31:32 +0800199 * Moves to the previous fragment.
200 */
201 void moveToPreviousFragment();
202 }
203
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800204 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800205 WallpaperPreferences mWallpaperPreferences;
206 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800207 RecyclerView mImageGrid;
208 IndividualAdapter mAdapter;
209 WallpaperCategory mCategory;
210 WallpaperRotationInitializer mWallpaperRotationInitializer;
211 List<WallpaperInfo> mWallpapers;
212 Point mTileSizePx;
213 WallpapersUiContainer mWallpapersUiContainer;
214 @FormFactor
215 int mFormFactor;
216 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800217
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800218 Handler mHandler;
219 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800220 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800221
222 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
223 new WallpaperChangedNotifier.Listener() {
224 @Override
225 public void onWallpaperChanged() {
226 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
227 return;
228 }
229
230 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
231 mAdapter.mSelectedAdapterPosition);
232
233 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
234 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
235 if (selectedViewHolder instanceof SelectableHolder) {
236 ((SelectableHolder) selectedViewHolder).setSelectionState(
237 SelectableHolder.SELECTION_STATE_DESELECTED);
238 }
239 } else {
240 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
241 }
242 }
243 };
244 PackageStatusNotifier.Listener mAppStatusListener;
chihhangchuang1a29e752020-04-28 18:22:53 +0800245 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800246 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800247
chihhangchuangc687d912020-05-04 14:33:05 +0800248 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800249 private ProgressDialog mProgressDialog;
250 private boolean mTestingMode;
251 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800252 private SetIndividualHolder mPendingSetIndividualHolder;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800253 private ContentLoadingProgressBar mLoading;
Chuck Liaoddf2b522021-04-15 00:36:25 +0800254 private CategoryProvider mCategoryProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800255
256 /**
257 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
258 * committing fragment transactions.
259 */
260 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
261 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
262
Jon Miranda16ea1b12017-12-12 14:52:48 -0800263 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800264
265 /**
266 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
267 * invocation of the fragment.
268 */
269 private boolean mWasUpdateRunnableRun;
270
271 /**
272 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
273 * mode.
274 */
275 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
276 @Override
277 public void run() {
278 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
279 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
280 if (viewHolder instanceof DesktopRotationHolder) {
281 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
282 } else { // viewHolder is null
283 // If the rotation tile is unavailable (because user has scrolled down, causing the
284 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
285 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
286 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
287 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
288 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
289 }
290 }
291 };
292
Chuck Liao69630f12020-03-05 19:01:25 +0800293 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800294 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800295 private WallpaperManager mWallpaperManager;
296 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800297 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800298
Jon Miranda16ea1b12017-12-12 14:52:48 -0800299 public static IndividualPickerFragment newInstance(String collectionId) {
300 Bundle args = new Bundle();
301 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
302
303 IndividualPickerFragment fragment = new IndividualPickerFragment();
304 fragment.setArguments(args);
305 return fragment;
306 }
307
“Chuck7ef99722020-03-22 04:34:03 +0800308 /**
309 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
310 * would be set.
311 *
312 * @param wallpaperDestination the destination a wallpaper would be set.
313 * It will be either {@link Destination#DEST_HOME_SCREEN}
314 * or {@link Destination#DEST_LOCK_SCREEN}.
315 */
316 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
317 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800318 if (mWallpapers != null) {
319 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800320 }
321 }
322
Jon Miranda16ea1b12017-12-12 14:52:48 -0800323 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
324 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
325 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
326 getActivity());
327 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
328 @Override
329 public void onDrawableLoaded() {
330 if (getActivity() == null) {
331 return;
332 }
333
334 // Schedule the next update of the thumbnail.
335 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
336 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
337 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
338 }
339 });
340 }
341
342 @Override
343 public void onCreate(Bundle savedInstanceState) {
344 super.onCreate(savedInstanceState);
345
346 Injector injector = InjectorProvider.getInjector();
347 Context appContext = getContext().getApplicationContext();
348 mWallpaperPreferences = injector.getPreferences(appContext);
349
350 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
351 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
352
“Chuck7ef99722020-03-22 04:34:03 +0800353 mWallpaperManager = WallpaperManager.getInstance(appContext);
354
Jon Miranda16ea1b12017-12-12 14:52:48 -0800355 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
356
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700357 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
358
chihhangchuangc687d912020-05-04 14:33:05 +0800359 mUserEventLogger = injector.getUserEventLogger(appContext);
360
“Chuckffd832c2020-03-22 02:15:58 +0800361 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800362 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800363 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800364 injector.getPreferences(appContext),
365 injector.getUserEventLogger(appContext),
366 false);
367
Jon Miranda16ea1b12017-12-12 14:52:48 -0800368 mWallpapers = new ArrayList<>();
369 mRandom = new Random();
370 mHandler = new Handler();
371
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700372 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
373 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
374 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
375 Glide.get(getContext()).clearMemory();
376 }
377
Chuck Liaoddf2b522021-04-15 00:36:25 +0800378 mCategoryProvider = injector.getCategoryProvider(appContext);
379 mCategoryProvider.fetchCategories(new CategoryReceiver() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700380 @Override
381 public void onCategoryReceived(Category category) {
382 // Do nothing.
383 }
384
385 @Override
386 public void doneFetchingCategories() {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800387 Category category = mCategoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700388 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800389 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800390 return;
391 }
392 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700393 if (mCategory == null) {
394 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
395
396 // The absence of this category in the CategoryProvider indicates a broken
397 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800398 getIndividualPickerFragmentHost().moveToPreviousFragment();
399 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
400 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700401 return;
402 }
403 onCategoryLoaded();
404 }
405 }, false);
406 }
407
Chuck Liaof6b4b192020-08-07 02:31:32 +0800408
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700409 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800410 if (getIndividualPickerFragmentHost() == null) {
411 return;
412 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800413 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
414 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
415 } else {
416 setTitle(mCategory.getTitle());
417 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700418 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao33f1af42021-04-13 20:52:35 +0800419 if (mToolbar != null && isRotationEnabled()) {
420 setUpToolbarMenu(R.menu.individual_picker_menu);
421 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700422 fetchWallpapers(false);
423
424 if (mCategory.supportsThirdParty()) {
425 mAppStatusListener = (packageName, status) -> {
426 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
427 mCategory.containsThirdParty(packageName)) {
428 fetchWallpapers(true);
429 }
430 };
431 mPackageStatusNotifier.addListener(mAppStatusListener,
432 WallpaperService.SERVICE_INTERFACE);
433 }
434 }
435
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800436 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700437 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800438 mIsWallpapersReceived = false;
439 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800440 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
441 @Override
442 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800443 mIsWallpapersReceived = true;
444 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800445 for (WallpaperInfo wallpaper : wallpapers) {
446 mWallpapers.add(wallpaper);
447 }
Chuck Liaob10d2a32021-04-27 20:59:53 +0800448 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800449
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 Liaof6b4b192020-08-07 02:31:32 +0800518 mLoading = view.findViewById(R.id.loading_indicator);
519 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700520 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800521 setUpBottomSheet();
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800522 // For nav bar edge-to-edge effect.
Kunhung Li1b0cb472021-04-26 20:52:04 +0800523 view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
524 // For status bar height.
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800525 v.setPadding(
526 v.getPaddingLeft(),
Kunhung Li1b0cb472021-04-26 20:52:04 +0800527 windowInsets.getSystemWindowInsetTop(),
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800528 v.getPaddingRight(),
Kunhung Li1b0cb472021-04-26 20:52:04 +0800529 v.getPaddingBottom());
530
531 View gridView = v.findViewById(R.id.wallpaper_grid);
532 gridView.setPadding(
533 gridView.getPaddingLeft(),
534 gridView.getPaddingTop(),
535 gridView.getPaddingRight(),
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800536 windowInsets.getSystemWindowInsetBottom());
537 return windowInsets.consumeSystemWindowInsets();
538 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800539 return view;
540 }
541
542 @Override
543 public void onClickTryAgain(@Destination int unused) {
544 if (mPendingSetIndividualHolder != null) {
545 mPendingSetIndividualHolder.setWallpaper();
546 }
547 }
548
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800549 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800550 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
551 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
552 R.dimen.current_wallpaper_bottom_sheet_layout_height);
553 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
554 // Only left and top may be set in order for the GridMarginDecoration to work properly.
555 mImageGrid.setPadding(
556 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
557 }
558
Chuck Liaof6b4b192020-08-07 02:31:32 +0800559 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
Chuck Liao7e333722021-02-27 02:21:20 +0800560 Fragment parentFragment = getParentFragment();
561 if (parentFragment != null) {
562 return (IndividualPickerFragmentHost) parentFragment;
563 } else {
564 return (IndividualPickerFragmentHost) getActivity();
565 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800566 }
567
Chuck Liaob10d2a32021-04-27 20:59:53 +0800568 protected void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700569 // Skip if mImageGrid been initialized yet
570 if (mImageGrid == null) {
571 return;
572 }
573 // Skip if category hasn't loaded yet
574 if (mCategory == null) {
575 return;
576 }
577 // Skip if the adapter was already created
578 if (mAdapter != null) {
579 return;
580 }
Chuck Liao1e6cb682021-04-22 00:51:11 +0800581 mImageGrid.addItemDecoration(new GridPaddingDecoration(getGridItemPaddingHorizontal(),
582 getGridItemPaddingBottom()));
583 int edgePadding = getEdgePadding();
584 mImageGrid.setPadding(edgePadding, mImageGrid.getPaddingTop(), edgePadding,
585 mImageGrid.getPaddingBottom());
Chuck Liaob10d2a32021-04-27 20:59:53 +0800586 mTileSizePx = isFewerColumnLayout()
Chuck Liaoddf2b522021-04-15 00:36:25 +0800587 ? SizeCalculator.getFeaturedIndividualTileSize(getActivity())
588 : SizeCalculator.getIndividualTileSize(getActivity());
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700589 setUpImageGrid();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800590 mImageGrid.setAccessibilityDelegateCompat(
591 new WallpaperPickerRecyclerViewAccessibilityDelegate(
592 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700593 }
594
Chuck Liaob10d2a32021-04-27 20:59:53 +0800595 private boolean isFewerColumnLayout() {
596 return mWallpapers != null && mWallpapers.size() <= MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT;
597 }
598
Chuck Liao1e6cb682021-04-22 00:51:11 +0800599 private int getGridItemPaddingHorizontal() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800600 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800601 ? getResources().getDimensionPixelSize(
602 R.dimen.grid_item_featured_individual_padding_horizontal)
603 : getResources().getDimensionPixelSize(
604 R.dimen.grid_item_individual_padding_horizontal);
605 }
606
607 private int getGridItemPaddingBottom() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800608 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800609 ? getResources().getDimensionPixelSize(
610 R.dimen.grid_item_featured_individual_padding_bottom)
611 : getResources().getDimensionPixelSize(R.dimen.grid_item_individual_padding_bottom);
612 }
613
614 private int getEdgePadding() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800615 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800616 ? getResources().getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space)
617 : getResources().getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space);
618 }
619
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700620 /**
621 * Create the adapter and assign it to mImageGrid.
622 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
623 */
624 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800625 mAdapter = new IndividualAdapter(mWallpapers);
626 mImageGrid.setAdapter(mAdapter);
627 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
628 }
629
Jon Miranda16ea1b12017-12-12 14:52:48 -0800630 /**
631 * Enables and populates the "Currently set" wallpaper BottomSheet.
632 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800633 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800634 mImageGrid.addOnScrollListener(new OnScrollListener() {
635 @Override
636 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
637 if (mCurrentWallpaperBottomSheetPresenter == null) {
638 return;
639 }
640
641 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
642 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
643 }
644 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
645 @Override
646 public void run() {
647 if (dy > 0) {
648 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
649 } else {
650 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
651 }
652 }
653 };
654 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
655 }
656 });
657 }
658
chihhangchuang3efb6832020-04-17 02:06:25 +0800659 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800660 public void onResume() {
661 super.onResume();
662
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700663 WallpaperPreferences preferences = InjectorProvider.getInjector()
664 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800665 preferences.setLastAppActiveTimestamp(new Date().getTime());
666
667 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
668 // PreviewFragment.
669 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
670
671 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
672 // shown earlier when this fragment's hosting activity didn't allow committing fragment
673 // transactions.
674 if (mStagedStartRotationErrorDialogFragment != null) {
675 mStagedStartRotationErrorDialogFragment.show(
676 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
677 mStagedStartRotationErrorDialogFragment = null;
678 }
679
680 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
681 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
682 // committing fragment transactions.
683 if (mStagedSetWallpaperErrorDialogFragment != null) {
684 mStagedSetWallpaperErrorDialogFragment.show(
685 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
686 mStagedSetWallpaperErrorDialogFragment = null;
687 }
688
Chuck Liao5a4243b2020-05-20 23:56:39 +0800689 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
690 // Must be resuming from a previously stopped state, so re-schedule the update of the
691 // daily wallpapers tile thumbnail.
692 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800693 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800694 }
695
696 @Override
697 public void onStop() {
698 super.onStop();
699 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
700 }
701
702 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800703 public void onDestroyView() {
704 super.onDestroyView();
705 getIndividualPickerFragmentHost().removeToolbarMenu();
706 }
707
708 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800709 public void onDestroy() {
710 super.onDestroy();
711 if (mProgressDialog != null) {
712 mProgressDialog.dismiss();
713 }
714 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700715 if (mAppStatusListener != null) {
716 mPackageStatusNotifier.removeListener(mAppStatusListener);
717 }
Chuck Liao69630f12020-03-05 19:01:25 +0800718 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800719 }
720
721 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800722 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800723 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
724 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
725 // still holds a reference to the destroyed Fragment and is calling
726 // onStartRotationDialogDismissed on that destroyed Fragment.
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800727 }
728
729 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800730 public void retryStartRotation(@NetworkPreference int networkPreference) {
731 startRotation(networkPreference);
732 }
733
734 public void setCurrentWallpaperBottomSheetPresenter(
735 CurrentWallpaperBottomSheetPresenter presenter) {
736 mCurrentWallpaperBottomSheetPresenter = presenter;
737 }
738
739 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
740 mWallpapersUiContainer = uiContainer;
741 }
742
Chuck Liaob3829fb2020-04-01 00:47:50 +0800743 public void setOnWallpaperSelectedListener(
744 WallpaperSelectedListener wallpaperSelectedListener) {
745 mWallpaperSelectedListener = wallpaperSelectedListener;
746 }
747
748 /**
749 * Resizes the layout's height.
750 */
751 public void resizeLayout(int height) {
752 mImageGrid.getLayoutParams().height = height;
753 mImageGrid.requestLayout();
754 }
755
756 /**
757 * Scrolls to the specific item.
758 *
759 * @param position the position of the item
760 */
761 public void scrollToPosition(int position) {
762 ((GridLayoutManager) mImageGrid.getLayoutManager())
763 .scrollToPositionWithOffset(position, /* offset= */ 0);
764 }
765
Jon Miranda16ea1b12017-12-12 14:52:48 -0800766 /**
767 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
768 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
769 * constantly keeps the UI thread alive and blocks a test forever.
770 *
771 * @param testingMode
772 */
773 void setTestingMode(boolean testingMode) {
774 mTestingMode = testingMode;
775 }
776
Jon Miranda16ea1b12017-12-12 14:52:48 -0800777 @Override
778 public void startRotation(@NetworkPreference final int networkPreference) {
779 if (!isRotationEnabled()) {
780 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
781 return;
782 }
783
784 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
785 // causes Espresso to hang once the dialog is shown.
786 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
787 int themeResId;
788 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
789 themeResId = R.style.ProgressDialogThemePreL;
790 } else {
791 themeResId = R.style.LightDialogTheme;
792 }
793 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
794
795 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
796 mProgressDialog.setMessage(
797 getResources().getString(R.string.start_rotation_progress_message));
798 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
799 mProgressDialog.show();
800 }
801
802 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
803 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
804 }
805
806 final Context appContext = getActivity().getApplicationContext();
807
808 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
809 appContext,
810 networkPreference,
811 new Listener() {
812 @Override
813 public void onFirstWallpaperInRotationSet() {
814 if (mProgressDialog != null) {
815 mProgressDialog.dismiss();
816 }
817
818 // The fragment may be detached from its containing activity if the user exits the
819 // app before the first wallpaper image in rotation finishes downloading.
820 Activity activity = getActivity();
821
Jon Miranda16ea1b12017-12-12 14:52:48 -0800822
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700823 if (mWallpaperRotationInitializer.startRotation(appContext)) {
824 if (activity != null
825 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
826 try {
827 Toast.makeText(getActivity(),
828 R.string.wallpaper_set_successfully_message,
829 Toast.LENGTH_SHORT).show();
830 } catch (NotFoundException e) {
831 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800832 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800833
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700834 activity.setResult(Activity.RESULT_OK);
835 activity.finish();
836 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
837 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
838 }
839 } else { // Failed to start rotation.
840 showStartRotationErrorDialog(networkPreference);
841
842 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
843 DesktopRotationHolder rotationViewHolder =
844 (DesktopRotationHolder)
845 mImageGrid.findViewHolderForAdapterPosition(
846 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
847 rotationViewHolder.setSelectionState(
848 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800849 }
850 }
851 }
852
853 @Override
854 public void onError() {
855 if (mProgressDialog != null) {
856 mProgressDialog.dismiss();
857 }
858
859 showStartRotationErrorDialog(networkPreference);
860
861 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
862 DesktopRotationHolder rotationViewHolder =
863 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
864 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
865 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
866 }
867 }
868 });
869 }
870
871 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800872 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800873 if (activity != null) {
874 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
875 StartRotationErrorDialogFragment.newInstance(networkPreference);
876 startRotationErrorDialogFragment.setTargetFragment(
877 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
878
879 if (activity.isSafeToCommitFragmentTransaction()) {
880 startRotationErrorDialogFragment.show(
881 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
882 } else {
883 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
884 }
885 }
886 }
887
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800888 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800889 Activity activity = getActivity();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800890 if (activity == null) {
891 return 1;
892 }
Chuck Liaob10d2a32021-04-27 20:59:53 +0800893 return isFewerColumnLayout()
Chuck Liaoddf2b522021-04-15 00:36:25 +0800894 ? SizeCalculator.getNumFeaturedIndividualColumns(activity)
895 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800896 }
897
898 /**
899 * Returns whether rotation is enabled for this category.
900 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800901 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700902 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800903 }
904
905 @Override
906 public void onCurrentWallpaperRefreshed() {
907 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
908 }
909
Chuck Liao69630f12020-03-05 19:01:25 +0800910 @Override
911 public void onSet(int destination) {
912 if (mSelectedWallpaperInfo == null) {
913 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
914 return;
915 }
916
“Chuckffd832c2020-03-22 02:15:58 +0800917 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800918 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
919 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
920 destination, 0, null, mSetWallpaperCallback);
921 } else {
922 mWallpaperSetter.setCurrentWallpaper(
923 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
924 }
“Chuck7ef99722020-03-22 04:34:03 +0800925 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800926 }
927
928 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
929 new WallpaperPersister.SetWallpaperCallback() {
930 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800931 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800932 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800933 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
934 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800935 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
936 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800937 }
938
939 @Override
940 public void onError(@Nullable Throwable throwable) {
941 Log.e(TAG, "Can't apply the wallpaper.");
Chuck Liao69630f12020-03-05 19:01:25 +0800942 }
943 };
944
945 @Override
Chuck Liao33f1af42021-04-13 20:52:35 +0800946 public boolean onMenuItemClick(MenuItem item) {
947 if (item.getItemId() == R.id.daily_rotation) {
Chuck Liaoa7215812021-04-13 22:33:43 +0800948 showRotationDialog();
Chuck Liao33f1af42021-04-13 20:52:35 +0800949 return true;
950 }
951 return super.onMenuItemClick(item);
952 }
953
Jon Miranda16ea1b12017-12-12 14:52:48 -0800954 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800955 * Popups a daily rotation dialog for the uses to confirm.
956 */
957 public void showRotationDialog() {
958 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
959 startRotationDialogFragment.setTargetFragment(
960 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
961 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
962 }
963
964 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800965 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
966 */
967 private void showSetWallpaperErrorDialog() {
968 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
969 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
970 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
971
972 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
973 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
974 } else {
975 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
976 }
977 }
978
Chuck Liao69630f12020-03-05 19:01:25 +0800979 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800980 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800981 if (thumbnailUpdater == null) {
982 return;
983 }
984
985 if (selectedWallpaperInfo != null) {
986 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
987 } else {
988 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800989 }
990 }
991
“Chuck7ef99722020-03-22 04:34:03 +0800992 private void onWallpaperDestinationSet(int destination) {
993 WallpaperDestinationCallback wallpaperDestinationCallback =
994 (WallpaperDestinationCallback) getParentFragment();
995 if (wallpaperDestinationCallback == null) {
996 return;
997 }
998
999 wallpaperDestinationCallback.onDestinationSet(destination);
1000 }
1001
Ching-Sung Li073812b2020-04-07 21:19:21 +08001002 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +08001003 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001004 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +08001005 return;
1006 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001007 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +08001008 updateActivatedStatus(mSelectedWallpaperInfo == null
1009 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001010 // Update new selected wallpaper.
1011 updateActivatedStatus(newSelectedWallpaperInfo == null
1012 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
1013
1014 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001015 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +08001016 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +08001017 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +08001018 WallpaperInfoHelper.loadExploreIntent(
1019 getContext(),
1020 mSelectedWallpaperInfo,
1021 (actionLabel, exploreIntent) ->
1022 mWallpaperInfoView.populateWallpaperInfo(
Chuck Liao9991d682021-01-20 17:45:14 +08001023 mSelectedWallpaperInfo,
1024 actionLabel,
1025 WallpaperInfoHelper.shouldShowExploreButton(
1026 getContext(), exploreIntent),
1027 v -> onExploreClicked(exploreIntent))
1028 );
Chuck Liaob3829fb2020-04-01 00:47:50 +08001029 }
1030
1031 if (mWallpaperSelectedListener != null) {
1032 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001033 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001034 }
1035
chihhangchuangc687d912020-05-04 14:33:05 +08001036 private void onExploreClicked(Intent exploreIntent) {
1037 if (getContext() == null) {
1038 return;
1039 }
1040 Context context = getContext();
1041 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
1042 mSelectedWallpaperInfo.getActionLabelRes(context));
1043
1044 startActivity(exploreIntent);
1045 }
1046
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001047 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +08001048 if (wallpaperInfo == null) {
1049 return;
1050 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001051 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001052 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001053 ? index + 1 : index;
1054 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1055 if (holder != null) {
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001056 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001057 thumbnail.setClipped(isActivated);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001058 } else {
1059 // Item is not visible, make sure the item is re-bound when it becomes visible.
1060 mAdapter.notifyItemChanged(index);
1061 }
1062 }
1063
“Chuckffd832c2020-03-22 02:15:58 +08001064 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1065 if (wallpaperInfo == null) {
1066 return;
1067 }
1068 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001069 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001070 ? index + 1 : index;
1071 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001072 if (holder == null) {
“Chuckffd832c2020-03-22 02:15:58 +08001073 // Item is not visible, make sure the item is re-bound when it becomes visible.
1074 mAdapter.notifyItemChanged(index);
1075 }
1076 }
1077
Chuck Liao11f4a762020-04-08 13:24:43 +08001078 private void refreshAppliedWallpaper() {
1079 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1080 showCheckMarkAndBorderForAppliedWallpaper(false);
1081
1082 // Update to the new applied wallpaper.
1083 String appliedWallpaperId = getAppliedWallpaperId();
1084 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1085 .stream()
1086 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1087 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1088 .findFirst();
1089 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1090
1091 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1092 showCheckMarkAndBorderForAppliedWallpaper(true);
1093 }
1094
1095 private String getAppliedWallpaperId() {
1096 WallpaperPreferences prefs =
1097 InjectorProvider.getInjector().getPreferences(getContext());
1098 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1099 boolean isDestinationBoth =
1100 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1101
1102 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1103 return wallpaperInfo != null
1104 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1105 } else {
1106 return prefs.getLockWallpaperRemoteId();
1107 }
1108 }
1109
1110 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1111 updateAppliedStatus(mAppliedWallpaperInfo, show);
1112 if (mSelectedWallpaperInfo == null) {
1113 updateActivatedStatus(mAppliedWallpaperInfo, show);
1114 }
1115 }
1116
Chuck Liao5a4243b2020-05-20 23:56:39 +08001117 private boolean shouldShowRotationTile() {
1118 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001119 }
1120
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001121 class EmptySelectionAnimator implements SelectionAnimator{
1122 EmptySelectionAnimator() {}
1123
1124 public boolean isSelected() {
1125 return false;
1126 }
1127
1128 /**
1129 * Sets the UI to selected immediately with no animation.
1130 */
1131 public void selectImmediately() {}
1132
1133 /**
1134 * Sets the UI to deselected immediately with no animation.
1135 */
1136 public void deselectImmediately() {}
1137
1138 /**
1139 * Sets the UI to selected with a smooth animation.
1140 */
1141 public void animateSelected() {}
1142
1143 /**
1144 * Sets the UI to deselected with a smooth animation.
1145 */
1146 public void animateDeselected() {}
1147
1148 /**
1149 * Sets the UI to show a loading indicator.
1150 */
1151 public void showLoading() {}
1152
1153 /**
1154 * Sets the UI to hide the loading indicator.
1155 */
1156 public void showNotLoading() {}
1157
1158 }
1159
Jon Miranda16ea1b12017-12-12 14:52:48 -08001160 /**
1161 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1162 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001163 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1164 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1165 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1166 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001167
1168 private final List<WallpaperInfo> mWallpapers;
1169
1170 private int mPendingSelectedAdapterPosition;
1171 private int mSelectedAdapterPosition;
1172
1173 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1174 mWallpapers = wallpapers;
1175 mPendingSelectedAdapterPosition = -1;
1176 mSelectedAdapterPosition = -1;
1177 }
1178
1179 @Override
1180 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1181 switch (viewType) {
1182 case ITEM_VIEW_TYPE_ROTATION:
1183 return createRotationHolder(parent);
1184 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1185 return createIndividualHolder(parent);
1186 case ITEM_VIEW_TYPE_MY_PHOTOS:
1187 return createMyPhotosHolder(parent);
1188 default:
1189 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1190 return null;
1191 }
1192 }
1193
1194 @Override
1195 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001196 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001197 return ITEM_VIEW_TYPE_ROTATION;
1198 }
1199
1200 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1201 if (mCategory.supportsCustomPhotos()
1202 && !isRotationEnabled()
1203 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1204 return ITEM_VIEW_TYPE_MY_PHOTOS;
1205 }
1206
1207 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1208 }
1209
1210 @Override
1211 public void onBindViewHolder(ViewHolder holder, int position) {
1212 int viewType = getItemViewType(position);
1213
1214 switch (viewType) {
1215 case ITEM_VIEW_TYPE_ROTATION:
1216 onBindRotationHolder(holder, position);
1217 break;
1218 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1219 onBindIndividualHolder(holder, position);
1220 break;
1221 case ITEM_VIEW_TYPE_MY_PHOTOS:
1222 ((MyPhotosViewHolder) holder).bind();
1223 break;
1224 default:
1225 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1226 }
1227 }
1228
1229 @Override
1230 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001231 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001232 ? mWallpapers.size() + 1
1233 : mWallpapers.size();
1234 }
1235
1236 private ViewHolder createRotationHolder(ViewGroup parent) {
1237 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001238 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001239 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Chuck Liao5a4243b2020-05-20 23:56:39 +08001240 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1241 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001242 }
1243
1244 private ViewHolder createIndividualHolder(ViewGroup parent) {
1245 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1246 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1247
1248 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001249 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001250 return new SetIndividualHolder(
1251 getActivity(), mTileSizePx.y, view,
1252 selectionAnimator,
1253 new OnSetListener() {
1254 @Override
1255 public void onPendingWallpaperSet(int adapterPosition) {
1256 // Deselect and hide loading indicator for any previously pending tile.
1257 if (mPendingSelectedAdapterPosition != -1) {
1258 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1259 mPendingSelectedAdapterPosition);
1260 if (oldViewHolder instanceof SelectableHolder) {
1261 ((SelectableHolder) oldViewHolder).setSelectionState(
1262 SelectableHolder.SELECTION_STATE_DESELECTED);
1263 }
1264 }
1265
1266 if (mSelectedAdapterPosition != -1) {
1267 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1268 mSelectedAdapterPosition);
1269 if (oldViewHolder instanceof SelectableHolder) {
1270 ((SelectableHolder) oldViewHolder).setSelectionState(
1271 SelectableHolder.SELECTION_STATE_DESELECTED);
1272 }
1273 }
1274
1275 mPendingSelectedAdapterPosition = adapterPosition;
1276 }
1277
1278 @Override
1279 public void onWallpaperSet(int adapterPosition) {
1280 // No-op -- UI handles a new wallpaper being set by reacting to the
1281 // WallpaperChangedNotifier.
1282 }
1283
1284 @Override
1285 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1286 showSetWallpaperErrorDialog();
1287 mPendingSetIndividualHolder = holder;
1288 }
1289 });
1290 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001291 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001292 }
1293 }
1294
1295 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1296 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1297 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1298
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001299 return new MyPhotosViewHolder(getActivity(),
1300 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1301 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001302 }
1303
1304 /**
1305 * Marks the tile at the given position as selected with a visual indication. Also updates the
1306 * "currently selected" BottomSheet to reflect the newly selected tile.
1307 */
1308 private void updateSelectedTile(int newlySelectedPosition) {
1309 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1310 // succession.
1311 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1312 return;
1313 }
1314
1315 if (mCurrentWallpaperBottomSheetPresenter != null) {
1316 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1317 IndividualPickerFragment.this);
1318
1319 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1320 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1321 }
1322 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1323 @Override
1324 public void run() {
1325 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1326 }
1327 };
1328 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1329 }
1330
1331 // User may have switched to another category, thus detaching this fragment, so check here.
1332 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1333 // still occurs in the UI after the user selects that other category.
1334 if (getActivity() == null) {
1335 return;
1336 }
1337
1338 // Update the newly selected wallpaper ViewHolder and the old one so that if
1339 // selection UI state applies (desktop UI), it is updated.
1340 if (mSelectedAdapterPosition >= 0) {
1341 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1342 mSelectedAdapterPosition);
1343 if (oldViewHolder instanceof SelectableHolder) {
1344 ((SelectableHolder) oldViewHolder).setSelectionState(
1345 SelectableHolder.SELECTION_STATE_DESELECTED);
1346 }
1347 }
1348
1349 // Animate selection of newly selected tile.
1350 ViewHolder newViewHolder = mImageGrid
1351 .findViewHolderForAdapterPosition(newlySelectedPosition);
1352 if (newViewHolder instanceof SelectableHolder) {
1353 ((SelectableHolder) newViewHolder).setSelectionState(
1354 SelectableHolder.SELECTION_STATE_SELECTED);
1355 }
1356
1357 mSelectedAdapterPosition = newlySelectedPosition;
1358
1359 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1360 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1361 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1362 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1363 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1364 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1365
1366 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1367 }
1368
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001369 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001370 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1371 String collectionId = mCategory.getCollectionId();
1372 ((DesktopRotationHolder) holder).bind(collectionId);
1373
1374 if (mWallpaperPreferences.getWallpaperPresentationMode()
1375 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1376 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1377 mSelectedAdapterPosition = position;
1378 }
1379
1380 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1381 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1382 mWasUpdateRunnableRun = true;
1383 }
1384 }
1385 }
1386
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001387 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001388 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001389 ? position - 1 : position;
1390 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1391 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001392 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001393 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1394 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1395 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001396
“Chuckffd832c2020-03-22 02:15:58 +08001397 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001398 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001399 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001400 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001401
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001402 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001403 thumbnail.setClipped(isWallpaperApplied);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001404 }
1405 }
Chuck Liaoba401232020-03-13 20:11:04 +08001406
1407 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1408
Chuck Liao1e6cb682021-04-22 00:51:11 +08001409 private final int mPaddingHorizontal;
1410 private final int mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001411
Chuck Liao1e6cb682021-04-22 00:51:11 +08001412 GridPaddingDecoration(int paddingHorizontal, int paddingBottom) {
1413 mPaddingHorizontal = paddingHorizontal;
1414 mPaddingBottom = paddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001415 }
1416
1417 @Override
1418 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1419 RecyclerView.State state) {
1420 int position = parent.getChildAdapterPosition(view);
1421 if (position >= 0) {
Chuck Liao1e6cb682021-04-22 00:51:11 +08001422 outRect.left = mPaddingHorizontal;
1423 outRect.right = mPaddingHorizontal;
1424 outRect.bottom = mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001425 }
1426 }
1427 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001428}