blob: 9fff1f359463f0c46d0ce2baadfd1832565aec15 [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;
Chuck Liaoec053b02021-04-24 01:30:50 +080034import android.text.TextUtils;
35import android.util.ArraySet;
Jon Miranda16ea1b12017-12-12 14:52:48 -080036import android.util.Log;
37import android.view.LayoutInflater;
Chuck Liao33f1af42021-04-13 20:52:35 +080038import android.view.MenuItem;
Jon Miranda16ea1b12017-12-12 14:52:48 -080039import android.view.View;
40import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080041import android.widget.Toast;
42
Santiago Etchebehereb1854472019-06-06 17:44:54 -070043import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080044import androidx.annotation.Nullable;
Chuck Liaof6b4b192020-08-07 02:31:32 +080045import androidx.core.widget.ContentLoadingProgressBar;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080046import androidx.fragment.app.DialogFragment;
Chuck Liao7e333722021-02-27 02:21:20 +080047import androidx.fragment.app.Fragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080048import androidx.recyclerview.widget.GridLayoutManager;
49import androidx.recyclerview.widget.RecyclerView;
50import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
51import androidx.recyclerview.widget.RecyclerView.ViewHolder;
52
Jon Miranda16ea1b12017-12-12 14:52:48 -080053import com.android.wallpaper.R;
54import com.android.wallpaper.asset.Asset;
55import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070056import com.android.wallpaper.model.Category;
57import com.android.wallpaper.model.CategoryProvider;
58import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080059import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080060import com.android.wallpaper.model.WallpaperCategory;
61import com.android.wallpaper.model.WallpaperInfo;
62import com.android.wallpaper.model.WallpaperReceiver;
63import com.android.wallpaper.model.WallpaperRotationInitializer;
64import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
65import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080066import com.android.wallpaper.module.FormFactorChecker;
67import com.android.wallpaper.module.FormFactorChecker.FormFactor;
68import com.android.wallpaper.module.Injector;
69import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070070import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080071import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080072import com.android.wallpaper.module.WallpaperChangedNotifier;
73import com.android.wallpaper.module.WallpaperPersister;
74import com.android.wallpaper.module.WallpaperPersister.Destination;
75import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080076import com.android.wallpaper.module.WallpaperSetter;
Chuck Liao58aca1c2021-03-17 01:20:55 +080077import com.android.wallpaper.picker.AppbarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080078import com.android.wallpaper.picker.BaseActivity;
79import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Chuck Liaob17d1502020-09-07 21:55:30 +080080import com.android.wallpaper.picker.FragmentTransactionChecker;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080081import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -080082import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080083import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080084import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
85import com.android.wallpaper.picker.StartRotationDialogFragment;
86import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080087import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080088import com.android.wallpaper.picker.WallpapersUiContainer;
89import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
90import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070091import com.android.wallpaper.util.SizeCalculator;
chihhangchuang1a29e752020-04-28 18:22:53 +080092import com.android.wallpaper.widget.WallpaperInfoView;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080093import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
94import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070095
Jon Miranda16ea1b12017-12-12 14:52:48 -080096import com.bumptech.glide.Glide;
97import com.bumptech.glide.MemoryCategory;
98
99import java.util.ArrayList;
100import java.util.Date;
101import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800102import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800103import java.util.Random;
Chuck Liaoec053b02021-04-24 01:30:50 +0800104import java.util.Set;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800105
106/**
107 * Displays the Main UI for picking an individual wallpaper image.
108 */
Chuck Liao58aca1c2021-03-17 01:20:55 +0800109public class IndividualPickerFragment extends AppbarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800110 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
111 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800112 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
113 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800114
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800115 /**
116 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
117 * such as "my photos" or "daily rotation".
118 */
119 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
120 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
121
Jon Miranda16ea1b12017-12-12 14:52:48 -0800122 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800123 private static final int UNUSED_REQUEST_CODE = 1;
124 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
125 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
126 private static final String PROGRESS_DIALOG_NO_TITLE = null;
127 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
128 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
129 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700130 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800131
Chuck Liao8ec38e02020-02-26 20:59:32 +0800132 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800133 * An interface for updating the thumbnail with the specific wallpaper.
134 */
135 public interface ThumbnailUpdater {
136 /**
137 * Updates the thumbnail with the specific wallpaper.
138 */
139 void updateThumbnail(WallpaperInfo wallpaperInfo);
140
141 /**
142 * Restores to the thumbnails of the wallpapers which were applied.
143 */
144 void restoreThumbnails();
145 }
146
“Chuck7ef99722020-03-22 04:34:03 +0800147 /**
148 * An interface for receiving the destination of the new applied wallpaper.
149 */
150 public interface WallpaperDestinationCallback {
151 /**
152 * Called when the destination of the wallpaper is set.
153 *
154 * @param destination the destination which a wallpaper may be set.
155 * See {@link Destination} for more details.
156 */
157 void onDestinationSet(@Destination int destination);
158 }
159
Chuck Liaob3829fb2020-04-01 00:47:50 +0800160 /**
161 * The listener which will be notified when the wallpaper is selected.
162 */
163 public interface WallpaperSelectedListener {
164 /**
165 * Called when the wallpaper is selected.
166 *
167 * @param position the position of the selected wallpaper
168 */
169 void onWallpaperSelected(int position);
170 }
171
Chuck Liaof6b4b192020-08-07 02:31:32 +0800172 /**
Chuck Liao7e333722021-02-27 02:21:20 +0800173 * Interface to be implemented by a Fragment(or an Activity) hosting
174 * a {@link IndividualPickerFragment}.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800175 */
176 public interface IndividualPickerFragmentHost {
177 /**
Chuck Liao58aca1c2021-03-17 01:20:55 +0800178 * Indicates if the host has toolbar to show the title. If it does, we should set the title
179 * there.
180 */
181 boolean isHostToolbarShown();
182
183 /**
184 * Sets the title in the host's toolbar.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800185 */
186 void setToolbarTitle(CharSequence title);
187
188 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800189 * Configures the menu in the toolbar.
190 *
191 * @param menuResId the resource id of the menu
192 */
193 void setToolbarMenu(@MenuRes int menuResId);
194
195 /**
196 * Removes the menu in the toolbar.
197 */
198 void removeToolbarMenu();
199
200 /**
Chuck Liaof6b4b192020-08-07 02:31:32 +0800201 * Moves to the previous fragment.
202 */
203 void moveToPreviousFragment();
204 }
205
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800206 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800207 WallpaperPreferences mWallpaperPreferences;
208 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800209 RecyclerView mImageGrid;
210 IndividualAdapter mAdapter;
211 WallpaperCategory mCategory;
212 WallpaperRotationInitializer mWallpaperRotationInitializer;
213 List<WallpaperInfo> mWallpapers;
214 Point mTileSizePx;
215 WallpapersUiContainer mWallpapersUiContainer;
216 @FormFactor
217 int mFormFactor;
218 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800219
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800220 Handler mHandler;
221 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800222 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800223
224 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
225 new WallpaperChangedNotifier.Listener() {
226 @Override
227 public void onWallpaperChanged() {
228 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
229 return;
230 }
231
232 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
233 mAdapter.mSelectedAdapterPosition);
234
235 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
236 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
237 if (selectedViewHolder instanceof SelectableHolder) {
238 ((SelectableHolder) selectedViewHolder).setSelectionState(
239 SelectableHolder.SELECTION_STATE_DESELECTED);
240 }
241 } else {
242 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
243 }
244 }
245 };
246 PackageStatusNotifier.Listener mAppStatusListener;
chihhangchuang1a29e752020-04-28 18:22:53 +0800247 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800248 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800249
chihhangchuangc687d912020-05-04 14:33:05 +0800250 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800251 private ProgressDialog mProgressDialog;
252 private boolean mTestingMode;
253 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800254 private SetIndividualHolder mPendingSetIndividualHolder;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800255 private ContentLoadingProgressBar mLoading;
Chuck Liaoddf2b522021-04-15 00:36:25 +0800256 private CategoryProvider mCategoryProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800257
258 /**
259 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
260 * committing fragment transactions.
261 */
262 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
263 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
264
Jon Miranda16ea1b12017-12-12 14:52:48 -0800265 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800266
267 /**
268 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
269 * invocation of the fragment.
270 */
271 private boolean mWasUpdateRunnableRun;
272
273 /**
274 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
275 * mode.
276 */
277 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
278 @Override
279 public void run() {
280 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
281 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
282 if (viewHolder instanceof DesktopRotationHolder) {
283 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
284 } else { // viewHolder is null
285 // If the rotation tile is unavailable (because user has scrolled down, causing the
286 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
287 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
288 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
289 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
290 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
291 }
292 }
293 };
294
Chuck Liao69630f12020-03-05 19:01:25 +0800295 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800296 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800297 private WallpaperManager mWallpaperManager;
298 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800299 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liaoec053b02021-04-24 01:30:50 +0800300 private Set<String> mAppliedWallpaperIds;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800301
Jon Miranda16ea1b12017-12-12 14:52:48 -0800302 public static IndividualPickerFragment newInstance(String collectionId) {
303 Bundle args = new Bundle();
304 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
305
306 IndividualPickerFragment fragment = new IndividualPickerFragment();
307 fragment.setArguments(args);
308 return fragment;
309 }
310
“Chuck7ef99722020-03-22 04:34:03 +0800311 /**
312 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
313 * would be set.
314 *
315 * @param wallpaperDestination the destination a wallpaper would be set.
316 * It will be either {@link Destination#DEST_HOME_SCREEN}
317 * or {@link Destination#DEST_LOCK_SCREEN}.
318 */
319 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
320 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800321 if (mWallpapers != null) {
322 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800323 }
324 }
325
Jon Miranda16ea1b12017-12-12 14:52:48 -0800326 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
327 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
328 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
329 getActivity());
330 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
331 @Override
332 public void onDrawableLoaded() {
333 if (getActivity() == null) {
334 return;
335 }
336
337 // Schedule the next update of the thumbnail.
338 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
339 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
340 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
341 }
342 });
343 }
344
345 @Override
346 public void onCreate(Bundle savedInstanceState) {
347 super.onCreate(savedInstanceState);
348
349 Injector injector = InjectorProvider.getInjector();
350 Context appContext = getContext().getApplicationContext();
351 mWallpaperPreferences = injector.getPreferences(appContext);
352
353 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
354 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
355
“Chuck7ef99722020-03-22 04:34:03 +0800356 mWallpaperManager = WallpaperManager.getInstance(appContext);
357
Jon Miranda16ea1b12017-12-12 14:52:48 -0800358 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
359
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700360 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
361
chihhangchuangc687d912020-05-04 14:33:05 +0800362 mUserEventLogger = injector.getUserEventLogger(appContext);
363
“Chuckffd832c2020-03-22 02:15:58 +0800364 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800365 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800366 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800367 injector.getPreferences(appContext),
368 injector.getUserEventLogger(appContext),
369 false);
370
Jon Miranda16ea1b12017-12-12 14:52:48 -0800371 mWallpapers = new ArrayList<>();
372 mRandom = new Random();
373 mHandler = new Handler();
374
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700375 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
376 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
377 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
378 Glide.get(getContext()).clearMemory();
379 }
380
Chuck Liaoddf2b522021-04-15 00:36:25 +0800381 mCategoryProvider = injector.getCategoryProvider(appContext);
382 mCategoryProvider.fetchCategories(new CategoryReceiver() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700383 @Override
384 public void onCategoryReceived(Category category) {
385 // Do nothing.
386 }
387
388 @Override
389 public void doneFetchingCategories() {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800390 Category category = mCategoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700391 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800392 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800393 return;
394 }
395 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700396 if (mCategory == null) {
397 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
398
399 // The absence of this category in the CategoryProvider indicates a broken
400 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800401 getIndividualPickerFragmentHost().moveToPreviousFragment();
402 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
403 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700404 return;
405 }
406 onCategoryLoaded();
407 }
408 }, false);
409 }
410
Chuck Liaof6b4b192020-08-07 02:31:32 +0800411
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700412 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800413 if (getIndividualPickerFragmentHost() == null) {
414 return;
415 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800416 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
417 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
418 } else {
419 setTitle(mCategory.getTitle());
420 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700421 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao33f1af42021-04-13 20:52:35 +0800422 if (mToolbar != null && isRotationEnabled()) {
423 setUpToolbarMenu(R.menu.individual_picker_menu);
424 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700425 fetchWallpapers(false);
426
427 if (mCategory.supportsThirdParty()) {
428 mAppStatusListener = (packageName, status) -> {
429 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
430 mCategory.containsThirdParty(packageName)) {
431 fetchWallpapers(true);
432 }
433 };
434 mPackageStatusNotifier.addListener(mAppStatusListener,
435 WallpaperService.SERVICE_INTERFACE);
436 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700437
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700438 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700439 }
440
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800441 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700442 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800443 mIsWallpapersReceived = false;
444 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800445 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
446 @Override
447 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800448 mIsWallpapersReceived = true;
449 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800450 for (WallpaperInfo wallpaper : wallpapers) {
451 mWallpapers.add(wallpaper);
452 }
453
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700454 // Wallpapers may load after the adapter is initialized, in which case we have
455 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800456 if (mAdapter != null) {
457 mAdapter.notifyDataSetChanged();
458 }
459
460 if (mWallpapersUiContainer != null) {
461 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700462 } else {
463 if (wallpapers.isEmpty()) {
464 // If there are no more wallpapers and we're on phone, just finish the
465 // Activity.
466 Activity activity = getActivity();
467 if (activity != null
468 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
469 activity.finish();
470 }
471 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800472 }
473 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700474 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800475 }
476
Chuck Liaof6b4b192020-08-07 02:31:32 +0800477 void updateLoading() {
478 if (mLoading == null) {
479 return;
480 }
481
482 if (mIsWallpapersReceived) {
483 mLoading.hide();
484 } else {
485 mLoading.show();
486 }
487 }
488
Jon Miranda16ea1b12017-12-12 14:52:48 -0800489 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700490 public void onSaveInstanceState(@NonNull Bundle outState) {
491 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700492 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700493 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
494 }
495
496 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800497 public View onCreateView(LayoutInflater inflater, ViewGroup container,
498 Bundle savedInstanceState) {
499 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
Chuck Liao58aca1c2021-03-17 01:20:55 +0800500 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
501 view.findViewById(R.id.header_bar).setVisibility(View.GONE);
Kunhung Lid0174362021-04-05 15:31:41 +0800502 setUpArrowEnabled(/* upArrow= */ true);
Chuck Liaoa7215812021-04-13 22:33:43 +0800503 if (isRotationEnabled()) {
504 getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu);
505 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800506 } else {
507 setUpToolbar(view);
Chuck Liao33f1af42021-04-13 20:52:35 +0800508 if (isRotationEnabled()) {
509 setUpToolbarMenu(R.menu.individual_picker_menu);
510 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800511 if (mCategory != null) {
512 setTitle(mCategory.getTitle());
513 }
514 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800515
Chuck Liaoec053b02021-04-24 01:30:50 +0800516 mAppliedWallpaperIds = getAppliedWallpaperIds();
517
Jon Miranda16ea1b12017-12-12 14:52:48 -0800518 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
519 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
520 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
521 updateImageGridPadding(false /* addExtraBottomSpace */);
522 mImageGrid.setScrollBarSize(gridPaddingPx);
523 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800524 mLoading = view.findViewById(R.id.loading_indicator);
525 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700526 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800527 setUpBottomSheet();
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800528 // For nav bar edge-to-edge effect.
529 view.findViewById(R.id.wallpaper_grid).setOnApplyWindowInsetsListener((v, windowInsets) -> {
530 v.setPadding(
531 v.getPaddingLeft(),
532 v.getPaddingTop(),
533 v.getPaddingRight(),
534 windowInsets.getSystemWindowInsetBottom());
535 return windowInsets.consumeSystemWindowInsets();
536 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800537 return view;
538 }
539
540 @Override
541 public void onClickTryAgain(@Destination int unused) {
542 if (mPendingSetIndividualHolder != null) {
543 mPendingSetIndividualHolder.setWallpaper();
544 }
545 }
546
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800547 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800548 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
549 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
550 R.dimen.current_wallpaper_bottom_sheet_layout_height);
551 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
552 // Only left and top may be set in order for the GridMarginDecoration to work properly.
553 mImageGrid.setPadding(
554 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
555 }
556
Chuck Liaof6b4b192020-08-07 02:31:32 +0800557 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
Chuck Liao7e333722021-02-27 02:21:20 +0800558 Fragment parentFragment = getParentFragment();
559 if (parentFragment != null) {
560 return (IndividualPickerFragmentHost) parentFragment;
561 } else {
562 return (IndividualPickerFragmentHost) getActivity();
563 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800564 }
565
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700566 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700567 // Skip if mImageGrid been initialized yet
568 if (mImageGrid == null) {
569 return;
570 }
571 // Skip if category hasn't loaded yet
572 if (mCategory == null) {
573 return;
574 }
575 // Skip if the adapter was already created
576 if (mAdapter != null) {
577 return;
578 }
Chuck Liao1e6cb682021-04-22 00:51:11 +0800579 mImageGrid.addItemDecoration(new GridPaddingDecoration(getGridItemPaddingHorizontal(),
580 getGridItemPaddingBottom()));
581 int edgePadding = getEdgePadding();
582 mImageGrid.setPadding(edgePadding, mImageGrid.getPaddingTop(), edgePadding,
583 mImageGrid.getPaddingBottom());
Chuck Liaoddf2b522021-04-15 00:36:25 +0800584 mTileSizePx = mCategoryProvider.isFeaturedCategory(mCategory)
585 ? SizeCalculator.getFeaturedIndividualTileSize(getActivity())
586 : SizeCalculator.getIndividualTileSize(getActivity());
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700587 setUpImageGrid();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800588 mImageGrid.setAccessibilityDelegateCompat(
589 new WallpaperPickerRecyclerViewAccessibilityDelegate(
590 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700591 }
592
Chuck Liao1e6cb682021-04-22 00:51:11 +0800593 private int getGridItemPaddingHorizontal() {
594 return mCategoryProvider.isFeaturedCategory(mCategory)
595 ? getResources().getDimensionPixelSize(
596 R.dimen.grid_item_featured_individual_padding_horizontal)
597 : getResources().getDimensionPixelSize(
598 R.dimen.grid_item_individual_padding_horizontal);
599 }
600
601 private int getGridItemPaddingBottom() {
602 return mCategoryProvider.isFeaturedCategory(mCategory)
603 ? getResources().getDimensionPixelSize(
604 R.dimen.grid_item_featured_individual_padding_bottom)
605 : getResources().getDimensionPixelSize(R.dimen.grid_item_individual_padding_bottom);
606 }
607
608 private int getEdgePadding() {
609 return mCategoryProvider.isFeaturedCategory(mCategory)
610 ? getResources().getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space)
611 : getResources().getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space);
612 }
613
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700614 /**
615 * Create the adapter and assign it to mImageGrid.
616 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
617 */
618 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800619 mAdapter = new IndividualAdapter(mWallpapers);
620 mImageGrid.setAdapter(mAdapter);
621 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
622 }
623
Jon Miranda16ea1b12017-12-12 14:52:48 -0800624 /**
625 * Enables and populates the "Currently set" wallpaper BottomSheet.
626 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800627 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800628 mImageGrid.addOnScrollListener(new OnScrollListener() {
629 @Override
630 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
631 if (mCurrentWallpaperBottomSheetPresenter == null) {
632 return;
633 }
634
635 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
636 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
637 }
638 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
639 @Override
640 public void run() {
641 if (dy > 0) {
642 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
643 } else {
644 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
645 }
646 }
647 };
648 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
649 }
650 });
651 }
652
chihhangchuang3efb6832020-04-17 02:06:25 +0800653 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800654 public void onResume() {
655 super.onResume();
656
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700657 WallpaperPreferences preferences = InjectorProvider.getInjector()
658 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800659 preferences.setLastAppActiveTimestamp(new Date().getTime());
660
661 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
662 // PreviewFragment.
663 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
664
665 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
666 // shown earlier when this fragment's hosting activity didn't allow committing fragment
667 // transactions.
668 if (mStagedStartRotationErrorDialogFragment != null) {
669 mStagedStartRotationErrorDialogFragment.show(
670 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
671 mStagedStartRotationErrorDialogFragment = null;
672 }
673
674 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
675 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
676 // committing fragment transactions.
677 if (mStagedSetWallpaperErrorDialogFragment != null) {
678 mStagedSetWallpaperErrorDialogFragment.show(
679 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
680 mStagedSetWallpaperErrorDialogFragment = null;
681 }
682
Chuck Liao5a4243b2020-05-20 23:56:39 +0800683 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
684 // Must be resuming from a previously stopped state, so re-schedule the update of the
685 // daily wallpapers tile thumbnail.
686 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800687 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800688 }
689
690 @Override
691 public void onStop() {
692 super.onStop();
693 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
694 }
695
696 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800697 public void onDestroyView() {
698 super.onDestroyView();
699 getIndividualPickerFragmentHost().removeToolbarMenu();
700 }
701
702 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800703 public void onDestroy() {
704 super.onDestroy();
705 if (mProgressDialog != null) {
706 mProgressDialog.dismiss();
707 }
708 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700709 if (mAppStatusListener != null) {
710 mPackageStatusNotifier.removeListener(mAppStatusListener);
711 }
Chuck Liao69630f12020-03-05 19:01:25 +0800712 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800713 }
714
715 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800716 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800717 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
718 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
719 // still holds a reference to the destroyed Fragment and is calling
720 // onStartRotationDialogDismissed on that destroyed Fragment.
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800721 }
722
723 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800724 public void retryStartRotation(@NetworkPreference int networkPreference) {
725 startRotation(networkPreference);
726 }
727
728 public void setCurrentWallpaperBottomSheetPresenter(
729 CurrentWallpaperBottomSheetPresenter presenter) {
730 mCurrentWallpaperBottomSheetPresenter = presenter;
731 }
732
733 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
734 mWallpapersUiContainer = uiContainer;
735 }
736
Chuck Liaob3829fb2020-04-01 00:47:50 +0800737 public void setOnWallpaperSelectedListener(
738 WallpaperSelectedListener wallpaperSelectedListener) {
739 mWallpaperSelectedListener = wallpaperSelectedListener;
740 }
741
742 /**
743 * Resizes the layout's height.
744 */
745 public void resizeLayout(int height) {
746 mImageGrid.getLayoutParams().height = height;
747 mImageGrid.requestLayout();
748 }
749
750 /**
751 * Scrolls to the specific item.
752 *
753 * @param position the position of the item
754 */
755 public void scrollToPosition(int position) {
756 ((GridLayoutManager) mImageGrid.getLayoutManager())
757 .scrollToPositionWithOffset(position, /* offset= */ 0);
758 }
759
Jon Miranda16ea1b12017-12-12 14:52:48 -0800760 /**
761 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
762 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
763 * constantly keeps the UI thread alive and blocks a test forever.
764 *
765 * @param testingMode
766 */
767 void setTestingMode(boolean testingMode) {
768 mTestingMode = testingMode;
769 }
770
Jon Miranda16ea1b12017-12-12 14:52:48 -0800771 @Override
772 public void startRotation(@NetworkPreference final int networkPreference) {
773 if (!isRotationEnabled()) {
774 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
775 return;
776 }
777
778 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
779 // causes Espresso to hang once the dialog is shown.
780 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
781 int themeResId;
782 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
783 themeResId = R.style.ProgressDialogThemePreL;
784 } else {
785 themeResId = R.style.LightDialogTheme;
786 }
787 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
788
789 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
790 mProgressDialog.setMessage(
791 getResources().getString(R.string.start_rotation_progress_message));
792 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
793 mProgressDialog.show();
794 }
795
796 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
797 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
798 }
799
800 final Context appContext = getActivity().getApplicationContext();
801
802 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
803 appContext,
804 networkPreference,
805 new Listener() {
806 @Override
807 public void onFirstWallpaperInRotationSet() {
808 if (mProgressDialog != null) {
809 mProgressDialog.dismiss();
810 }
811
812 // The fragment may be detached from its containing activity if the user exits the
813 // app before the first wallpaper image in rotation finishes downloading.
814 Activity activity = getActivity();
815
Jon Miranda16ea1b12017-12-12 14:52:48 -0800816
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700817 if (mWallpaperRotationInitializer.startRotation(appContext)) {
818 if (activity != null
819 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
820 try {
821 Toast.makeText(getActivity(),
822 R.string.wallpaper_set_successfully_message,
823 Toast.LENGTH_SHORT).show();
824 } catch (NotFoundException e) {
825 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800826 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800827
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700828 activity.setResult(Activity.RESULT_OK);
829 activity.finish();
830 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
831 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
832 }
833 } else { // Failed to start rotation.
834 showStartRotationErrorDialog(networkPreference);
835
836 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
837 DesktopRotationHolder rotationViewHolder =
838 (DesktopRotationHolder)
839 mImageGrid.findViewHolderForAdapterPosition(
840 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
841 rotationViewHolder.setSelectionState(
842 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800843 }
844 }
845 }
846
847 @Override
848 public void onError() {
849 if (mProgressDialog != null) {
850 mProgressDialog.dismiss();
851 }
852
853 showStartRotationErrorDialog(networkPreference);
854
855 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
856 DesktopRotationHolder rotationViewHolder =
857 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
858 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
859 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
860 }
861 }
862 });
863 }
864
865 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800866 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800867 if (activity != null) {
868 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
869 StartRotationErrorDialogFragment.newInstance(networkPreference);
870 startRotationErrorDialogFragment.setTargetFragment(
871 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
872
873 if (activity.isSafeToCommitFragmentTransaction()) {
874 startRotationErrorDialogFragment.show(
875 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
876 } else {
877 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
878 }
879 }
880 }
881
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800882 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800883 Activity activity = getActivity();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800884 if (activity == null) {
885 return 1;
886 }
887 return mCategoryProvider.isFeaturedCategory(mCategory)
888 ? SizeCalculator.getNumFeaturedIndividualColumns(activity)
889 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800890 }
891
892 /**
893 * Returns whether rotation is enabled for this category.
894 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800895 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700896 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800897 }
898
899 @Override
900 public void onCurrentWallpaperRefreshed() {
901 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
902 }
903
Chuck Liao69630f12020-03-05 19:01:25 +0800904 @Override
905 public void onSet(int destination) {
906 if (mSelectedWallpaperInfo == null) {
907 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
908 return;
909 }
910
“Chuckffd832c2020-03-22 02:15:58 +0800911 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800912 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
913 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
914 destination, 0, null, mSetWallpaperCallback);
915 } else {
916 mWallpaperSetter.setCurrentWallpaper(
917 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
918 }
“Chuck7ef99722020-03-22 04:34:03 +0800919 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800920 }
921
922 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
923 new WallpaperPersister.SetWallpaperCallback() {
924 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800925 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800926 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800927 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
928 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800929 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
930 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800931 }
932
933 @Override
934 public void onError(@Nullable Throwable throwable) {
935 Log.e(TAG, "Can't apply the wallpaper.");
Chuck Liao69630f12020-03-05 19:01:25 +0800936 }
937 };
938
939 @Override
Chuck Liao33f1af42021-04-13 20:52:35 +0800940 public boolean onMenuItemClick(MenuItem item) {
941 if (item.getItemId() == R.id.daily_rotation) {
Chuck Liaoa7215812021-04-13 22:33:43 +0800942 showRotationDialog();
Chuck Liao33f1af42021-04-13 20:52:35 +0800943 return true;
944 }
945 return super.onMenuItemClick(item);
946 }
947
Jon Miranda16ea1b12017-12-12 14:52:48 -0800948 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800949 * Popups a daily rotation dialog for the uses to confirm.
950 */
951 public void showRotationDialog() {
952 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
953 startRotationDialogFragment.setTargetFragment(
954 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
955 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
956 }
957
958 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800959 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
960 */
961 private void showSetWallpaperErrorDialog() {
962 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
963 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
964 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
965
966 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
967 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
968 } else {
969 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
970 }
971 }
972
Chuck Liao69630f12020-03-05 19:01:25 +0800973 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800974 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800975 if (thumbnailUpdater == null) {
976 return;
977 }
978
979 if (selectedWallpaperInfo != null) {
980 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
981 } else {
982 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800983 }
984 }
985
“Chuck7ef99722020-03-22 04:34:03 +0800986 private void onWallpaperDestinationSet(int destination) {
987 WallpaperDestinationCallback wallpaperDestinationCallback =
988 (WallpaperDestinationCallback) getParentFragment();
989 if (wallpaperDestinationCallback == null) {
990 return;
991 }
992
993 wallpaperDestinationCallback.onDestinationSet(destination);
994 }
995
Ching-Sung Li073812b2020-04-07 21:19:21 +0800996 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800997 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800998 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800999 return;
1000 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001001 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +08001002 updateActivatedStatus(mSelectedWallpaperInfo == null
1003 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001004 // Update new selected wallpaper.
1005 updateActivatedStatus(newSelectedWallpaperInfo == null
1006 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
1007
1008 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001009 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +08001010 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +08001011 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +08001012 WallpaperInfoHelper.loadExploreIntent(
1013 getContext(),
1014 mSelectedWallpaperInfo,
1015 (actionLabel, exploreIntent) ->
1016 mWallpaperInfoView.populateWallpaperInfo(
Chuck Liao9991d682021-01-20 17:45:14 +08001017 mSelectedWallpaperInfo,
1018 actionLabel,
1019 WallpaperInfoHelper.shouldShowExploreButton(
1020 getContext(), exploreIntent),
1021 v -> onExploreClicked(exploreIntent))
1022 );
Chuck Liaob3829fb2020-04-01 00:47:50 +08001023 }
1024
1025 if (mWallpaperSelectedListener != null) {
1026 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001027 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001028 }
1029
chihhangchuangc687d912020-05-04 14:33:05 +08001030 private void onExploreClicked(Intent exploreIntent) {
1031 if (getContext() == null) {
1032 return;
1033 }
1034 Context context = getContext();
1035 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
1036 mSelectedWallpaperInfo.getActionLabelRes(context));
1037
1038 startActivity(exploreIntent);
1039 }
1040
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001041 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +08001042 if (wallpaperInfo == null) {
1043 return;
1044 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001045 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001046 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001047 ? index + 1 : index;
1048 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1049 if (holder != null) {
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001050 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001051 thumbnail.setClipped(isActivated);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001052 } else {
1053 // Item is not visible, make sure the item is re-bound when it becomes visible.
1054 mAdapter.notifyItemChanged(index);
1055 }
1056 }
1057
“Chuckffd832c2020-03-22 02:15:58 +08001058 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1059 if (wallpaperInfo == null) {
1060 return;
1061 }
1062 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001063 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001064 ? index + 1 : index;
1065 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001066 if (holder == null) {
“Chuckffd832c2020-03-22 02:15:58 +08001067 // Item is not visible, make sure the item is re-bound when it becomes visible.
1068 mAdapter.notifyItemChanged(index);
1069 }
1070 }
1071
Chuck Liao11f4a762020-04-08 13:24:43 +08001072 private void refreshAppliedWallpaper() {
1073 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1074 showCheckMarkAndBorderForAppliedWallpaper(false);
1075
1076 // Update to the new applied wallpaper.
1077 String appliedWallpaperId = getAppliedWallpaperId();
1078 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1079 .stream()
1080 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1081 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1082 .findFirst();
1083 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1084
1085 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1086 showCheckMarkAndBorderForAppliedWallpaper(true);
1087 }
1088
1089 private String getAppliedWallpaperId() {
1090 WallpaperPreferences prefs =
1091 InjectorProvider.getInjector().getPreferences(getContext());
1092 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1093 boolean isDestinationBoth =
1094 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1095
1096 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1097 return wallpaperInfo != null
1098 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1099 } else {
1100 return prefs.getLockWallpaperRemoteId();
1101 }
1102 }
1103
Chuck Liaoec053b02021-04-24 01:30:50 +08001104 private Set<String> getAppliedWallpaperIds() {
1105 WallpaperPreferences prefs =
1106 InjectorProvider.getInjector().getPreferences(getContext());
1107 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1108 Set<String> appliedWallpaperIds = new ArraySet<>();
1109
1110 String homeWallpaperId = wallpaperInfo != null ? wallpaperInfo.getServiceName()
1111 : prefs.getHomeWallpaperRemoteId();
1112 if (!TextUtils.isEmpty(homeWallpaperId)) {
1113 appliedWallpaperIds.add(homeWallpaperId);
1114 }
1115
1116 boolean isLockWallpaperApplied =
1117 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) >= 0;
1118 String lockWallpaperId = prefs.getLockWallpaperRemoteId();
1119 if (isLockWallpaperApplied && !TextUtils.isEmpty(lockWallpaperId)) {
1120 appliedWallpaperIds.add(lockWallpaperId);
1121 }
1122
1123 return appliedWallpaperIds;
1124 }
1125
Chuck Liao11f4a762020-04-08 13:24:43 +08001126 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1127 updateAppliedStatus(mAppliedWallpaperInfo, show);
1128 if (mSelectedWallpaperInfo == null) {
1129 updateActivatedStatus(mAppliedWallpaperInfo, show);
1130 }
1131 }
1132
Chuck Liao5a4243b2020-05-20 23:56:39 +08001133 private boolean shouldShowRotationTile() {
1134 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001135 }
1136
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001137 class EmptySelectionAnimator implements SelectionAnimator{
1138 EmptySelectionAnimator() {}
1139
1140 public boolean isSelected() {
1141 return false;
1142 }
1143
1144 /**
1145 * Sets the UI to selected immediately with no animation.
1146 */
1147 public void selectImmediately() {}
1148
1149 /**
1150 * Sets the UI to deselected immediately with no animation.
1151 */
1152 public void deselectImmediately() {}
1153
1154 /**
1155 * Sets the UI to selected with a smooth animation.
1156 */
1157 public void animateSelected() {}
1158
1159 /**
1160 * Sets the UI to deselected with a smooth animation.
1161 */
1162 public void animateDeselected() {}
1163
1164 /**
1165 * Sets the UI to show a loading indicator.
1166 */
1167 public void showLoading() {}
1168
1169 /**
1170 * Sets the UI to hide the loading indicator.
1171 */
1172 public void showNotLoading() {}
1173
1174 }
1175
Jon Miranda16ea1b12017-12-12 14:52:48 -08001176 /**
1177 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1178 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001179 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1180 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1181 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1182 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001183
1184 private final List<WallpaperInfo> mWallpapers;
1185
1186 private int mPendingSelectedAdapterPosition;
1187 private int mSelectedAdapterPosition;
1188
1189 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1190 mWallpapers = wallpapers;
1191 mPendingSelectedAdapterPosition = -1;
1192 mSelectedAdapterPosition = -1;
1193 }
1194
1195 @Override
1196 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1197 switch (viewType) {
1198 case ITEM_VIEW_TYPE_ROTATION:
1199 return createRotationHolder(parent);
1200 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1201 return createIndividualHolder(parent);
1202 case ITEM_VIEW_TYPE_MY_PHOTOS:
1203 return createMyPhotosHolder(parent);
1204 default:
1205 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1206 return null;
1207 }
1208 }
1209
1210 @Override
1211 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001212 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001213 return ITEM_VIEW_TYPE_ROTATION;
1214 }
1215
1216 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1217 if (mCategory.supportsCustomPhotos()
1218 && !isRotationEnabled()
1219 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1220 return ITEM_VIEW_TYPE_MY_PHOTOS;
1221 }
1222
1223 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1224 }
1225
1226 @Override
1227 public void onBindViewHolder(ViewHolder holder, int position) {
1228 int viewType = getItemViewType(position);
1229
1230 switch (viewType) {
1231 case ITEM_VIEW_TYPE_ROTATION:
1232 onBindRotationHolder(holder, position);
1233 break;
1234 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1235 onBindIndividualHolder(holder, position);
1236 break;
1237 case ITEM_VIEW_TYPE_MY_PHOTOS:
1238 ((MyPhotosViewHolder) holder).bind();
1239 break;
1240 default:
1241 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1242 }
1243 }
1244
1245 @Override
1246 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001247 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001248 ? mWallpapers.size() + 1
1249 : mWallpapers.size();
1250 }
1251
1252 private ViewHolder createRotationHolder(ViewGroup parent) {
1253 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001254 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001255 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Chuck Liao5a4243b2020-05-20 23:56:39 +08001256 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1257 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001258 }
1259
1260 private ViewHolder createIndividualHolder(ViewGroup parent) {
1261 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1262 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1263
1264 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001265 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001266 return new SetIndividualHolder(
1267 getActivity(), mTileSizePx.y, view,
1268 selectionAnimator,
1269 new OnSetListener() {
1270 @Override
1271 public void onPendingWallpaperSet(int adapterPosition) {
1272 // Deselect and hide loading indicator for any previously pending tile.
1273 if (mPendingSelectedAdapterPosition != -1) {
1274 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1275 mPendingSelectedAdapterPosition);
1276 if (oldViewHolder instanceof SelectableHolder) {
1277 ((SelectableHolder) oldViewHolder).setSelectionState(
1278 SelectableHolder.SELECTION_STATE_DESELECTED);
1279 }
1280 }
1281
1282 if (mSelectedAdapterPosition != -1) {
1283 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1284 mSelectedAdapterPosition);
1285 if (oldViewHolder instanceof SelectableHolder) {
1286 ((SelectableHolder) oldViewHolder).setSelectionState(
1287 SelectableHolder.SELECTION_STATE_DESELECTED);
1288 }
1289 }
1290
1291 mPendingSelectedAdapterPosition = adapterPosition;
1292 }
1293
1294 @Override
1295 public void onWallpaperSet(int adapterPosition) {
1296 // No-op -- UI handles a new wallpaper being set by reacting to the
1297 // WallpaperChangedNotifier.
1298 }
1299
1300 @Override
1301 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1302 showSetWallpaperErrorDialog();
1303 mPendingSetIndividualHolder = holder;
1304 }
1305 });
1306 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001307 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001308 }
1309 }
1310
1311 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1312 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1313 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1314
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001315 return new MyPhotosViewHolder(getActivity(),
1316 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1317 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001318 }
1319
1320 /**
1321 * Marks the tile at the given position as selected with a visual indication. Also updates the
1322 * "currently selected" BottomSheet to reflect the newly selected tile.
1323 */
1324 private void updateSelectedTile(int newlySelectedPosition) {
1325 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1326 // succession.
1327 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1328 return;
1329 }
1330
1331 if (mCurrentWallpaperBottomSheetPresenter != null) {
1332 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1333 IndividualPickerFragment.this);
1334
1335 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1336 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1337 }
1338 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1339 @Override
1340 public void run() {
1341 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1342 }
1343 };
1344 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1345 }
1346
1347 // User may have switched to another category, thus detaching this fragment, so check here.
1348 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1349 // still occurs in the UI after the user selects that other category.
1350 if (getActivity() == null) {
1351 return;
1352 }
1353
1354 // Update the newly selected wallpaper ViewHolder and the old one so that if
1355 // selection UI state applies (desktop UI), it is updated.
1356 if (mSelectedAdapterPosition >= 0) {
1357 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1358 mSelectedAdapterPosition);
1359 if (oldViewHolder instanceof SelectableHolder) {
1360 ((SelectableHolder) oldViewHolder).setSelectionState(
1361 SelectableHolder.SELECTION_STATE_DESELECTED);
1362 }
1363 }
1364
1365 // Animate selection of newly selected tile.
1366 ViewHolder newViewHolder = mImageGrid
1367 .findViewHolderForAdapterPosition(newlySelectedPosition);
1368 if (newViewHolder instanceof SelectableHolder) {
1369 ((SelectableHolder) newViewHolder).setSelectionState(
1370 SelectableHolder.SELECTION_STATE_SELECTED);
1371 }
1372
1373 mSelectedAdapterPosition = newlySelectedPosition;
1374
1375 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1376 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1377 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1378 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1379 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1380 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1381
1382 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1383 }
1384
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001385 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001386 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1387 String collectionId = mCategory.getCollectionId();
1388 ((DesktopRotationHolder) holder).bind(collectionId);
1389
1390 if (mWallpaperPreferences.getWallpaperPresentationMode()
1391 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1392 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1393 mSelectedAdapterPosition = position;
1394 }
1395
1396 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1397 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1398 mWasUpdateRunnableRun = true;
1399 }
1400 }
1401 }
1402
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001403 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001404 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001405 ? position - 1 : position;
1406 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1407 ((IndividualHolder) holder).bindWallpaper(wallpaper);
Chuck Liaoec053b02021-04-24 01:30:50 +08001408 boolean isWallpaperApplied = mAppliedWallpaperIds.contains(wallpaper.getWallpaperId());
“Chuckffd832c2020-03-22 02:15:58 +08001409 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1410 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001411
“Chuckffd832c2020-03-22 02:15:58 +08001412 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001413 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001414 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001415 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001416
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001417 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001418 thumbnail.setClipped(isWallpaperApplied);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001419 }
1420 }
Chuck Liaoba401232020-03-13 20:11:04 +08001421
1422 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1423
Chuck Liao1e6cb682021-04-22 00:51:11 +08001424 private final int mPaddingHorizontal;
1425 private final int mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001426
Chuck Liao1e6cb682021-04-22 00:51:11 +08001427 GridPaddingDecoration(int paddingHorizontal, int paddingBottom) {
1428 mPaddingHorizontal = paddingHorizontal;
1429 mPaddingBottom = paddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001430 }
1431
1432 @Override
1433 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1434 RecyclerView.State state) {
1435 int position = parent.getChildAdapterPosition(view);
1436 if (position >= 0) {
Chuck Liao1e6cb682021-04-22 00:51:11 +08001437 outRect.left = mPaddingHorizontal;
1438 outRect.right = mPaddingHorizontal;
1439 outRect.bottom = mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001440 }
1441 }
1442 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001443}