blob: 44363043ca5abc557a15c1813f078f7c91e4c702 [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;
Chuck Liao369e0822021-07-01 00:09:22 +080041import android.widget.ImageView;
42import android.widget.RelativeLayout;
Jon Miranda16ea1b12017-12-12 14:52:48 -080043import android.widget.Toast;
44
Chuck Liao369e0822021-07-01 00:09:22 +080045import androidx.annotation.DrawableRes;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070046import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080047import androidx.annotation.Nullable;
Ching-Sung Li1ac024f2021-06-08 14:43:28 +080048import androidx.cardview.widget.CardView;
Chuck Liaof6b4b192020-08-07 02:31:32 +080049import androidx.core.widget.ContentLoadingProgressBar;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080050import androidx.fragment.app.DialogFragment;
Chuck Liao7e333722021-02-27 02:21:20 +080051import androidx.fragment.app.Fragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080052import androidx.recyclerview.widget.GridLayoutManager;
53import androidx.recyclerview.widget.RecyclerView;
54import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
55import androidx.recyclerview.widget.RecyclerView.ViewHolder;
56
Jon Miranda16ea1b12017-12-12 14:52:48 -080057import com.android.wallpaper.R;
58import com.android.wallpaper.asset.Asset;
59import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070060import com.android.wallpaper.model.Category;
61import com.android.wallpaper.model.CategoryProvider;
62import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080063import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080064import com.android.wallpaper.model.WallpaperCategory;
65import com.android.wallpaper.model.WallpaperInfo;
66import com.android.wallpaper.model.WallpaperReceiver;
67import com.android.wallpaper.model.WallpaperRotationInitializer;
68import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
69import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080070import com.android.wallpaper.module.FormFactorChecker;
71import com.android.wallpaper.module.FormFactorChecker.FormFactor;
72import com.android.wallpaper.module.Injector;
73import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070074import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080075import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080076import com.android.wallpaper.module.WallpaperChangedNotifier;
77import com.android.wallpaper.module.WallpaperPersister;
78import com.android.wallpaper.module.WallpaperPersister.Destination;
79import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080080import com.android.wallpaper.module.WallpaperSetter;
Chuck Liao58aca1c2021-03-17 01:20:55 +080081import com.android.wallpaper.picker.AppbarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080082import com.android.wallpaper.picker.BaseActivity;
83import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Chuck Liaob17d1502020-09-07 21:55:30 +080084import com.android.wallpaper.picker.FragmentTransactionChecker;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080085import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -080086import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080087import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080088import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
89import com.android.wallpaper.picker.StartRotationDialogFragment;
90import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080091import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080092import com.android.wallpaper.picker.WallpapersUiContainer;
93import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
94import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070095import com.android.wallpaper.util.SizeCalculator;
chihhangchuang1a29e752020-04-28 18:22:53 +080096import com.android.wallpaper.widget.WallpaperInfoView;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080097import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
98import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070099
Jon Miranda16ea1b12017-12-12 14:52:48 -0800100import com.bumptech.glide.Glide;
101import com.bumptech.glide.MemoryCategory;
102
103import java.util.ArrayList;
104import java.util.Date;
105import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800106import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800107import java.util.Random;
Chuck Liaoec053b02021-04-24 01:30:50 +0800108import java.util.Set;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800109
110/**
111 * Displays the Main UI for picking an individual wallpaper image.
112 */
Chuck Liao58aca1c2021-03-17 01:20:55 +0800113public class IndividualPickerFragment extends AppbarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800114 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
115 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800116 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
117 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800118
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800119 /**
120 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
121 * such as "my photos" or "daily rotation".
122 */
123 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
124 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
125
Chuck Liao6db3eba2021-06-16 23:50:45 +0800126 protected static final int MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT = 8;
127
Jon Miranda16ea1b12017-12-12 14:52:48 -0800128 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800129 private static final int UNUSED_REQUEST_CODE = 1;
130 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
131 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
132 private static final String PROGRESS_DIALOG_NO_TITLE = null;
133 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
134 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
135 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700136 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800137
Chuck Liao8ec38e02020-02-26 20:59:32 +0800138 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800139 * An interface for updating the thumbnail with the specific wallpaper.
140 */
141 public interface ThumbnailUpdater {
142 /**
143 * Updates the thumbnail with the specific wallpaper.
144 */
145 void updateThumbnail(WallpaperInfo wallpaperInfo);
146
147 /**
148 * Restores to the thumbnails of the wallpapers which were applied.
149 */
150 void restoreThumbnails();
151 }
152
“Chuck7ef99722020-03-22 04:34:03 +0800153 /**
154 * An interface for receiving the destination of the new applied wallpaper.
155 */
156 public interface WallpaperDestinationCallback {
157 /**
158 * Called when the destination of the wallpaper is set.
159 *
160 * @param destination the destination which a wallpaper may be set.
161 * See {@link Destination} for more details.
162 */
163 void onDestinationSet(@Destination int destination);
164 }
165
Chuck Liaob3829fb2020-04-01 00:47:50 +0800166 /**
167 * The listener which will be notified when the wallpaper is selected.
168 */
169 public interface WallpaperSelectedListener {
170 /**
171 * Called when the wallpaper is selected.
172 *
173 * @param position the position of the selected wallpaper
174 */
175 void onWallpaperSelected(int position);
176 }
177
Chuck Liaof6b4b192020-08-07 02:31:32 +0800178 /**
Chuck Liao7e333722021-02-27 02:21:20 +0800179 * Interface to be implemented by a Fragment(or an Activity) hosting
180 * a {@link IndividualPickerFragment}.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800181 */
182 public interface IndividualPickerFragmentHost {
183 /**
Chuck Liao58aca1c2021-03-17 01:20:55 +0800184 * Indicates if the host has toolbar to show the title. If it does, we should set the title
185 * there.
186 */
187 boolean isHostToolbarShown();
188
189 /**
190 * Sets the title in the host's toolbar.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800191 */
192 void setToolbarTitle(CharSequence title);
193
194 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800195 * Configures the menu in the toolbar.
196 *
197 * @param menuResId the resource id of the menu
198 */
199 void setToolbarMenu(@MenuRes int menuResId);
200
201 /**
202 * Removes the menu in the toolbar.
203 */
204 void removeToolbarMenu();
205
206 /**
Chuck Liaof6b4b192020-08-07 02:31:32 +0800207 * Moves to the previous fragment.
208 */
209 void moveToPreviousFragment();
210 }
211
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800212 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800213 WallpaperPreferences mWallpaperPreferences;
214 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800215 RecyclerView mImageGrid;
216 IndividualAdapter mAdapter;
217 WallpaperCategory mCategory;
218 WallpaperRotationInitializer mWallpaperRotationInitializer;
219 List<WallpaperInfo> mWallpapers;
220 Point mTileSizePx;
221 WallpapersUiContainer mWallpapersUiContainer;
222 @FormFactor
223 int mFormFactor;
224 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800225
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800226 Handler mHandler;
227 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800228 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800229
230 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
231 new WallpaperChangedNotifier.Listener() {
232 @Override
233 public void onWallpaperChanged() {
234 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
235 return;
236 }
237
238 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
239 mAdapter.mSelectedAdapterPosition);
240
241 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
242 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
243 if (selectedViewHolder instanceof SelectableHolder) {
244 ((SelectableHolder) selectedViewHolder).setSelectionState(
245 SelectableHolder.SELECTION_STATE_DESELECTED);
246 }
247 } else {
248 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
249 }
250 }
251 };
252 PackageStatusNotifier.Listener mAppStatusListener;
chihhangchuang1a29e752020-04-28 18:22:53 +0800253 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800254 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800255
chihhangchuangc687d912020-05-04 14:33:05 +0800256 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800257 private ProgressDialog mProgressDialog;
258 private boolean mTestingMode;
259 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800260 private SetIndividualHolder mPendingSetIndividualHolder;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800261 private ContentLoadingProgressBar mLoading;
Chuck Liaoddf2b522021-04-15 00:36:25 +0800262 private CategoryProvider mCategoryProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800263
264 /**
265 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
266 * committing fragment transactions.
267 */
268 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
269 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
270
Jon Miranda16ea1b12017-12-12 14:52:48 -0800271 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800272
273 /**
274 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
275 * invocation of the fragment.
276 */
277 private boolean mWasUpdateRunnableRun;
278
279 /**
280 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
281 * mode.
282 */
283 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
284 @Override
285 public void run() {
286 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
287 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
288 if (viewHolder instanceof DesktopRotationHolder) {
289 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
290 } else { // viewHolder is null
291 // If the rotation tile is unavailable (because user has scrolled down, causing the
292 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
293 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
294 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
295 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
296 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
297 }
298 }
299 };
300
Chuck Liao69630f12020-03-05 19:01:25 +0800301 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800302 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800303 private WallpaperManager mWallpaperManager;
304 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800305 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liaoec053b02021-04-24 01:30:50 +0800306 private Set<String> mAppliedWallpaperIds;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800307
Jon Miranda16ea1b12017-12-12 14:52:48 -0800308 public static IndividualPickerFragment newInstance(String collectionId) {
309 Bundle args = new Bundle();
310 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
311
312 IndividualPickerFragment fragment = new IndividualPickerFragment();
313 fragment.setArguments(args);
314 return fragment;
315 }
316
“Chuck7ef99722020-03-22 04:34:03 +0800317 /**
318 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
319 * would be set.
320 *
321 * @param wallpaperDestination the destination a wallpaper would be set.
322 * It will be either {@link Destination#DEST_HOME_SCREEN}
323 * or {@link Destination#DEST_LOCK_SCREEN}.
324 */
325 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
326 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800327 if (mWallpapers != null) {
328 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800329 }
330 }
331
Jon Miranda16ea1b12017-12-12 14:52:48 -0800332 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
333 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
334 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
335 getActivity());
336 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
337 @Override
338 public void onDrawableLoaded() {
339 if (getActivity() == null) {
340 return;
341 }
342
343 // Schedule the next update of the thumbnail.
344 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
345 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
346 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
347 }
348 });
349 }
350
351 @Override
352 public void onCreate(Bundle savedInstanceState) {
353 super.onCreate(savedInstanceState);
354
355 Injector injector = InjectorProvider.getInjector();
356 Context appContext = getContext().getApplicationContext();
357 mWallpaperPreferences = injector.getPreferences(appContext);
358
359 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
360 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
361
“Chuck7ef99722020-03-22 04:34:03 +0800362 mWallpaperManager = WallpaperManager.getInstance(appContext);
363
Jon Miranda16ea1b12017-12-12 14:52:48 -0800364 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
365
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700366 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
367
chihhangchuangc687d912020-05-04 14:33:05 +0800368 mUserEventLogger = injector.getUserEventLogger(appContext);
369
“Chuckffd832c2020-03-22 02:15:58 +0800370 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800371 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800372 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800373 injector.getPreferences(appContext),
374 injector.getUserEventLogger(appContext),
375 false);
376
Jon Miranda16ea1b12017-12-12 14:52:48 -0800377 mWallpapers = new ArrayList<>();
378 mRandom = new Random();
379 mHandler = new Handler();
380
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700381 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
382 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
383 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
384 Glide.get(getContext()).clearMemory();
385 }
386
Chuck Liaoddf2b522021-04-15 00:36:25 +0800387 mCategoryProvider = injector.getCategoryProvider(appContext);
388 mCategoryProvider.fetchCategories(new CategoryReceiver() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700389 @Override
390 public void onCategoryReceived(Category category) {
391 // Do nothing.
392 }
393
394 @Override
395 public void doneFetchingCategories() {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800396 Category category = mCategoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700397 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800398 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800399 return;
400 }
401 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700402 if (mCategory == null) {
403 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
404
405 // The absence of this category in the CategoryProvider indicates a broken
406 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800407 getIndividualPickerFragmentHost().moveToPreviousFragment();
408 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
409 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700410 return;
411 }
412 onCategoryLoaded();
413 }
414 }, false);
415 }
416
Chuck Liaof6b4b192020-08-07 02:31:32 +0800417
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700418 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800419 if (getIndividualPickerFragmentHost() == null) {
420 return;
421 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800422 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
423 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
424 } else {
425 setTitle(mCategory.getTitle());
426 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700427 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao33f1af42021-04-13 20:52:35 +0800428 if (mToolbar != null && isRotationEnabled()) {
429 setUpToolbarMenu(R.menu.individual_picker_menu);
430 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700431 fetchWallpapers(false);
432
433 if (mCategory.supportsThirdParty()) {
434 mAppStatusListener = (packageName, status) -> {
435 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
436 mCategory.containsThirdParty(packageName)) {
437 fetchWallpapers(true);
438 }
439 };
440 mPackageStatusNotifier.addListener(mAppStatusListener,
441 WallpaperService.SERVICE_INTERFACE);
442 }
443 }
444
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800445 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700446 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800447 mIsWallpapersReceived = false;
448 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800449 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
450 @Override
451 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800452 mIsWallpapersReceived = true;
453 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800454 for (WallpaperInfo wallpaper : wallpapers) {
455 mWallpapers.add(wallpaper);
456 }
Chuck Liaob10d2a32021-04-27 20:59:53 +0800457 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800458
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700459 // Wallpapers may load after the adapter is initialized, in which case we have
460 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800461 if (mAdapter != null) {
462 mAdapter.notifyDataSetChanged();
463 }
464
465 if (mWallpapersUiContainer != null) {
466 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700467 } else {
468 if (wallpapers.isEmpty()) {
469 // If there are no more wallpapers and we're on phone, just finish the
470 // Activity.
471 Activity activity = getActivity();
472 if (activity != null
473 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
474 activity.finish();
475 }
476 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800477 }
478 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700479 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800480 }
481
Chuck Liaof6b4b192020-08-07 02:31:32 +0800482 void updateLoading() {
483 if (mLoading == null) {
484 return;
485 }
486
487 if (mIsWallpapersReceived) {
488 mLoading.hide();
489 } else {
490 mLoading.show();
491 }
492 }
493
Jon Miranda16ea1b12017-12-12 14:52:48 -0800494 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700495 public void onSaveInstanceState(@NonNull Bundle outState) {
496 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700497 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700498 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
499 }
500
501 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800502 public View onCreateView(LayoutInflater inflater, ViewGroup container,
503 Bundle savedInstanceState) {
504 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
Chuck Liao58aca1c2021-03-17 01:20:55 +0800505 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
506 view.findViewById(R.id.header_bar).setVisibility(View.GONE);
Kunhung Lid0174362021-04-05 15:31:41 +0800507 setUpArrowEnabled(/* upArrow= */ true);
Chuck Liaoa7215812021-04-13 22:33:43 +0800508 if (isRotationEnabled()) {
509 getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu);
510 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800511 } else {
512 setUpToolbar(view);
Chuck Liao33f1af42021-04-13 20:52:35 +0800513 if (isRotationEnabled()) {
514 setUpToolbarMenu(R.menu.individual_picker_menu);
515 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800516 if (mCategory != null) {
517 setTitle(mCategory.getTitle());
518 }
519 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800520
Chuck Liaoec053b02021-04-24 01:30:50 +0800521 mAppliedWallpaperIds = getAppliedWallpaperIds();
522
Jon Miranda16ea1b12017-12-12 14:52:48 -0800523 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
524 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
525 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
526 updateImageGridPadding(false /* addExtraBottomSpace */);
527 mImageGrid.setScrollBarSize(gridPaddingPx);
528 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800529 mLoading = view.findViewById(R.id.loading_indicator);
530 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700531 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800532 setUpBottomSheet();
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800533 // For nav bar edge-to-edge effect.
Kunhung Li1b0cb472021-04-26 20:52:04 +0800534 view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
535 // For status bar height.
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800536 v.setPadding(
537 v.getPaddingLeft(),
Kunhung Li1b0cb472021-04-26 20:52:04 +0800538 windowInsets.getSystemWindowInsetTop(),
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800539 v.getPaddingRight(),
Kunhung Li1b0cb472021-04-26 20:52:04 +0800540 v.getPaddingBottom());
541
542 View gridView = v.findViewById(R.id.wallpaper_grid);
543 gridView.setPadding(
544 gridView.getPaddingLeft(),
545 gridView.getPaddingTop(),
546 gridView.getPaddingRight(),
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800547 windowInsets.getSystemWindowInsetBottom());
548 return windowInsets.consumeSystemWindowInsets();
549 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800550 return view;
551 }
552
553 @Override
554 public void onClickTryAgain(@Destination int unused) {
555 if (mPendingSetIndividualHolder != null) {
556 mPendingSetIndividualHolder.setWallpaper();
557 }
558 }
559
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800560 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800561 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
562 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
563 R.dimen.current_wallpaper_bottom_sheet_layout_height);
564 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
565 // Only left and top may be set in order for the GridMarginDecoration to work properly.
566 mImageGrid.setPadding(
567 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
568 }
569
Chuck Liaof6b4b192020-08-07 02:31:32 +0800570 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
Chuck Liao7e333722021-02-27 02:21:20 +0800571 Fragment parentFragment = getParentFragment();
572 if (parentFragment != null) {
573 return (IndividualPickerFragmentHost) parentFragment;
574 } else {
575 return (IndividualPickerFragmentHost) getActivity();
576 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800577 }
578
Chuck Liaob10d2a32021-04-27 20:59:53 +0800579 protected void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700580 // Skip if mImageGrid been initialized yet
581 if (mImageGrid == null) {
582 return;
583 }
584 // Skip if category hasn't loaded yet
585 if (mCategory == null) {
586 return;
587 }
Chuck Liao658ee892021-06-30 10:17:12 +0800588 if (getContext() == null) {
589 return;
590 }
Chuck Liao6db3eba2021-06-16 23:50:45 +0800591
592 // Wallpaper count could change, so we may need to change the layout(2 or 3 columns layout)
593 GridLayoutManager gridLayoutManager = (GridLayoutManager) mImageGrid.getLayoutManager();
594 boolean needUpdateLayout =
595 gridLayoutManager != null && gridLayoutManager.getSpanCount() != getNumColumns();
596
597 // Skip if the adapter was already created and don't need to change the layout
598 if (mAdapter != null && !needUpdateLayout) {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700599 return;
600 }
Chuck Liao6db3eba2021-06-16 23:50:45 +0800601
602 // Clear the old decoration
603 int decorationCount = mImageGrid.getItemDecorationCount();
604 for (int i = 0; i < decorationCount; i++) {
605 mImageGrid.removeItemDecorationAt(i);
606 }
607
Chuck Liao1e6cb682021-04-22 00:51:11 +0800608 mImageGrid.addItemDecoration(new GridPaddingDecoration(getGridItemPaddingHorizontal(),
609 getGridItemPaddingBottom()));
610 int edgePadding = getEdgePadding();
611 mImageGrid.setPadding(edgePadding, mImageGrid.getPaddingTop(), edgePadding,
612 mImageGrid.getPaddingBottom());
Chuck Liaob10d2a32021-04-27 20:59:53 +0800613 mTileSizePx = isFewerColumnLayout()
Chuck Liaoddf2b522021-04-15 00:36:25 +0800614 ? SizeCalculator.getFeaturedIndividualTileSize(getActivity())
615 : SizeCalculator.getIndividualTileSize(getActivity());
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700616 setUpImageGrid();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800617 mImageGrid.setAccessibilityDelegateCompat(
618 new WallpaperPickerRecyclerViewAccessibilityDelegate(
619 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700620 }
621
Ching-Sung Lib69e9932021-05-25 20:03:01 +0800622 boolean isFewerColumnLayout() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800623 return mWallpapers != null && mWallpapers.size() <= MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT;
624 }
625
Chuck Liao1e6cb682021-04-22 00:51:11 +0800626 private int getGridItemPaddingHorizontal() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800627 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800628 ? getResources().getDimensionPixelSize(
629 R.dimen.grid_item_featured_individual_padding_horizontal)
630 : getResources().getDimensionPixelSize(
631 R.dimen.grid_item_individual_padding_horizontal);
632 }
633
634 private int getGridItemPaddingBottom() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800635 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800636 ? getResources().getDimensionPixelSize(
637 R.dimen.grid_item_featured_individual_padding_bottom)
638 : getResources().getDimensionPixelSize(R.dimen.grid_item_individual_padding_bottom);
639 }
640
641 private int getEdgePadding() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800642 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800643 ? getResources().getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space)
644 : getResources().getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space);
645 }
646
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700647 /**
648 * Create the adapter and assign it to mImageGrid.
649 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
650 */
651 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800652 mAdapter = new IndividualAdapter(mWallpapers);
653 mImageGrid.setAdapter(mAdapter);
654 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
655 }
656
Jon Miranda16ea1b12017-12-12 14:52:48 -0800657 /**
658 * Enables and populates the "Currently set" wallpaper BottomSheet.
659 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800660 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800661 mImageGrid.addOnScrollListener(new OnScrollListener() {
662 @Override
663 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
664 if (mCurrentWallpaperBottomSheetPresenter == null) {
665 return;
666 }
667
668 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
669 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
670 }
671 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
672 @Override
673 public void run() {
674 if (dy > 0) {
675 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
676 } else {
677 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
678 }
679 }
680 };
681 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
682 }
683 });
684 }
685
chihhangchuang3efb6832020-04-17 02:06:25 +0800686 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800687 public void onResume() {
688 super.onResume();
689
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700690 WallpaperPreferences preferences = InjectorProvider.getInjector()
691 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800692 preferences.setLastAppActiveTimestamp(new Date().getTime());
693
694 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
695 // PreviewFragment.
696 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
697
698 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
699 // shown earlier when this fragment's hosting activity didn't allow committing fragment
700 // transactions.
701 if (mStagedStartRotationErrorDialogFragment != null) {
702 mStagedStartRotationErrorDialogFragment.show(
703 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
704 mStagedStartRotationErrorDialogFragment = null;
705 }
706
707 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
708 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
709 // committing fragment transactions.
710 if (mStagedSetWallpaperErrorDialogFragment != null) {
711 mStagedSetWallpaperErrorDialogFragment.show(
712 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
713 mStagedSetWallpaperErrorDialogFragment = null;
714 }
715
Chuck Liao5a4243b2020-05-20 23:56:39 +0800716 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
717 // Must be resuming from a previously stopped state, so re-schedule the update of the
718 // daily wallpapers tile thumbnail.
719 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800720 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800721 }
722
723 @Override
724 public void onStop() {
725 super.onStop();
726 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
727 }
728
729 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800730 public void onDestroyView() {
731 super.onDestroyView();
732 getIndividualPickerFragmentHost().removeToolbarMenu();
733 }
734
735 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800736 public void onDestroy() {
737 super.onDestroy();
738 if (mProgressDialog != null) {
739 mProgressDialog.dismiss();
740 }
741 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700742 if (mAppStatusListener != null) {
743 mPackageStatusNotifier.removeListener(mAppStatusListener);
744 }
Chuck Liao69630f12020-03-05 19:01:25 +0800745 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800746 }
747
748 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800749 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800750 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
751 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
752 // still holds a reference to the destroyed Fragment and is calling
753 // onStartRotationDialogDismissed on that destroyed Fragment.
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800754 }
755
756 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800757 public void retryStartRotation(@NetworkPreference int networkPreference) {
758 startRotation(networkPreference);
759 }
760
761 public void setCurrentWallpaperBottomSheetPresenter(
762 CurrentWallpaperBottomSheetPresenter presenter) {
763 mCurrentWallpaperBottomSheetPresenter = presenter;
764 }
765
766 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
767 mWallpapersUiContainer = uiContainer;
768 }
769
Chuck Liaob3829fb2020-04-01 00:47:50 +0800770 public void setOnWallpaperSelectedListener(
771 WallpaperSelectedListener wallpaperSelectedListener) {
772 mWallpaperSelectedListener = wallpaperSelectedListener;
773 }
774
775 /**
776 * Resizes the layout's height.
777 */
778 public void resizeLayout(int height) {
779 mImageGrid.getLayoutParams().height = height;
780 mImageGrid.requestLayout();
781 }
782
783 /**
784 * Scrolls to the specific item.
785 *
786 * @param position the position of the item
787 */
788 public void scrollToPosition(int position) {
789 ((GridLayoutManager) mImageGrid.getLayoutManager())
790 .scrollToPositionWithOffset(position, /* offset= */ 0);
791 }
792
Jon Miranda16ea1b12017-12-12 14:52:48 -0800793 /**
794 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
795 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
796 * constantly keeps the UI thread alive and blocks a test forever.
797 *
798 * @param testingMode
799 */
800 void setTestingMode(boolean testingMode) {
801 mTestingMode = testingMode;
802 }
803
Jon Miranda16ea1b12017-12-12 14:52:48 -0800804 @Override
805 public void startRotation(@NetworkPreference final int networkPreference) {
806 if (!isRotationEnabled()) {
807 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
808 return;
809 }
810
811 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
812 // causes Espresso to hang once the dialog is shown.
813 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
814 int themeResId;
815 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
816 themeResId = R.style.ProgressDialogThemePreL;
817 } else {
818 themeResId = R.style.LightDialogTheme;
819 }
820 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
821
822 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
823 mProgressDialog.setMessage(
824 getResources().getString(R.string.start_rotation_progress_message));
825 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
826 mProgressDialog.show();
827 }
828
829 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
830 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
831 }
832
833 final Context appContext = getActivity().getApplicationContext();
834
835 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
836 appContext,
837 networkPreference,
838 new Listener() {
839 @Override
840 public void onFirstWallpaperInRotationSet() {
841 if (mProgressDialog != null) {
842 mProgressDialog.dismiss();
843 }
844
845 // The fragment may be detached from its containing activity if the user exits the
846 // app before the first wallpaper image in rotation finishes downloading.
847 Activity activity = getActivity();
848
Jon Miranda16ea1b12017-12-12 14:52:48 -0800849
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700850 if (mWallpaperRotationInitializer.startRotation(appContext)) {
851 if (activity != null
852 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
853 try {
854 Toast.makeText(getActivity(),
855 R.string.wallpaper_set_successfully_message,
856 Toast.LENGTH_SHORT).show();
857 } catch (NotFoundException e) {
858 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800859 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800860
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700861 activity.setResult(Activity.RESULT_OK);
862 activity.finish();
863 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
864 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
865 }
866 } else { // Failed to start rotation.
867 showStartRotationErrorDialog(networkPreference);
868
869 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
870 DesktopRotationHolder rotationViewHolder =
871 (DesktopRotationHolder)
872 mImageGrid.findViewHolderForAdapterPosition(
873 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
874 rotationViewHolder.setSelectionState(
875 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800876 }
877 }
878 }
879
880 @Override
881 public void onError() {
882 if (mProgressDialog != null) {
883 mProgressDialog.dismiss();
884 }
885
886 showStartRotationErrorDialog(networkPreference);
887
888 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
889 DesktopRotationHolder rotationViewHolder =
890 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
891 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
892 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
893 }
894 }
895 });
896 }
897
898 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800899 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800900 if (activity != null) {
901 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
902 StartRotationErrorDialogFragment.newInstance(networkPreference);
903 startRotationErrorDialogFragment.setTargetFragment(
904 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
905
906 if (activity.isSafeToCommitFragmentTransaction()) {
907 startRotationErrorDialogFragment.show(
908 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
909 } else {
910 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
911 }
912 }
913 }
914
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800915 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800916 Activity activity = getActivity();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800917 if (activity == null) {
918 return 1;
919 }
Chuck Liaob10d2a32021-04-27 20:59:53 +0800920 return isFewerColumnLayout()
Chuck Liaoddf2b522021-04-15 00:36:25 +0800921 ? SizeCalculator.getNumFeaturedIndividualColumns(activity)
922 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800923 }
924
925 /**
926 * Returns whether rotation is enabled for this category.
927 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800928 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700929 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800930 }
931
932 @Override
933 public void onCurrentWallpaperRefreshed() {
934 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
935 }
936
Chuck Liao69630f12020-03-05 19:01:25 +0800937 @Override
938 public void onSet(int destination) {
939 if (mSelectedWallpaperInfo == null) {
940 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
941 return;
942 }
943
“Chuckffd832c2020-03-22 02:15:58 +0800944 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800945 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
946 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
Santiago Etchebeherec320c5f2021-05-14 17:35:57 -0700947 destination, 0, null, null, mSetWallpaperCallback);
Chuck Liao69630f12020-03-05 19:01:25 +0800948 } else {
949 mWallpaperSetter.setCurrentWallpaper(
950 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
951 }
“Chuck7ef99722020-03-22 04:34:03 +0800952 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800953 }
954
955 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
956 new WallpaperPersister.SetWallpaperCallback() {
957 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800958 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800959 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800960 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
961 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800962 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
963 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800964 }
965
966 @Override
967 public void onError(@Nullable Throwable throwable) {
968 Log.e(TAG, "Can't apply the wallpaper.");
Chuck Liao69630f12020-03-05 19:01:25 +0800969 }
970 };
971
972 @Override
Chuck Liao33f1af42021-04-13 20:52:35 +0800973 public boolean onMenuItemClick(MenuItem item) {
974 if (item.getItemId() == R.id.daily_rotation) {
Chuck Liaoa7215812021-04-13 22:33:43 +0800975 showRotationDialog();
Chuck Liao33f1af42021-04-13 20:52:35 +0800976 return true;
977 }
978 return super.onMenuItemClick(item);
979 }
980
Jon Miranda16ea1b12017-12-12 14:52:48 -0800981 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800982 * Popups a daily rotation dialog for the uses to confirm.
983 */
984 public void showRotationDialog() {
985 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
986 startRotationDialogFragment.setTargetFragment(
987 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
988 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
989 }
990
991 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800992 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
993 */
994 private void showSetWallpaperErrorDialog() {
995 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
996 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
997 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
998
999 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
1000 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
1001 } else {
1002 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
1003 }
1004 }
1005
Chuck Liao69630f12020-03-05 19:01:25 +08001006 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +08001007 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +08001008 if (thumbnailUpdater == null) {
1009 return;
1010 }
1011
1012 if (selectedWallpaperInfo != null) {
1013 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
1014 } else {
1015 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +08001016 }
1017 }
1018
“Chuck7ef99722020-03-22 04:34:03 +08001019 private void onWallpaperDestinationSet(int destination) {
1020 WallpaperDestinationCallback wallpaperDestinationCallback =
1021 (WallpaperDestinationCallback) getParentFragment();
1022 if (wallpaperDestinationCallback == null) {
1023 return;
1024 }
1025
1026 wallpaperDestinationCallback.onDestinationSet(destination);
1027 }
1028
Ching-Sung Li073812b2020-04-07 21:19:21 +08001029 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +08001030 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001031 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +08001032 return;
1033 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001034 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +08001035 updateActivatedStatus(mSelectedWallpaperInfo == null
1036 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001037 // Update new selected wallpaper.
1038 updateActivatedStatus(newSelectedWallpaperInfo == null
1039 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
1040
1041 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001042 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +08001043 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +08001044 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +08001045 WallpaperInfoHelper.loadExploreIntent(
1046 getContext(),
1047 mSelectedWallpaperInfo,
1048 (actionLabel, exploreIntent) ->
1049 mWallpaperInfoView.populateWallpaperInfo(
Chuck Liao9991d682021-01-20 17:45:14 +08001050 mSelectedWallpaperInfo,
1051 actionLabel,
1052 WallpaperInfoHelper.shouldShowExploreButton(
1053 getContext(), exploreIntent),
1054 v -> onExploreClicked(exploreIntent))
1055 );
Chuck Liaob3829fb2020-04-01 00:47:50 +08001056 }
1057
1058 if (mWallpaperSelectedListener != null) {
1059 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001060 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001061 }
1062
chihhangchuangc687d912020-05-04 14:33:05 +08001063 private void onExploreClicked(Intent exploreIntent) {
1064 if (getContext() == null) {
1065 return;
1066 }
1067 Context context = getContext();
1068 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
1069 mSelectedWallpaperInfo.getActionLabelRes(context));
1070
1071 startActivity(exploreIntent);
1072 }
1073
Chuck Liao369e0822021-07-01 00:09:22 +08001074 // TODO: Dead code. Should remove this method in the future.
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001075 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +08001076 if (wallpaperInfo == null) {
1077 return;
1078 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001079 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001080 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001081 ? index + 1 : index;
1082 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1083 if (holder != null) {
Chuck Liao369e0822021-07-01 00:09:22 +08001084 holder.itemView.setActivated(isActivated);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001085 } else {
1086 // Item is not visible, make sure the item is re-bound when it becomes visible.
1087 mAdapter.notifyItemChanged(index);
1088 }
1089 }
1090
Chuck Liao369e0822021-07-01 00:09:22 +08001091 // TODO: Dead code. Should remove this method in the future.
“Chuckffd832c2020-03-22 02:15:58 +08001092 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1093 if (wallpaperInfo == null) {
1094 return;
1095 }
1096 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001097 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001098 ? index + 1 : index;
1099 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
Chuck Liao369e0822021-07-01 00:09:22 +08001100 if (holder != null) {
1101 mAdapter.showBadge(holder, R.drawable.wallpaper_check_circle_24dp, isApplied);
1102 } else {
“Chuckffd832c2020-03-22 02:15:58 +08001103 // Item is not visible, make sure the item is re-bound when it becomes visible.
1104 mAdapter.notifyItemChanged(index);
1105 }
1106 }
1107
Chuck Liao11f4a762020-04-08 13:24:43 +08001108 private void refreshAppliedWallpaper() {
1109 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1110 showCheckMarkAndBorderForAppliedWallpaper(false);
1111
1112 // Update to the new applied wallpaper.
1113 String appliedWallpaperId = getAppliedWallpaperId();
1114 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1115 .stream()
1116 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1117 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1118 .findFirst();
1119 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1120
1121 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1122 showCheckMarkAndBorderForAppliedWallpaper(true);
1123 }
1124
1125 private String getAppliedWallpaperId() {
1126 WallpaperPreferences prefs =
1127 InjectorProvider.getInjector().getPreferences(getContext());
1128 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1129 boolean isDestinationBoth =
1130 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1131
1132 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1133 return wallpaperInfo != null
1134 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1135 } else {
1136 return prefs.getLockWallpaperRemoteId();
1137 }
1138 }
1139
Chuck Liaoec053b02021-04-24 01:30:50 +08001140 private Set<String> getAppliedWallpaperIds() {
1141 WallpaperPreferences prefs =
1142 InjectorProvider.getInjector().getPreferences(getContext());
1143 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1144 Set<String> appliedWallpaperIds = new ArraySet<>();
1145
1146 String homeWallpaperId = wallpaperInfo != null ? wallpaperInfo.getServiceName()
1147 : prefs.getHomeWallpaperRemoteId();
1148 if (!TextUtils.isEmpty(homeWallpaperId)) {
1149 appliedWallpaperIds.add(homeWallpaperId);
1150 }
1151
1152 boolean isLockWallpaperApplied =
1153 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) >= 0;
1154 String lockWallpaperId = prefs.getLockWallpaperRemoteId();
1155 if (isLockWallpaperApplied && !TextUtils.isEmpty(lockWallpaperId)) {
1156 appliedWallpaperIds.add(lockWallpaperId);
1157 }
1158
1159 return appliedWallpaperIds;
1160 }
1161
Chuck Liao11f4a762020-04-08 13:24:43 +08001162 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1163 updateAppliedStatus(mAppliedWallpaperInfo, show);
1164 if (mSelectedWallpaperInfo == null) {
1165 updateActivatedStatus(mAppliedWallpaperInfo, show);
1166 }
1167 }
1168
Ching-Sung Lib69e9932021-05-25 20:03:01 +08001169 boolean shouldShowRotationTile() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001170 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001171 }
1172
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001173 class EmptySelectionAnimator implements SelectionAnimator{
1174 EmptySelectionAnimator() {}
1175
1176 public boolean isSelected() {
1177 return false;
1178 }
1179
1180 /**
1181 * Sets the UI to selected immediately with no animation.
1182 */
1183 public void selectImmediately() {}
1184
1185 /**
1186 * Sets the UI to deselected immediately with no animation.
1187 */
1188 public void deselectImmediately() {}
1189
1190 /**
1191 * Sets the UI to selected with a smooth animation.
1192 */
1193 public void animateSelected() {}
1194
1195 /**
1196 * Sets the UI to deselected with a smooth animation.
1197 */
1198 public void animateDeselected() {}
1199
1200 /**
1201 * Sets the UI to show a loading indicator.
1202 */
1203 public void showLoading() {}
1204
1205 /**
1206 * Sets the UI to hide the loading indicator.
1207 */
1208 public void showNotLoading() {}
1209
1210 }
1211
Jon Miranda16ea1b12017-12-12 14:52:48 -08001212 /**
1213 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1214 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001215 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1216 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1217 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1218 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001219
1220 private final List<WallpaperInfo> mWallpapers;
1221
1222 private int mPendingSelectedAdapterPosition;
1223 private int mSelectedAdapterPosition;
1224
1225 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1226 mWallpapers = wallpapers;
1227 mPendingSelectedAdapterPosition = -1;
1228 mSelectedAdapterPosition = -1;
1229 }
1230
1231 @Override
1232 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1233 switch (viewType) {
1234 case ITEM_VIEW_TYPE_ROTATION:
1235 return createRotationHolder(parent);
1236 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1237 return createIndividualHolder(parent);
1238 case ITEM_VIEW_TYPE_MY_PHOTOS:
1239 return createMyPhotosHolder(parent);
1240 default:
1241 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1242 return null;
1243 }
1244 }
1245
1246 @Override
1247 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001248 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001249 return ITEM_VIEW_TYPE_ROTATION;
1250 }
1251
1252 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1253 if (mCategory.supportsCustomPhotos()
1254 && !isRotationEnabled()
1255 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1256 return ITEM_VIEW_TYPE_MY_PHOTOS;
1257 }
1258
1259 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1260 }
1261
1262 @Override
1263 public void onBindViewHolder(ViewHolder holder, int position) {
1264 int viewType = getItemViewType(position);
1265
1266 switch (viewType) {
1267 case ITEM_VIEW_TYPE_ROTATION:
1268 onBindRotationHolder(holder, position);
1269 break;
1270 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1271 onBindIndividualHolder(holder, position);
1272 break;
1273 case ITEM_VIEW_TYPE_MY_PHOTOS:
1274 ((MyPhotosViewHolder) holder).bind();
1275 break;
1276 default:
1277 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1278 }
1279 }
1280
1281 @Override
1282 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001283 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001284 ? mWallpapers.size() + 1
1285 : mWallpapers.size();
1286 }
1287
1288 private ViewHolder createRotationHolder(ViewGroup parent) {
1289 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001290 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001291 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Chuck Liao5a4243b2020-05-20 23:56:39 +08001292 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1293 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001294 }
1295
1296 private ViewHolder createIndividualHolder(ViewGroup parent) {
1297 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1298 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1299
1300 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001301 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001302 return new SetIndividualHolder(
1303 getActivity(), mTileSizePx.y, view,
1304 selectionAnimator,
1305 new OnSetListener() {
1306 @Override
1307 public void onPendingWallpaperSet(int adapterPosition) {
1308 // Deselect and hide loading indicator for any previously pending tile.
1309 if (mPendingSelectedAdapterPosition != -1) {
1310 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1311 mPendingSelectedAdapterPosition);
1312 if (oldViewHolder instanceof SelectableHolder) {
1313 ((SelectableHolder) oldViewHolder).setSelectionState(
1314 SelectableHolder.SELECTION_STATE_DESELECTED);
1315 }
1316 }
1317
1318 if (mSelectedAdapterPosition != -1) {
1319 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1320 mSelectedAdapterPosition);
1321 if (oldViewHolder instanceof SelectableHolder) {
1322 ((SelectableHolder) oldViewHolder).setSelectionState(
1323 SelectableHolder.SELECTION_STATE_DESELECTED);
1324 }
1325 }
1326
1327 mPendingSelectedAdapterPosition = adapterPosition;
1328 }
1329
1330 @Override
1331 public void onWallpaperSet(int adapterPosition) {
1332 // No-op -- UI handles a new wallpaper being set by reacting to the
1333 // WallpaperChangedNotifier.
1334 }
1335
1336 @Override
1337 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1338 showSetWallpaperErrorDialog();
1339 mPendingSetIndividualHolder = holder;
1340 }
1341 });
1342 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001343 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001344 }
1345 }
1346
1347 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1348 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1349 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1350
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001351 return new MyPhotosViewHolder(getActivity(),
1352 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1353 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001354 }
1355
1356 /**
1357 * Marks the tile at the given position as selected with a visual indication. Also updates the
1358 * "currently selected" BottomSheet to reflect the newly selected tile.
1359 */
1360 private void updateSelectedTile(int newlySelectedPosition) {
1361 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1362 // succession.
1363 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1364 return;
1365 }
1366
1367 if (mCurrentWallpaperBottomSheetPresenter != null) {
1368 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1369 IndividualPickerFragment.this);
1370
1371 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1372 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1373 }
1374 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1375 @Override
1376 public void run() {
1377 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1378 }
1379 };
1380 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1381 }
1382
1383 // User may have switched to another category, thus detaching this fragment, so check here.
1384 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1385 // still occurs in the UI after the user selects that other category.
1386 if (getActivity() == null) {
1387 return;
1388 }
1389
1390 // Update the newly selected wallpaper ViewHolder and the old one so that if
1391 // selection UI state applies (desktop UI), it is updated.
1392 if (mSelectedAdapterPosition >= 0) {
1393 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1394 mSelectedAdapterPosition);
1395 if (oldViewHolder instanceof SelectableHolder) {
1396 ((SelectableHolder) oldViewHolder).setSelectionState(
1397 SelectableHolder.SELECTION_STATE_DESELECTED);
1398 }
1399 }
1400
1401 // Animate selection of newly selected tile.
1402 ViewHolder newViewHolder = mImageGrid
1403 .findViewHolderForAdapterPosition(newlySelectedPosition);
1404 if (newViewHolder instanceof SelectableHolder) {
1405 ((SelectableHolder) newViewHolder).setSelectionState(
1406 SelectableHolder.SELECTION_STATE_SELECTED);
1407 }
1408
1409 mSelectedAdapterPosition = newlySelectedPosition;
1410
1411 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1412 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1413 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1414 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1415 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1416 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1417
1418 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1419 }
1420
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001421 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001422 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1423 String collectionId = mCategory.getCollectionId();
1424 ((DesktopRotationHolder) holder).bind(collectionId);
1425
1426 if (mWallpaperPreferences.getWallpaperPresentationMode()
1427 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1428 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1429 mSelectedAdapterPosition = position;
1430 }
1431
1432 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1433 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1434 mWasUpdateRunnableRun = true;
1435 }
1436 }
1437 }
1438
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001439 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001440 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001441 ? position - 1 : position;
1442 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -07001443 wallpaper.computePlaceholderColor(holder.itemView.getContext());
Jon Miranda16ea1b12017-12-12 14:52:48 -08001444 ((IndividualHolder) holder).bindWallpaper(wallpaper);
Chuck Liao369e0822021-07-01 00:09:22 +08001445 boolean isWallpaperApplied = isWallpaperApplied(wallpaper);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001446
“Chuckffd832c2020-03-22 02:15:58 +08001447 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001448 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001449 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001450 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001451
Ching-Sung Li1ac024f2021-06-08 14:43:28 +08001452 CardView container = holder.itemView.findViewById(R.id.wallpaper_container);
1453 int radiusId = isFewerColumnLayout() ? R.dimen.grid_item_all_radius
1454 : R.dimen.grid_item_all_radius_small;
1455 container.setRadius(getResources().getDimension(radiusId));
Chuck Liao369e0822021-07-01 00:09:22 +08001456 showBadge(holder, R.drawable.wallpaper_check_circle_24dp, isWallpaperApplied);
1457 }
1458
1459 protected boolean isWallpaperApplied(WallpaperInfo wallpaper) {
1460 return mAppliedWallpaperIds.contains(wallpaper.getWallpaperId());
1461 }
1462
1463 protected void showBadge(ViewHolder holder, @DrawableRes int icon, boolean show) {
1464 ImageView badge = holder.itemView.findViewById(R.id.indicator_icon);
1465 if (show) {
1466 final float margin = isFewerColumnLayout() ? getResources().getDimension(
1467 R.dimen.grid_item_badge_margin) : getResources().getDimension(
1468 R.dimen.grid_item_badge_margin_small);
1469 final RelativeLayout.LayoutParams layoutParams =
1470 (RelativeLayout.LayoutParams) badge.getLayoutParams();
1471 layoutParams.setMargins(/* left= */ (int) margin, /* top= */ (int) margin,
1472 /* right= */ (int) margin, /* bottom= */ (int) margin);
1473 badge.setLayoutParams(layoutParams);
1474 badge.setBackgroundResource(icon);
1475 badge.setVisibility(View.VISIBLE);
1476 } else {
1477 badge.setVisibility(View.GONE);
1478 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001479 }
1480 }
Chuck Liaoba401232020-03-13 20:11:04 +08001481
1482 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1483
Chuck Liao1e6cb682021-04-22 00:51:11 +08001484 private final int mPaddingHorizontal;
1485 private final int mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001486
Chuck Liao1e6cb682021-04-22 00:51:11 +08001487 GridPaddingDecoration(int paddingHorizontal, int paddingBottom) {
1488 mPaddingHorizontal = paddingHorizontal;
1489 mPaddingBottom = paddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001490 }
1491
1492 @Override
1493 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1494 RecyclerView.State state) {
1495 int position = parent.getChildAdapterPosition(view);
1496 if (position >= 0) {
Chuck Liao1e6cb682021-04-22 00:51:11 +08001497 outRect.left = mPaddingHorizontal;
1498 outRect.right = mPaddingHorizontal;
1499 outRect.bottom = mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001500 }
1501 }
1502 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001503}