blob: 09d4c93d0d7f13a7b1c081e294be5ef382c920d9 [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;
Ching-Sung Li1ac024f2021-06-08 14:43:28 +080045import androidx.cardview.widget.CardView;
Chuck Liaof6b4b192020-08-07 02:31:32 +080046import androidx.core.widget.ContentLoadingProgressBar;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080047import androidx.fragment.app.DialogFragment;
Chuck Liao7e333722021-02-27 02:21:20 +080048import androidx.fragment.app.Fragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080049import androidx.recyclerview.widget.GridLayoutManager;
50import androidx.recyclerview.widget.RecyclerView;
51import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
52import androidx.recyclerview.widget.RecyclerView.ViewHolder;
53
Jon Miranda16ea1b12017-12-12 14:52:48 -080054import com.android.wallpaper.R;
55import com.android.wallpaper.asset.Asset;
56import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070057import com.android.wallpaper.model.Category;
58import com.android.wallpaper.model.CategoryProvider;
59import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080060import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080061import com.android.wallpaper.model.WallpaperCategory;
62import com.android.wallpaper.model.WallpaperInfo;
63import com.android.wallpaper.model.WallpaperReceiver;
64import com.android.wallpaper.model.WallpaperRotationInitializer;
65import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
66import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080067import com.android.wallpaper.module.FormFactorChecker;
68import com.android.wallpaper.module.FormFactorChecker.FormFactor;
69import com.android.wallpaper.module.Injector;
70import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070071import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080072import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080073import com.android.wallpaper.module.WallpaperChangedNotifier;
74import com.android.wallpaper.module.WallpaperPersister;
75import com.android.wallpaper.module.WallpaperPersister.Destination;
76import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080077import com.android.wallpaper.module.WallpaperSetter;
Chuck Liao58aca1c2021-03-17 01:20:55 +080078import com.android.wallpaper.picker.AppbarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080079import com.android.wallpaper.picker.BaseActivity;
80import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Chuck Liaob17d1502020-09-07 21:55:30 +080081import com.android.wallpaper.picker.FragmentTransactionChecker;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080082import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -080083import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080084import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080085import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
86import com.android.wallpaper.picker.StartRotationDialogFragment;
87import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080088import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080089import com.android.wallpaper.picker.WallpapersUiContainer;
90import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
91import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070092import com.android.wallpaper.util.SizeCalculator;
chihhangchuang1a29e752020-04-28 18:22:53 +080093import com.android.wallpaper.widget.WallpaperInfoView;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080094import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
95import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070096
Jon Miranda16ea1b12017-12-12 14:52:48 -080097import com.bumptech.glide.Glide;
98import com.bumptech.glide.MemoryCategory;
99
100import java.util.ArrayList;
101import java.util.Date;
102import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800103import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800104import java.util.Random;
Chuck Liaoec053b02021-04-24 01:30:50 +0800105import java.util.Set;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800106
107/**
108 * Displays the Main UI for picking an individual wallpaper image.
109 */
Chuck Liao58aca1c2021-03-17 01:20:55 +0800110public class IndividualPickerFragment extends AppbarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800111 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
112 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800113 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
114 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800115
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800116 /**
117 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
118 * such as "my photos" or "daily rotation".
119 */
120 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
121 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
122
Chuck Liao6db3eba2021-06-16 23:50:45 +0800123 protected static final int MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT = 8;
124
Jon Miranda16ea1b12017-12-12 14:52:48 -0800125 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800126 private static final int UNUSED_REQUEST_CODE = 1;
127 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
128 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
129 private static final String PROGRESS_DIALOG_NO_TITLE = null;
130 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
131 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
132 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700133 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800134
Chuck Liao8ec38e02020-02-26 20:59:32 +0800135 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800136 * An interface for updating the thumbnail with the specific wallpaper.
137 */
138 public interface ThumbnailUpdater {
139 /**
140 * Updates the thumbnail with the specific wallpaper.
141 */
142 void updateThumbnail(WallpaperInfo wallpaperInfo);
143
144 /**
145 * Restores to the thumbnails of the wallpapers which were applied.
146 */
147 void restoreThumbnails();
148 }
149
“Chuck7ef99722020-03-22 04:34:03 +0800150 /**
151 * An interface for receiving the destination of the new applied wallpaper.
152 */
153 public interface WallpaperDestinationCallback {
154 /**
155 * Called when the destination of the wallpaper is set.
156 *
157 * @param destination the destination which a wallpaper may be set.
158 * See {@link Destination} for more details.
159 */
160 void onDestinationSet(@Destination int destination);
161 }
162
Chuck Liaob3829fb2020-04-01 00:47:50 +0800163 /**
164 * The listener which will be notified when the wallpaper is selected.
165 */
166 public interface WallpaperSelectedListener {
167 /**
168 * Called when the wallpaper is selected.
169 *
170 * @param position the position of the selected wallpaper
171 */
172 void onWallpaperSelected(int position);
173 }
174
Chuck Liaof6b4b192020-08-07 02:31:32 +0800175 /**
Chuck Liao7e333722021-02-27 02:21:20 +0800176 * Interface to be implemented by a Fragment(or an Activity) hosting
177 * a {@link IndividualPickerFragment}.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800178 */
179 public interface IndividualPickerFragmentHost {
180 /**
Chuck Liao58aca1c2021-03-17 01:20:55 +0800181 * Indicates if the host has toolbar to show the title. If it does, we should set the title
182 * there.
183 */
184 boolean isHostToolbarShown();
185
186 /**
187 * Sets the title in the host's toolbar.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800188 */
189 void setToolbarTitle(CharSequence title);
190
191 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800192 * Configures the menu in the toolbar.
193 *
194 * @param menuResId the resource id of the menu
195 */
196 void setToolbarMenu(@MenuRes int menuResId);
197
198 /**
199 * Removes the menu in the toolbar.
200 */
201 void removeToolbarMenu();
202
203 /**
Chuck Liaof6b4b192020-08-07 02:31:32 +0800204 * Moves to the previous fragment.
205 */
206 void moveToPreviousFragment();
207 }
208
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800209 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800210 WallpaperPreferences mWallpaperPreferences;
211 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800212 RecyclerView mImageGrid;
213 IndividualAdapter mAdapter;
214 WallpaperCategory mCategory;
215 WallpaperRotationInitializer mWallpaperRotationInitializer;
216 List<WallpaperInfo> mWallpapers;
217 Point mTileSizePx;
218 WallpapersUiContainer mWallpapersUiContainer;
219 @FormFactor
220 int mFormFactor;
221 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800222
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800223 Handler mHandler;
224 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800225 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800226
227 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
228 new WallpaperChangedNotifier.Listener() {
229 @Override
230 public void onWallpaperChanged() {
231 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
232 return;
233 }
234
235 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
236 mAdapter.mSelectedAdapterPosition);
237
238 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
239 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
240 if (selectedViewHolder instanceof SelectableHolder) {
241 ((SelectableHolder) selectedViewHolder).setSelectionState(
242 SelectableHolder.SELECTION_STATE_DESELECTED);
243 }
244 } else {
245 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
246 }
247 }
248 };
249 PackageStatusNotifier.Listener mAppStatusListener;
chihhangchuang1a29e752020-04-28 18:22:53 +0800250 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800251 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800252
chihhangchuangc687d912020-05-04 14:33:05 +0800253 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800254 private ProgressDialog mProgressDialog;
255 private boolean mTestingMode;
256 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800257 private SetIndividualHolder mPendingSetIndividualHolder;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800258 private ContentLoadingProgressBar mLoading;
Chuck Liaoddf2b522021-04-15 00:36:25 +0800259 private CategoryProvider mCategoryProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800260
261 /**
262 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
263 * committing fragment transactions.
264 */
265 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
266 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
267
Jon Miranda16ea1b12017-12-12 14:52:48 -0800268 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800269
270 /**
271 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
272 * invocation of the fragment.
273 */
274 private boolean mWasUpdateRunnableRun;
275
276 /**
277 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
278 * mode.
279 */
280 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
281 @Override
282 public void run() {
283 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
284 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
285 if (viewHolder instanceof DesktopRotationHolder) {
286 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
287 } else { // viewHolder is null
288 // If the rotation tile is unavailable (because user has scrolled down, causing the
289 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
290 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
291 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
292 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
293 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
294 }
295 }
296 };
297
Chuck Liao69630f12020-03-05 19:01:25 +0800298 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800299 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800300 private WallpaperManager mWallpaperManager;
301 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800302 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liaoec053b02021-04-24 01:30:50 +0800303 private Set<String> mAppliedWallpaperIds;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800304
Jon Miranda16ea1b12017-12-12 14:52:48 -0800305 public static IndividualPickerFragment newInstance(String collectionId) {
306 Bundle args = new Bundle();
307 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
308
309 IndividualPickerFragment fragment = new IndividualPickerFragment();
310 fragment.setArguments(args);
311 return fragment;
312 }
313
“Chuck7ef99722020-03-22 04:34:03 +0800314 /**
315 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
316 * would be set.
317 *
318 * @param wallpaperDestination the destination a wallpaper would be set.
319 * It will be either {@link Destination#DEST_HOME_SCREEN}
320 * or {@link Destination#DEST_LOCK_SCREEN}.
321 */
322 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
323 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800324 if (mWallpapers != null) {
325 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800326 }
327 }
328
Jon Miranda16ea1b12017-12-12 14:52:48 -0800329 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
330 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
331 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
332 getActivity());
333 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
334 @Override
335 public void onDrawableLoaded() {
336 if (getActivity() == null) {
337 return;
338 }
339
340 // Schedule the next update of the thumbnail.
341 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
342 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
343 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
344 }
345 });
346 }
347
348 @Override
349 public void onCreate(Bundle savedInstanceState) {
350 super.onCreate(savedInstanceState);
351
352 Injector injector = InjectorProvider.getInjector();
353 Context appContext = getContext().getApplicationContext();
354 mWallpaperPreferences = injector.getPreferences(appContext);
355
356 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
357 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
358
“Chuck7ef99722020-03-22 04:34:03 +0800359 mWallpaperManager = WallpaperManager.getInstance(appContext);
360
Jon Miranda16ea1b12017-12-12 14:52:48 -0800361 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
362
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700363 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
364
chihhangchuangc687d912020-05-04 14:33:05 +0800365 mUserEventLogger = injector.getUserEventLogger(appContext);
366
“Chuckffd832c2020-03-22 02:15:58 +0800367 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800368 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800369 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800370 injector.getPreferences(appContext),
371 injector.getUserEventLogger(appContext),
372 false);
373
Jon Miranda16ea1b12017-12-12 14:52:48 -0800374 mWallpapers = new ArrayList<>();
375 mRandom = new Random();
376 mHandler = new Handler();
377
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700378 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
379 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
380 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
381 Glide.get(getContext()).clearMemory();
382 }
383
Chuck Liaoddf2b522021-04-15 00:36:25 +0800384 mCategoryProvider = injector.getCategoryProvider(appContext);
385 mCategoryProvider.fetchCategories(new CategoryReceiver() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700386 @Override
387 public void onCategoryReceived(Category category) {
388 // Do nothing.
389 }
390
391 @Override
392 public void doneFetchingCategories() {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800393 Category category = mCategoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700394 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800395 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800396 return;
397 }
398 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700399 if (mCategory == null) {
400 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
401
402 // The absence of this category in the CategoryProvider indicates a broken
403 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800404 getIndividualPickerFragmentHost().moveToPreviousFragment();
405 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
406 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700407 return;
408 }
409 onCategoryLoaded();
410 }
411 }, false);
412 }
413
Chuck Liaof6b4b192020-08-07 02:31:32 +0800414
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700415 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800416 if (getIndividualPickerFragmentHost() == null) {
417 return;
418 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800419 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
420 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
421 } else {
422 setTitle(mCategory.getTitle());
423 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700424 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao33f1af42021-04-13 20:52:35 +0800425 if (mToolbar != null && isRotationEnabled()) {
426 setUpToolbarMenu(R.menu.individual_picker_menu);
427 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700428 fetchWallpapers(false);
429
430 if (mCategory.supportsThirdParty()) {
431 mAppStatusListener = (packageName, status) -> {
432 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
433 mCategory.containsThirdParty(packageName)) {
434 fetchWallpapers(true);
435 }
436 };
437 mPackageStatusNotifier.addListener(mAppStatusListener,
438 WallpaperService.SERVICE_INTERFACE);
439 }
440 }
441
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800442 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700443 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800444 mIsWallpapersReceived = false;
445 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800446 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
447 @Override
448 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800449 mIsWallpapersReceived = true;
450 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800451 for (WallpaperInfo wallpaper : wallpapers) {
452 mWallpapers.add(wallpaper);
453 }
Chuck Liaob10d2a32021-04-27 20:59:53 +0800454 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800455
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700456 // Wallpapers may load after the adapter is initialized, in which case we have
457 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800458 if (mAdapter != null) {
459 mAdapter.notifyDataSetChanged();
460 }
461
462 if (mWallpapersUiContainer != null) {
463 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700464 } else {
465 if (wallpapers.isEmpty()) {
466 // If there are no more wallpapers and we're on phone, just finish the
467 // Activity.
468 Activity activity = getActivity();
469 if (activity != null
470 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
471 activity.finish();
472 }
473 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800474 }
475 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700476 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800477 }
478
Chuck Liaof6b4b192020-08-07 02:31:32 +0800479 void updateLoading() {
480 if (mLoading == null) {
481 return;
482 }
483
484 if (mIsWallpapersReceived) {
485 mLoading.hide();
486 } else {
487 mLoading.show();
488 }
489 }
490
Jon Miranda16ea1b12017-12-12 14:52:48 -0800491 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700492 public void onSaveInstanceState(@NonNull Bundle outState) {
493 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700494 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700495 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
496 }
497
498 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800499 public View onCreateView(LayoutInflater inflater, ViewGroup container,
500 Bundle savedInstanceState) {
501 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
Chuck Liao58aca1c2021-03-17 01:20:55 +0800502 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
503 view.findViewById(R.id.header_bar).setVisibility(View.GONE);
Kunhung Lid0174362021-04-05 15:31:41 +0800504 setUpArrowEnabled(/* upArrow= */ true);
Chuck Liaoa7215812021-04-13 22:33:43 +0800505 if (isRotationEnabled()) {
506 getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu);
507 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800508 } else {
509 setUpToolbar(view);
Chuck Liao33f1af42021-04-13 20:52:35 +0800510 if (isRotationEnabled()) {
511 setUpToolbarMenu(R.menu.individual_picker_menu);
512 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800513 if (mCategory != null) {
514 setTitle(mCategory.getTitle());
515 }
516 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800517
Chuck Liaoec053b02021-04-24 01:30:50 +0800518 mAppliedWallpaperIds = getAppliedWallpaperIds();
519
Jon Miranda16ea1b12017-12-12 14:52:48 -0800520 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
521 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
522 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
523 updateImageGridPadding(false /* addExtraBottomSpace */);
524 mImageGrid.setScrollBarSize(gridPaddingPx);
525 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800526 mLoading = view.findViewById(R.id.loading_indicator);
527 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700528 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800529 setUpBottomSheet();
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800530 // For nav bar edge-to-edge effect.
Kunhung Li1b0cb472021-04-26 20:52:04 +0800531 view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
532 // For status bar height.
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800533 v.setPadding(
534 v.getPaddingLeft(),
Kunhung Li1b0cb472021-04-26 20:52:04 +0800535 windowInsets.getSystemWindowInsetTop(),
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800536 v.getPaddingRight(),
Kunhung Li1b0cb472021-04-26 20:52:04 +0800537 v.getPaddingBottom());
538
539 View gridView = v.findViewById(R.id.wallpaper_grid);
540 gridView.setPadding(
541 gridView.getPaddingLeft(),
542 gridView.getPaddingTop(),
543 gridView.getPaddingRight(),
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800544 windowInsets.getSystemWindowInsetBottom());
545 return windowInsets.consumeSystemWindowInsets();
546 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800547 return view;
548 }
549
550 @Override
551 public void onClickTryAgain(@Destination int unused) {
552 if (mPendingSetIndividualHolder != null) {
553 mPendingSetIndividualHolder.setWallpaper();
554 }
555 }
556
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800557 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800558 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
559 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
560 R.dimen.current_wallpaper_bottom_sheet_layout_height);
561 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
562 // Only left and top may be set in order for the GridMarginDecoration to work properly.
563 mImageGrid.setPadding(
564 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
565 }
566
Chuck Liaof6b4b192020-08-07 02:31:32 +0800567 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
Chuck Liao7e333722021-02-27 02:21:20 +0800568 Fragment parentFragment = getParentFragment();
569 if (parentFragment != null) {
570 return (IndividualPickerFragmentHost) parentFragment;
571 } else {
572 return (IndividualPickerFragmentHost) getActivity();
573 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800574 }
575
Chuck Liaob10d2a32021-04-27 20:59:53 +0800576 protected void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700577 // Skip if mImageGrid been initialized yet
578 if (mImageGrid == null) {
579 return;
580 }
581 // Skip if category hasn't loaded yet
582 if (mCategory == null) {
583 return;
584 }
Chuck Liao658ee892021-06-30 10:17:12 +0800585 if (getContext() == null) {
586 return;
587 }
Chuck Liao6db3eba2021-06-16 23:50:45 +0800588
589 // Wallpaper count could change, so we may need to change the layout(2 or 3 columns layout)
590 GridLayoutManager gridLayoutManager = (GridLayoutManager) mImageGrid.getLayoutManager();
591 boolean needUpdateLayout =
592 gridLayoutManager != null && gridLayoutManager.getSpanCount() != getNumColumns();
593
594 // Skip if the adapter was already created and don't need to change the layout
595 if (mAdapter != null && !needUpdateLayout) {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700596 return;
597 }
Chuck Liao6db3eba2021-06-16 23:50:45 +0800598
599 // Clear the old decoration
600 int decorationCount = mImageGrid.getItemDecorationCount();
601 for (int i = 0; i < decorationCount; i++) {
602 mImageGrid.removeItemDecorationAt(i);
603 }
604
Chuck Liao1e6cb682021-04-22 00:51:11 +0800605 mImageGrid.addItemDecoration(new GridPaddingDecoration(getGridItemPaddingHorizontal(),
606 getGridItemPaddingBottom()));
607 int edgePadding = getEdgePadding();
608 mImageGrid.setPadding(edgePadding, mImageGrid.getPaddingTop(), edgePadding,
609 mImageGrid.getPaddingBottom());
Chuck Liaob10d2a32021-04-27 20:59:53 +0800610 mTileSizePx = isFewerColumnLayout()
Chuck Liaoddf2b522021-04-15 00:36:25 +0800611 ? SizeCalculator.getFeaturedIndividualTileSize(getActivity())
612 : SizeCalculator.getIndividualTileSize(getActivity());
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700613 setUpImageGrid();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800614 mImageGrid.setAccessibilityDelegateCompat(
615 new WallpaperPickerRecyclerViewAccessibilityDelegate(
616 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700617 }
618
Ching-Sung Lib69e9932021-05-25 20:03:01 +0800619 boolean isFewerColumnLayout() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800620 return mWallpapers != null && mWallpapers.size() <= MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT;
621 }
622
Chuck Liao1e6cb682021-04-22 00:51:11 +0800623 private int getGridItemPaddingHorizontal() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800624 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800625 ? getResources().getDimensionPixelSize(
626 R.dimen.grid_item_featured_individual_padding_horizontal)
627 : getResources().getDimensionPixelSize(
628 R.dimen.grid_item_individual_padding_horizontal);
629 }
630
631 private int getGridItemPaddingBottom() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800632 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800633 ? getResources().getDimensionPixelSize(
634 R.dimen.grid_item_featured_individual_padding_bottom)
635 : getResources().getDimensionPixelSize(R.dimen.grid_item_individual_padding_bottom);
636 }
637
638 private int getEdgePadding() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800639 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800640 ? getResources().getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space)
641 : getResources().getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space);
642 }
643
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700644 /**
645 * Create the adapter and assign it to mImageGrid.
646 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
647 */
648 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800649 mAdapter = new IndividualAdapter(mWallpapers);
650 mImageGrid.setAdapter(mAdapter);
651 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
652 }
653
Jon Miranda16ea1b12017-12-12 14:52:48 -0800654 /**
655 * Enables and populates the "Currently set" wallpaper BottomSheet.
656 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800657 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800658 mImageGrid.addOnScrollListener(new OnScrollListener() {
659 @Override
660 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
661 if (mCurrentWallpaperBottomSheetPresenter == null) {
662 return;
663 }
664
665 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
666 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
667 }
668 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
669 @Override
670 public void run() {
671 if (dy > 0) {
672 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
673 } else {
674 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
675 }
676 }
677 };
678 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
679 }
680 });
681 }
682
chihhangchuang3efb6832020-04-17 02:06:25 +0800683 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800684 public void onResume() {
685 super.onResume();
686
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700687 WallpaperPreferences preferences = InjectorProvider.getInjector()
688 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800689 preferences.setLastAppActiveTimestamp(new Date().getTime());
690
691 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
692 // PreviewFragment.
693 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
694
695 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
696 // shown earlier when this fragment's hosting activity didn't allow committing fragment
697 // transactions.
698 if (mStagedStartRotationErrorDialogFragment != null) {
699 mStagedStartRotationErrorDialogFragment.show(
700 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
701 mStagedStartRotationErrorDialogFragment = null;
702 }
703
704 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
705 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
706 // committing fragment transactions.
707 if (mStagedSetWallpaperErrorDialogFragment != null) {
708 mStagedSetWallpaperErrorDialogFragment.show(
709 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
710 mStagedSetWallpaperErrorDialogFragment = null;
711 }
712
Chuck Liao5a4243b2020-05-20 23:56:39 +0800713 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
714 // Must be resuming from a previously stopped state, so re-schedule the update of the
715 // daily wallpapers tile thumbnail.
716 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800717 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800718 }
719
720 @Override
721 public void onStop() {
722 super.onStop();
723 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
724 }
725
726 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800727 public void onDestroyView() {
728 super.onDestroyView();
729 getIndividualPickerFragmentHost().removeToolbarMenu();
730 }
731
732 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800733 public void onDestroy() {
734 super.onDestroy();
735 if (mProgressDialog != null) {
736 mProgressDialog.dismiss();
737 }
738 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700739 if (mAppStatusListener != null) {
740 mPackageStatusNotifier.removeListener(mAppStatusListener);
741 }
Chuck Liao69630f12020-03-05 19:01:25 +0800742 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800743 }
744
745 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800746 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800747 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
748 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
749 // still holds a reference to the destroyed Fragment and is calling
750 // onStartRotationDialogDismissed on that destroyed Fragment.
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800751 }
752
753 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800754 public void retryStartRotation(@NetworkPreference int networkPreference) {
755 startRotation(networkPreference);
756 }
757
758 public void setCurrentWallpaperBottomSheetPresenter(
759 CurrentWallpaperBottomSheetPresenter presenter) {
760 mCurrentWallpaperBottomSheetPresenter = presenter;
761 }
762
763 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
764 mWallpapersUiContainer = uiContainer;
765 }
766
Chuck Liaob3829fb2020-04-01 00:47:50 +0800767 public void setOnWallpaperSelectedListener(
768 WallpaperSelectedListener wallpaperSelectedListener) {
769 mWallpaperSelectedListener = wallpaperSelectedListener;
770 }
771
772 /**
773 * Resizes the layout's height.
774 */
775 public void resizeLayout(int height) {
776 mImageGrid.getLayoutParams().height = height;
777 mImageGrid.requestLayout();
778 }
779
780 /**
781 * Scrolls to the specific item.
782 *
783 * @param position the position of the item
784 */
785 public void scrollToPosition(int position) {
786 ((GridLayoutManager) mImageGrid.getLayoutManager())
787 .scrollToPositionWithOffset(position, /* offset= */ 0);
788 }
789
Jon Miranda16ea1b12017-12-12 14:52:48 -0800790 /**
791 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
792 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
793 * constantly keeps the UI thread alive and blocks a test forever.
794 *
795 * @param testingMode
796 */
797 void setTestingMode(boolean testingMode) {
798 mTestingMode = testingMode;
799 }
800
Jon Miranda16ea1b12017-12-12 14:52:48 -0800801 @Override
802 public void startRotation(@NetworkPreference final int networkPreference) {
803 if (!isRotationEnabled()) {
804 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
805 return;
806 }
807
808 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
809 // causes Espresso to hang once the dialog is shown.
810 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
811 int themeResId;
812 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
813 themeResId = R.style.ProgressDialogThemePreL;
814 } else {
815 themeResId = R.style.LightDialogTheme;
816 }
817 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
818
819 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
820 mProgressDialog.setMessage(
821 getResources().getString(R.string.start_rotation_progress_message));
822 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
823 mProgressDialog.show();
824 }
825
826 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
827 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
828 }
829
830 final Context appContext = getActivity().getApplicationContext();
831
832 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
833 appContext,
834 networkPreference,
835 new Listener() {
836 @Override
837 public void onFirstWallpaperInRotationSet() {
838 if (mProgressDialog != null) {
839 mProgressDialog.dismiss();
840 }
841
842 // The fragment may be detached from its containing activity if the user exits the
843 // app before the first wallpaper image in rotation finishes downloading.
844 Activity activity = getActivity();
845
Jon Miranda16ea1b12017-12-12 14:52:48 -0800846
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700847 if (mWallpaperRotationInitializer.startRotation(appContext)) {
848 if (activity != null
849 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
850 try {
851 Toast.makeText(getActivity(),
852 R.string.wallpaper_set_successfully_message,
853 Toast.LENGTH_SHORT).show();
854 } catch (NotFoundException e) {
855 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800856 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800857
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700858 activity.setResult(Activity.RESULT_OK);
859 activity.finish();
860 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
861 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
862 }
863 } else { // Failed to start rotation.
864 showStartRotationErrorDialog(networkPreference);
865
866 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
867 DesktopRotationHolder rotationViewHolder =
868 (DesktopRotationHolder)
869 mImageGrid.findViewHolderForAdapterPosition(
870 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
871 rotationViewHolder.setSelectionState(
872 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800873 }
874 }
875 }
876
877 @Override
878 public void onError() {
879 if (mProgressDialog != null) {
880 mProgressDialog.dismiss();
881 }
882
883 showStartRotationErrorDialog(networkPreference);
884
885 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
886 DesktopRotationHolder rotationViewHolder =
887 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
888 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
889 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
890 }
891 }
892 });
893 }
894
895 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800896 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800897 if (activity != null) {
898 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
899 StartRotationErrorDialogFragment.newInstance(networkPreference);
900 startRotationErrorDialogFragment.setTargetFragment(
901 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
902
903 if (activity.isSafeToCommitFragmentTransaction()) {
904 startRotationErrorDialogFragment.show(
905 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
906 } else {
907 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
908 }
909 }
910 }
911
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800912 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800913 Activity activity = getActivity();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800914 if (activity == null) {
915 return 1;
916 }
Chuck Liaob10d2a32021-04-27 20:59:53 +0800917 return isFewerColumnLayout()
Chuck Liaoddf2b522021-04-15 00:36:25 +0800918 ? SizeCalculator.getNumFeaturedIndividualColumns(activity)
919 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800920 }
921
922 /**
923 * Returns whether rotation is enabled for this category.
924 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800925 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700926 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800927 }
928
929 @Override
930 public void onCurrentWallpaperRefreshed() {
931 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
932 }
933
Chuck Liao69630f12020-03-05 19:01:25 +0800934 @Override
935 public void onSet(int destination) {
936 if (mSelectedWallpaperInfo == null) {
937 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
938 return;
939 }
940
“Chuckffd832c2020-03-22 02:15:58 +0800941 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800942 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
943 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
Santiago Etchebeherec320c5f2021-05-14 17:35:57 -0700944 destination, 0, null, null, mSetWallpaperCallback);
Chuck Liao69630f12020-03-05 19:01:25 +0800945 } else {
946 mWallpaperSetter.setCurrentWallpaper(
947 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
948 }
“Chuck7ef99722020-03-22 04:34:03 +0800949 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800950 }
951
952 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
953 new WallpaperPersister.SetWallpaperCallback() {
954 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800955 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800956 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800957 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
958 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800959 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
960 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800961 }
962
963 @Override
964 public void onError(@Nullable Throwable throwable) {
965 Log.e(TAG, "Can't apply the wallpaper.");
Chuck Liao69630f12020-03-05 19:01:25 +0800966 }
967 };
968
969 @Override
Chuck Liao33f1af42021-04-13 20:52:35 +0800970 public boolean onMenuItemClick(MenuItem item) {
971 if (item.getItemId() == R.id.daily_rotation) {
Chuck Liaoa7215812021-04-13 22:33:43 +0800972 showRotationDialog();
Chuck Liao33f1af42021-04-13 20:52:35 +0800973 return true;
974 }
975 return super.onMenuItemClick(item);
976 }
977
Jon Miranda16ea1b12017-12-12 14:52:48 -0800978 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800979 * Popups a daily rotation dialog for the uses to confirm.
980 */
981 public void showRotationDialog() {
982 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
983 startRotationDialogFragment.setTargetFragment(
984 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
985 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
986 }
987
988 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800989 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
990 */
991 private void showSetWallpaperErrorDialog() {
992 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
993 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
994 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
995
996 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
997 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
998 } else {
999 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
1000 }
1001 }
1002
Chuck Liao69630f12020-03-05 19:01:25 +08001003 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +08001004 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +08001005 if (thumbnailUpdater == null) {
1006 return;
1007 }
1008
1009 if (selectedWallpaperInfo != null) {
1010 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
1011 } else {
1012 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +08001013 }
1014 }
1015
“Chuck7ef99722020-03-22 04:34:03 +08001016 private void onWallpaperDestinationSet(int destination) {
1017 WallpaperDestinationCallback wallpaperDestinationCallback =
1018 (WallpaperDestinationCallback) getParentFragment();
1019 if (wallpaperDestinationCallback == null) {
1020 return;
1021 }
1022
1023 wallpaperDestinationCallback.onDestinationSet(destination);
1024 }
1025
Ching-Sung Li073812b2020-04-07 21:19:21 +08001026 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +08001027 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001028 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +08001029 return;
1030 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001031 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +08001032 updateActivatedStatus(mSelectedWallpaperInfo == null
1033 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001034 // Update new selected wallpaper.
1035 updateActivatedStatus(newSelectedWallpaperInfo == null
1036 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
1037
1038 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001039 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +08001040 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +08001041 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +08001042 WallpaperInfoHelper.loadExploreIntent(
1043 getContext(),
1044 mSelectedWallpaperInfo,
1045 (actionLabel, exploreIntent) ->
1046 mWallpaperInfoView.populateWallpaperInfo(
Chuck Liao9991d682021-01-20 17:45:14 +08001047 mSelectedWallpaperInfo,
1048 actionLabel,
1049 WallpaperInfoHelper.shouldShowExploreButton(
1050 getContext(), exploreIntent),
1051 v -> onExploreClicked(exploreIntent))
1052 );
Chuck Liaob3829fb2020-04-01 00:47:50 +08001053 }
1054
1055 if (mWallpaperSelectedListener != null) {
1056 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001057 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001058 }
1059
chihhangchuangc687d912020-05-04 14:33:05 +08001060 private void onExploreClicked(Intent exploreIntent) {
1061 if (getContext() == null) {
1062 return;
1063 }
1064 Context context = getContext();
1065 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
1066 mSelectedWallpaperInfo.getActionLabelRes(context));
1067
1068 startActivity(exploreIntent);
1069 }
1070
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001071 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +08001072 if (wallpaperInfo == null) {
1073 return;
1074 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001075 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001076 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001077 ? index + 1 : index;
1078 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1079 if (holder != null) {
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001080 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001081 thumbnail.setClipped(isActivated);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001082 } else {
1083 // Item is not visible, make sure the item is re-bound when it becomes visible.
1084 mAdapter.notifyItemChanged(index);
1085 }
1086 }
1087
“Chuckffd832c2020-03-22 02:15:58 +08001088 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1089 if (wallpaperInfo == null) {
1090 return;
1091 }
1092 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001093 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001094 ? index + 1 : index;
1095 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001096 if (holder == null) {
“Chuckffd832c2020-03-22 02:15:58 +08001097 // Item is not visible, make sure the item is re-bound when it becomes visible.
1098 mAdapter.notifyItemChanged(index);
1099 }
1100 }
1101
Chuck Liao11f4a762020-04-08 13:24:43 +08001102 private void refreshAppliedWallpaper() {
1103 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1104 showCheckMarkAndBorderForAppliedWallpaper(false);
1105
1106 // Update to the new applied wallpaper.
1107 String appliedWallpaperId = getAppliedWallpaperId();
1108 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1109 .stream()
1110 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1111 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1112 .findFirst();
1113 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1114
1115 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1116 showCheckMarkAndBorderForAppliedWallpaper(true);
1117 }
1118
1119 private String getAppliedWallpaperId() {
1120 WallpaperPreferences prefs =
1121 InjectorProvider.getInjector().getPreferences(getContext());
1122 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1123 boolean isDestinationBoth =
1124 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1125
1126 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1127 return wallpaperInfo != null
1128 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1129 } else {
1130 return prefs.getLockWallpaperRemoteId();
1131 }
1132 }
1133
Chuck Liaoec053b02021-04-24 01:30:50 +08001134 private Set<String> getAppliedWallpaperIds() {
1135 WallpaperPreferences prefs =
1136 InjectorProvider.getInjector().getPreferences(getContext());
1137 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1138 Set<String> appliedWallpaperIds = new ArraySet<>();
1139
1140 String homeWallpaperId = wallpaperInfo != null ? wallpaperInfo.getServiceName()
1141 : prefs.getHomeWallpaperRemoteId();
1142 if (!TextUtils.isEmpty(homeWallpaperId)) {
1143 appliedWallpaperIds.add(homeWallpaperId);
1144 }
1145
1146 boolean isLockWallpaperApplied =
1147 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) >= 0;
1148 String lockWallpaperId = prefs.getLockWallpaperRemoteId();
1149 if (isLockWallpaperApplied && !TextUtils.isEmpty(lockWallpaperId)) {
1150 appliedWallpaperIds.add(lockWallpaperId);
1151 }
1152
1153 return appliedWallpaperIds;
1154 }
1155
Chuck Liao11f4a762020-04-08 13:24:43 +08001156 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1157 updateAppliedStatus(mAppliedWallpaperInfo, show);
1158 if (mSelectedWallpaperInfo == null) {
1159 updateActivatedStatus(mAppliedWallpaperInfo, show);
1160 }
1161 }
1162
Ching-Sung Lib69e9932021-05-25 20:03:01 +08001163 boolean shouldShowRotationTile() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001164 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001165 }
1166
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001167 class EmptySelectionAnimator implements SelectionAnimator{
1168 EmptySelectionAnimator() {}
1169
1170 public boolean isSelected() {
1171 return false;
1172 }
1173
1174 /**
1175 * Sets the UI to selected immediately with no animation.
1176 */
1177 public void selectImmediately() {}
1178
1179 /**
1180 * Sets the UI to deselected immediately with no animation.
1181 */
1182 public void deselectImmediately() {}
1183
1184 /**
1185 * Sets the UI to selected with a smooth animation.
1186 */
1187 public void animateSelected() {}
1188
1189 /**
1190 * Sets the UI to deselected with a smooth animation.
1191 */
1192 public void animateDeselected() {}
1193
1194 /**
1195 * Sets the UI to show a loading indicator.
1196 */
1197 public void showLoading() {}
1198
1199 /**
1200 * Sets the UI to hide the loading indicator.
1201 */
1202 public void showNotLoading() {}
1203
1204 }
1205
Jon Miranda16ea1b12017-12-12 14:52:48 -08001206 /**
1207 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1208 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001209 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1210 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1211 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1212 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001213
1214 private final List<WallpaperInfo> mWallpapers;
1215
1216 private int mPendingSelectedAdapterPosition;
1217 private int mSelectedAdapterPosition;
1218
1219 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1220 mWallpapers = wallpapers;
1221 mPendingSelectedAdapterPosition = -1;
1222 mSelectedAdapterPosition = -1;
1223 }
1224
1225 @Override
1226 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1227 switch (viewType) {
1228 case ITEM_VIEW_TYPE_ROTATION:
1229 return createRotationHolder(parent);
1230 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1231 return createIndividualHolder(parent);
1232 case ITEM_VIEW_TYPE_MY_PHOTOS:
1233 return createMyPhotosHolder(parent);
1234 default:
1235 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1236 return null;
1237 }
1238 }
1239
1240 @Override
1241 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001242 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001243 return ITEM_VIEW_TYPE_ROTATION;
1244 }
1245
1246 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1247 if (mCategory.supportsCustomPhotos()
1248 && !isRotationEnabled()
1249 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1250 return ITEM_VIEW_TYPE_MY_PHOTOS;
1251 }
1252
1253 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1254 }
1255
1256 @Override
1257 public void onBindViewHolder(ViewHolder holder, int position) {
1258 int viewType = getItemViewType(position);
1259
1260 switch (viewType) {
1261 case ITEM_VIEW_TYPE_ROTATION:
1262 onBindRotationHolder(holder, position);
1263 break;
1264 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1265 onBindIndividualHolder(holder, position);
1266 break;
1267 case ITEM_VIEW_TYPE_MY_PHOTOS:
1268 ((MyPhotosViewHolder) holder).bind();
1269 break;
1270 default:
1271 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1272 }
1273 }
1274
1275 @Override
1276 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001277 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001278 ? mWallpapers.size() + 1
1279 : mWallpapers.size();
1280 }
1281
1282 private ViewHolder createRotationHolder(ViewGroup parent) {
1283 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001284 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001285 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Chuck Liao5a4243b2020-05-20 23:56:39 +08001286 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1287 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001288 }
1289
1290 private ViewHolder createIndividualHolder(ViewGroup parent) {
1291 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1292 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1293
1294 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001295 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001296 return new SetIndividualHolder(
1297 getActivity(), mTileSizePx.y, view,
1298 selectionAnimator,
1299 new OnSetListener() {
1300 @Override
1301 public void onPendingWallpaperSet(int adapterPosition) {
1302 // Deselect and hide loading indicator for any previously pending tile.
1303 if (mPendingSelectedAdapterPosition != -1) {
1304 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1305 mPendingSelectedAdapterPosition);
1306 if (oldViewHolder instanceof SelectableHolder) {
1307 ((SelectableHolder) oldViewHolder).setSelectionState(
1308 SelectableHolder.SELECTION_STATE_DESELECTED);
1309 }
1310 }
1311
1312 if (mSelectedAdapterPosition != -1) {
1313 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1314 mSelectedAdapterPosition);
1315 if (oldViewHolder instanceof SelectableHolder) {
1316 ((SelectableHolder) oldViewHolder).setSelectionState(
1317 SelectableHolder.SELECTION_STATE_DESELECTED);
1318 }
1319 }
1320
1321 mPendingSelectedAdapterPosition = adapterPosition;
1322 }
1323
1324 @Override
1325 public void onWallpaperSet(int adapterPosition) {
1326 // No-op -- UI handles a new wallpaper being set by reacting to the
1327 // WallpaperChangedNotifier.
1328 }
1329
1330 @Override
1331 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1332 showSetWallpaperErrorDialog();
1333 mPendingSetIndividualHolder = holder;
1334 }
1335 });
1336 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001337 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001338 }
1339 }
1340
1341 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1342 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1343 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1344
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001345 return new MyPhotosViewHolder(getActivity(),
1346 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1347 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001348 }
1349
1350 /**
1351 * Marks the tile at the given position as selected with a visual indication. Also updates the
1352 * "currently selected" BottomSheet to reflect the newly selected tile.
1353 */
1354 private void updateSelectedTile(int newlySelectedPosition) {
1355 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1356 // succession.
1357 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1358 return;
1359 }
1360
1361 if (mCurrentWallpaperBottomSheetPresenter != null) {
1362 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1363 IndividualPickerFragment.this);
1364
1365 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1366 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1367 }
1368 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1369 @Override
1370 public void run() {
1371 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1372 }
1373 };
1374 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1375 }
1376
1377 // User may have switched to another category, thus detaching this fragment, so check here.
1378 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1379 // still occurs in the UI after the user selects that other category.
1380 if (getActivity() == null) {
1381 return;
1382 }
1383
1384 // Update the newly selected wallpaper ViewHolder and the old one so that if
1385 // selection UI state applies (desktop UI), it is updated.
1386 if (mSelectedAdapterPosition >= 0) {
1387 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1388 mSelectedAdapterPosition);
1389 if (oldViewHolder instanceof SelectableHolder) {
1390 ((SelectableHolder) oldViewHolder).setSelectionState(
1391 SelectableHolder.SELECTION_STATE_DESELECTED);
1392 }
1393 }
1394
1395 // Animate selection of newly selected tile.
1396 ViewHolder newViewHolder = mImageGrid
1397 .findViewHolderForAdapterPosition(newlySelectedPosition);
1398 if (newViewHolder instanceof SelectableHolder) {
1399 ((SelectableHolder) newViewHolder).setSelectionState(
1400 SelectableHolder.SELECTION_STATE_SELECTED);
1401 }
1402
1403 mSelectedAdapterPosition = newlySelectedPosition;
1404
1405 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1406 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1407 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1408 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1409 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1410 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1411
1412 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1413 }
1414
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001415 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001416 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1417 String collectionId = mCategory.getCollectionId();
1418 ((DesktopRotationHolder) holder).bind(collectionId);
1419
1420 if (mWallpaperPreferences.getWallpaperPresentationMode()
1421 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1422 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1423 mSelectedAdapterPosition = position;
1424 }
1425
1426 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1427 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1428 mWasUpdateRunnableRun = true;
1429 }
1430 }
1431 }
1432
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001433 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001434 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001435 ? position - 1 : position;
1436 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -07001437 wallpaper.computePlaceholderColor(holder.itemView.getContext());
Jon Miranda16ea1b12017-12-12 14:52:48 -08001438 ((IndividualHolder) holder).bindWallpaper(wallpaper);
Chuck Liaoec053b02021-04-24 01:30:50 +08001439 boolean isWallpaperApplied = mAppliedWallpaperIds.contains(wallpaper.getWallpaperId());
Jon Miranda16ea1b12017-12-12 14:52:48 -08001440
“Chuckffd832c2020-03-22 02:15:58 +08001441 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001442 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001443 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001444 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001445
Ching-Sung Li1ac024f2021-06-08 14:43:28 +08001446 CardView container = holder.itemView.findViewById(R.id.wallpaper_container);
1447 int radiusId = isFewerColumnLayout() ? R.dimen.grid_item_all_radius
1448 : R.dimen.grid_item_all_radius_small;
1449 container.setRadius(getResources().getDimension(radiusId));
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001450 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001451 thumbnail.setClipped(isWallpaperApplied);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001452 }
1453 }
Chuck Liaoba401232020-03-13 20:11:04 +08001454
1455 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1456
Chuck Liao1e6cb682021-04-22 00:51:11 +08001457 private final int mPaddingHorizontal;
1458 private final int mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001459
Chuck Liao1e6cb682021-04-22 00:51:11 +08001460 GridPaddingDecoration(int paddingHorizontal, int paddingBottom) {
1461 mPaddingHorizontal = paddingHorizontal;
1462 mPaddingBottom = paddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001463 }
1464
1465 @Override
1466 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1467 RecyclerView.State state) {
1468 int position = parent.getChildAdapterPosition(view);
1469 if (position >= 0) {
Chuck Liao1e6cb682021-04-22 00:51:11 +08001470 outRect.left = mPaddingHorizontal;
1471 outRect.right = mPaddingHorizontal;
1472 outRect.bottom = mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001473 }
1474 }
1475 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001476}