blob: c1eb6c94eb9258f0a369b0bc122e362f73f59ecd [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 Liao7dc21572020-05-14 18:37:36 +080018import static com.android.wallpaper.picker.WallpaperPickerDelegate.PREVIEW_WALLPAPER_REQUEST_CODE;
Chuck Liao69630f12020-03-05 19:01:25 +080019import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
Chuck Liao7dc21572020-05-14 18:37:36 +080020import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
chihhangchuang22aa0cc2020-03-25 19:12:42 +080021import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
Chuck Liao8ec38e02020-02-26 20:59:32 +080022import static com.android.wallpaper.widget.BottomActionBar.BottomAction.ROTATION;
23
Chuck Liaoa7215812021-04-13 22:33:43 +080024import android.annotation.MenuRes;
Jon Miranda16ea1b12017-12-12 14:52:48 -080025import android.app.Activity;
26import android.app.ProgressDialog;
“Chuck7ef99722020-03-22 04:34:03 +080027import android.app.WallpaperManager;
Jon Miranda16ea1b12017-12-12 14:52:48 -080028import android.content.Context;
Chihhang Chuang85f099a2020-06-16 18:04:39 +080029import android.content.DialogInterface;
chihhangchuangc687d912020-05-04 14:33:05 +080030import android.content.Intent;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070031import android.content.res.Configuration;
Jon Miranda16ea1b12017-12-12 14:52:48 -080032import android.content.res.Resources.NotFoundException;
33import android.graphics.Point;
Chuck Liaoba401232020-03-13 20:11:04 +080034import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080035import android.os.Build.VERSION;
36import android.os.Build.VERSION_CODES;
37import android.os.Bundle;
38import android.os.Handler;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070039import android.service.wallpaper.WallpaperService;
Jon Miranda16ea1b12017-12-12 14:52:48 -080040import android.util.Log;
41import android.view.LayoutInflater;
Chuck Liao33f1af42021-04-13 20:52:35 +080042import android.view.MenuItem;
Jon Miranda16ea1b12017-12-12 14:52:48 -080043import android.view.View;
44import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080045import android.widget.Toast;
46
Santiago Etchebehereb1854472019-06-06 17:44:54 -070047import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080048import androidx.annotation.Nullable;
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;
Chuck Liao7dc21572020-05-14 18:37:36 +080086import com.android.wallpaper.picker.PreviewActivity;
Jon Miranda16ea1b12017-12-12 14:52:48 -080087import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080088import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080089import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
90import com.android.wallpaper.picker.StartRotationDialogFragment;
91import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080092import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080093import com.android.wallpaper.picker.WallpapersUiContainer;
94import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
95import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070096import com.android.wallpaper.util.SizeCalculator;
Chuck Liao8ec38e02020-02-26 20:59:32 +080097import com.android.wallpaper.widget.BottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +080098import com.android.wallpaper.widget.WallpaperInfoView;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080099import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
100import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -0700101
Jon Miranda16ea1b12017-12-12 14:52:48 -0800102import com.bumptech.glide.Glide;
103import com.bumptech.glide.MemoryCategory;
104
105import java.util.ArrayList;
106import java.util.Date;
107import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800108import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800109import java.util.Random;
110
111/**
112 * Displays the Main UI for picking an individual wallpaper image.
113 */
Chuck Liao58aca1c2021-03-17 01:20:55 +0800114public class IndividualPickerFragment extends AppbarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800115 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
116 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800117 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
118 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800119
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800120 /**
121 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
122 * such as "my photos" or "daily rotation".
123 */
124 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
125 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
126
Jon Miranda16ea1b12017-12-12 14:52:48 -0800127 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800128 private static final int UNUSED_REQUEST_CODE = 1;
129 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
130 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
131 private static final String PROGRESS_DIALOG_NO_TITLE = null;
132 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
133 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
134 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700135 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800136
Chuck Liao8ec38e02020-02-26 20:59:32 +0800137 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800138 * An interface for updating the thumbnail with the specific wallpaper.
139 */
140 public interface ThumbnailUpdater {
141 /**
142 * Updates the thumbnail with the specific wallpaper.
143 */
144 void updateThumbnail(WallpaperInfo wallpaperInfo);
145
146 /**
147 * Restores to the thumbnails of the wallpapers which were applied.
148 */
149 void restoreThumbnails();
150 }
151
“Chuck7ef99722020-03-22 04:34:03 +0800152 /**
153 * An interface for receiving the destination of the new applied wallpaper.
154 */
155 public interface WallpaperDestinationCallback {
156 /**
157 * Called when the destination of the wallpaper is set.
158 *
159 * @param destination the destination which a wallpaper may be set.
160 * See {@link Destination} for more details.
161 */
162 void onDestinationSet(@Destination int destination);
163 }
164
Chuck Liaob3829fb2020-04-01 00:47:50 +0800165 /**
166 * The listener which will be notified when the wallpaper is selected.
167 */
168 public interface WallpaperSelectedListener {
169 /**
170 * Called when the wallpaper is selected.
171 *
172 * @param position the position of the selected wallpaper
173 */
174 void onWallpaperSelected(int position);
175 }
176
Chuck Liaof6b4b192020-08-07 02:31:32 +0800177 /**
Chuck Liao7e333722021-02-27 02:21:20 +0800178 * Interface to be implemented by a Fragment(or an Activity) hosting
179 * a {@link IndividualPickerFragment}.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800180 */
181 public interface IndividualPickerFragmentHost {
182 /**
Chuck Liao58aca1c2021-03-17 01:20:55 +0800183 * Indicates if the host has toolbar to show the title. If it does, we should set the title
184 * there.
185 */
186 boolean isHostToolbarShown();
187
188 /**
189 * Sets the title in the host's toolbar.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800190 */
191 void setToolbarTitle(CharSequence title);
192
193 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800194 * Configures the menu in the toolbar.
195 *
196 * @param menuResId the resource id of the menu
197 */
198 void setToolbarMenu(@MenuRes int menuResId);
199
200 /**
201 * Removes the menu in the toolbar.
202 */
203 void removeToolbarMenu();
204
205 /**
Chuck Liaof6b4b192020-08-07 02:31:32 +0800206 * Moves to the previous fragment.
207 */
208 void moveToPreviousFragment();
209 }
210
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800211 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800212 WallpaperPreferences mWallpaperPreferences;
213 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800214 RecyclerView mImageGrid;
215 IndividualAdapter mAdapter;
216 WallpaperCategory mCategory;
217 WallpaperRotationInitializer mWallpaperRotationInitializer;
218 List<WallpaperInfo> mWallpapers;
219 Point mTileSizePx;
220 WallpapersUiContainer mWallpapersUiContainer;
221 @FormFactor
222 int mFormFactor;
223 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800224
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800225 Handler mHandler;
226 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800227 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800228
229 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
230 new WallpaperChangedNotifier.Listener() {
231 @Override
232 public void onWallpaperChanged() {
233 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
234 return;
235 }
236
237 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
238 mAdapter.mSelectedAdapterPosition);
239
240 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
241 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
242 if (selectedViewHolder instanceof SelectableHolder) {
243 ((SelectableHolder) selectedViewHolder).setSelectionState(
244 SelectableHolder.SELECTION_STATE_DESELECTED);
245 }
246 } else {
247 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
248 }
249 }
250 };
251 PackageStatusNotifier.Listener mAppStatusListener;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800252 BottomActionBar mBottomActionBar;
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;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800262
263 /**
264 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
265 * committing fragment transactions.
266 */
267 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
268 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
269
Jon Miranda16ea1b12017-12-12 14:52:48 -0800270 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800271
272 /**
273 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
274 * invocation of the fragment.
275 */
276 private boolean mWasUpdateRunnableRun;
277
278 /**
279 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
280 * mode.
281 */
282 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
283 @Override
284 public void run() {
285 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
286 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
287 if (viewHolder instanceof DesktopRotationHolder) {
288 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
289 } else { // viewHolder is null
290 // If the rotation tile is unavailable (because user has scrolled down, causing the
291 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
292 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
293 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
294 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
295 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
296 }
297 }
298 };
299
Chuck Liao69630f12020-03-05 19:01:25 +0800300 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800301 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800302 private WallpaperManager mWallpaperManager;
303 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800304 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800305
Jon Miranda16ea1b12017-12-12 14:52:48 -0800306 public static IndividualPickerFragment newInstance(String collectionId) {
307 Bundle args = new Bundle();
308 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
309
310 IndividualPickerFragment fragment = new IndividualPickerFragment();
311 fragment.setArguments(args);
312 return fragment;
313 }
314
“Chuck7ef99722020-03-22 04:34:03 +0800315 /**
316 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
317 * would be set.
318 *
319 * @param wallpaperDestination the destination a wallpaper would be set.
320 * It will be either {@link Destination#DEST_HOME_SCREEN}
321 * or {@link Destination#DEST_LOCK_SCREEN}.
322 */
323 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
324 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800325 if (mWallpapers != null) {
326 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800327 }
328 }
329
Jon Miranda16ea1b12017-12-12 14:52:48 -0800330 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
331 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
332 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
333 getActivity());
334 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
335 @Override
336 public void onDrawableLoaded() {
337 if (getActivity() == null) {
338 return;
339 }
340
341 // Schedule the next update of the thumbnail.
342 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
343 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
344 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
345 }
346 });
347 }
348
349 @Override
350 public void onCreate(Bundle savedInstanceState) {
351 super.onCreate(savedInstanceState);
352
353 Injector injector = InjectorProvider.getInjector();
354 Context appContext = getContext().getApplicationContext();
355 mWallpaperPreferences = injector.getPreferences(appContext);
356
357 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
358 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
359
“Chuck7ef99722020-03-22 04:34:03 +0800360 mWallpaperManager = WallpaperManager.getInstance(appContext);
361
Jon Miranda16ea1b12017-12-12 14:52:48 -0800362 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
363
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700364 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
365
chihhangchuangc687d912020-05-04 14:33:05 +0800366 mUserEventLogger = injector.getUserEventLogger(appContext);
367
“Chuckffd832c2020-03-22 02:15:58 +0800368 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800369 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800370 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800371 injector.getPreferences(appContext),
372 injector.getUserEventLogger(appContext),
373 false);
374
Jon Miranda16ea1b12017-12-12 14:52:48 -0800375 mWallpapers = new ArrayList<>();
376 mRandom = new Random();
377 mHandler = new Handler();
378
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700379 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
380 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
381 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
382 Glide.get(getContext()).clearMemory();
383 }
384
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700385 CategoryProvider categoryProvider = injector.getCategoryProvider(appContext);
386 categoryProvider.fetchCategories(new CategoryReceiver() {
387 @Override
388 public void onCategoryReceived(Category category) {
389 // Do nothing.
390 }
391
392 @Override
393 public void doneFetchingCategories() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800394 Category category = categoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700395 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800396 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800397 return;
398 }
399 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700400 if (mCategory == null) {
401 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
402
403 // The absence of this category in the CategoryProvider indicates a broken
404 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800405 getIndividualPickerFragmentHost().moveToPreviousFragment();
406 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
407 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700408 return;
409 }
410 onCategoryLoaded();
411 }
412 }, false);
413 }
414
Chuck Liaof6b4b192020-08-07 02:31:32 +0800415
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700416 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800417 if (getIndividualPickerFragmentHost() == null) {
418 return;
419 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800420 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
421 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
422 } else {
423 setTitle(mCategory.getTitle());
424 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700425 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao58e4a1c2020-05-22 11:35:35 +0800426 // Avoids the "rotation" action is not shown correctly
427 // in a rare case : onCategoryLoaded() is called after onBottomActionBarReady().
428 if (isRotationEnabled() && mBottomActionBar != null
429 && !mBottomActionBar.areActionsShown(ROTATION)) {
430 mBottomActionBar.showActions(ROTATION);
431 }
Chuck Liao33f1af42021-04-13 20:52:35 +0800432 if (mToolbar != null && isRotationEnabled()) {
433 setUpToolbarMenu(R.menu.individual_picker_menu);
434 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700435 fetchWallpapers(false);
436
437 if (mCategory.supportsThirdParty()) {
438 mAppStatusListener = (packageName, status) -> {
439 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
440 mCategory.containsThirdParty(packageName)) {
441 fetchWallpapers(true);
442 }
443 };
444 mPackageStatusNotifier.addListener(mAppStatusListener,
445 WallpaperService.SERVICE_INTERFACE);
446 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700447
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700448 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700449 }
450
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800451 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700452 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800453 mIsWallpapersReceived = false;
454 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800455 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
456 @Override
457 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800458 mIsWallpapersReceived = true;
459 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800460 for (WallpaperInfo wallpaper : wallpapers) {
461 mWallpapers.add(wallpaper);
462 }
463
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700464 // Wallpapers may load after the adapter is initialized, in which case we have
465 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800466 if (mAdapter != null) {
467 mAdapter.notifyDataSetChanged();
468 }
469
470 if (mWallpapersUiContainer != null) {
471 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700472 } else {
473 if (wallpapers.isEmpty()) {
474 // If there are no more wallpapers and we're on phone, just finish the
475 // Activity.
476 Activity activity = getActivity();
477 if (activity != null
478 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
479 activity.finish();
480 }
481 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800482 }
483 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700484 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800485 }
486
Chuck Liaof6b4b192020-08-07 02:31:32 +0800487 void updateLoading() {
488 if (mLoading == null) {
489 return;
490 }
491
492 if (mIsWallpapersReceived) {
493 mLoading.hide();
494 } else {
495 mLoading.show();
496 }
497 }
498
Jon Miranda16ea1b12017-12-12 14:52:48 -0800499 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700500 public void onSaveInstanceState(@NonNull Bundle outState) {
501 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700502 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700503 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
504 }
505
506 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800507 public View onCreateView(LayoutInflater inflater, ViewGroup container,
508 Bundle savedInstanceState) {
509 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
Chuck Liao58aca1c2021-03-17 01:20:55 +0800510 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
511 view.findViewById(R.id.header_bar).setVisibility(View.GONE);
Kunhung Lid0174362021-04-05 15:31:41 +0800512 setUpArrowEnabled(/* upArrow= */ true);
Chuck Liaoa7215812021-04-13 22:33:43 +0800513 if (isRotationEnabled()) {
514 getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu);
515 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800516 } else {
517 setUpToolbar(view);
Chuck Liao33f1af42021-04-13 20:52:35 +0800518 if (isRotationEnabled()) {
519 setUpToolbarMenu(R.menu.individual_picker_menu);
520 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800521 if (mCategory != null) {
522 setTitle(mCategory.getTitle());
523 }
524 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800525
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700526 mTileSizePx = SizeCalculator.getIndividualTileSize(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800527
528 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
529 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
530 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
531 updateImageGridPadding(false /* addExtraBottomSpace */);
532 mImageGrid.setScrollBarSize(gridPaddingPx);
533 }
Chuck Liaoba401232020-03-13 20:11:04 +0800534 mImageGrid.addItemDecoration(new GridPaddingDecoration(
535 getResources().getDimensionPixelSize(R.dimen.grid_padding)));
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +0800536 mImageGrid.setAccessibilityDelegateCompat(
537 new WallpaperPickerRecyclerViewAccessibilityDelegate(
538 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Chuck Liaof6b4b192020-08-07 02:31:32 +0800539 mLoading = view.findViewById(R.id.loading_indicator);
540 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700541 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800542 setUpBottomSheet();
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800543 // For nav bar edge-to-edge effect.
544 view.findViewById(R.id.wallpaper_grid).setOnApplyWindowInsetsListener((v, windowInsets) -> {
545 v.setPadding(
546 v.getPaddingLeft(),
547 v.getPaddingTop(),
548 v.getPaddingRight(),
549 windowInsets.getSystemWindowInsetBottom());
550 return windowInsets.consumeSystemWindowInsets();
551 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800552 return view;
553 }
554
555 @Override
556 public void onClickTryAgain(@Destination int unused) {
557 if (mPendingSetIndividualHolder != null) {
558 mPendingSetIndividualHolder.setWallpaper();
559 }
560 }
561
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800562 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800563 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
564 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
565 R.dimen.current_wallpaper_bottom_sheet_layout_height);
566 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
567 // Only left and top may be set in order for the GridMarginDecoration to work properly.
568 mImageGrid.setPadding(
569 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
570 }
571
Chuck Liaof6b4b192020-08-07 02:31:32 +0800572 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
Chuck Liao7e333722021-02-27 02:21:20 +0800573 Fragment parentFragment = getParentFragment();
574 if (parentFragment != null) {
575 return (IndividualPickerFragmentHost) parentFragment;
576 } else {
577 return (IndividualPickerFragmentHost) getActivity();
578 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800579 }
580
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700581 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700582 // Skip if mImageGrid been initialized yet
583 if (mImageGrid == null) {
584 return;
585 }
586 // Skip if category hasn't loaded yet
587 if (mCategory == null) {
588 return;
589 }
590 // Skip if the adapter was already created
591 if (mAdapter != null) {
592 return;
593 }
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700594 setUpImageGrid();
595 }
596
597 /**
598 * Create the adapter and assign it to mImageGrid.
599 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
600 */
601 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800602 mAdapter = new IndividualAdapter(mWallpapers);
603 mImageGrid.setAdapter(mAdapter);
604 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
605 }
606
Jon Miranda16ea1b12017-12-12 14:52:48 -0800607 /**
608 * Enables and populates the "Currently set" wallpaper BottomSheet.
609 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800610 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800611 mImageGrid.addOnScrollListener(new OnScrollListener() {
612 @Override
613 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
614 if (mCurrentWallpaperBottomSheetPresenter == null) {
615 return;
616 }
617
618 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
619 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
620 }
621 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
622 @Override
623 public void run() {
624 if (dy > 0) {
625 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
626 } else {
627 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
628 }
629 }
630 };
631 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
632 }
633 });
634 }
635
chihhangchuang3efb6832020-04-17 02:06:25 +0800636 @Override
637 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Kunhung Lic6701492021-01-28 17:35:56 +0800638 super.onBottomActionBarReady(bottomActionBar);
Chuck Liao342f4ab2020-05-26 18:28:09 +0800639 mBottomActionBar = bottomActionBar;
Kunhung Lic6701492021-01-28 17:35:56 +0800640 boolean isRotationEnabled = isRotationEnabled();
641 if (isRotationEnabled) {
Chuck Liao342f4ab2020-05-26 18:28:09 +0800642 mBottomActionBar.showActionsOnly(ROTATION);
Ching-Sung Li073812b2020-04-07 21:19:21 +0800643 }
Chuck Liao342f4ab2020-05-26 18:28:09 +0800644 mBottomActionBar.setActionClickListener(ROTATION, unused -> {
645 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
646 startRotationDialogFragment.setTargetFragment(
647 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
648 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
649 });
650 mBottomActionBar.setActionClickListener(APPLY, unused -> {
651 mBottomActionBar.disableActions();
652 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
653 mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
654 });
655
656 mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
657 .inflate(R.layout.wallpaper_info_view, /* root= */ null);
658 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
659 mBottomActionBar.setActionClickListener(EDIT, unused -> {
660 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
661 mSelectedWallpaperInfo.showPreview(getActivity(),
662 new PreviewActivity.PreviewActivityIntentFactory(),
663 PREVIEW_WALLPAPER_REQUEST_CODE);
664 });
Kunhung Lic6701492021-01-28 17:35:56 +0800665 if (isRotationEnabled || (mBottomActionBar.getBackButtonVisibility() == View.VISIBLE)) {
666 mBottomActionBar.show();
667 }
Ching-Sung Li073812b2020-04-07 21:19:21 +0800668 }
669
Jon Miranda16ea1b12017-12-12 14:52:48 -0800670 @Override
671 public void onResume() {
672 super.onResume();
673
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700674 WallpaperPreferences preferences = InjectorProvider.getInjector()
675 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800676 preferences.setLastAppActiveTimestamp(new Date().getTime());
677
678 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
679 // PreviewFragment.
680 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
681
682 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
683 // shown earlier when this fragment's hosting activity didn't allow committing fragment
684 // transactions.
685 if (mStagedStartRotationErrorDialogFragment != null) {
686 mStagedStartRotationErrorDialogFragment.show(
687 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
688 mStagedStartRotationErrorDialogFragment = null;
689 }
690
691 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
692 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
693 // committing fragment transactions.
694 if (mStagedSetWallpaperErrorDialogFragment != null) {
695 mStagedSetWallpaperErrorDialogFragment.show(
696 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
697 mStagedSetWallpaperErrorDialogFragment = null;
698 }
699
Chuck Liao5a4243b2020-05-20 23:56:39 +0800700 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
701 // Must be resuming from a previously stopped state, so re-schedule the update of the
702 // daily wallpapers tile thumbnail.
703 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800704 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800705 }
706
707 @Override
708 public void onStop() {
709 super.onStop();
710 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
711 }
712
713 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800714 public void onDestroyView() {
715 super.onDestroyView();
716 getIndividualPickerFragmentHost().removeToolbarMenu();
717 }
718
719 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800720 public void onDestroy() {
721 super.onDestroy();
722 if (mProgressDialog != null) {
723 mProgressDialog.dismiss();
724 }
725 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700726 if (mAppStatusListener != null) {
727 mPackageStatusNotifier.removeListener(mAppStatusListener);
728 }
Chuck Liao69630f12020-03-05 19:01:25 +0800729 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800730 }
731
732 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800733 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800734 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
735 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
736 // still holds a reference to the destroyed Fragment and is calling
737 // onStartRotationDialogDismissed on that destroyed Fragment.
738 if (mBottomActionBar != null) {
739 mBottomActionBar.deselectAction(ROTATION);
740 }
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800741 }
742
743 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800744 public void retryStartRotation(@NetworkPreference int networkPreference) {
745 startRotation(networkPreference);
746 }
747
chihhangchuang7feb3752020-04-24 02:48:56 +0800748 @Override
749 public boolean onBackPressed() {
750 if (mSelectedWallpaperInfo != null) {
751 onWallpaperSelected(null, 0);
752 return true;
753 }
754 return false;
755 }
756
Jon Miranda16ea1b12017-12-12 14:52:48 -0800757 public void setCurrentWallpaperBottomSheetPresenter(
758 CurrentWallpaperBottomSheetPresenter presenter) {
759 mCurrentWallpaperBottomSheetPresenter = presenter;
760 }
761
762 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
763 mWallpapersUiContainer = uiContainer;
764 }
765
Chuck Liaob3829fb2020-04-01 00:47:50 +0800766 public void setOnWallpaperSelectedListener(
767 WallpaperSelectedListener wallpaperSelectedListener) {
768 mWallpaperSelectedListener = wallpaperSelectedListener;
769 }
770
771 /**
772 * Resizes the layout's height.
773 */
774 public void resizeLayout(int height) {
775 mImageGrid.getLayoutParams().height = height;
776 mImageGrid.requestLayout();
777 }
778
779 /**
780 * Scrolls to the specific item.
781 *
782 * @param position the position of the item
783 */
784 public void scrollToPosition(int position) {
785 ((GridLayoutManager) mImageGrid.getLayoutManager())
786 .scrollToPositionWithOffset(position, /* offset= */ 0);
787 }
788
Jon Miranda16ea1b12017-12-12 14:52:48 -0800789 /**
790 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
791 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
792 * constantly keeps the UI thread alive and blocks a test forever.
793 *
794 * @param testingMode
795 */
796 void setTestingMode(boolean testingMode) {
797 mTestingMode = testingMode;
798 }
799
Jon Miranda16ea1b12017-12-12 14:52:48 -0800800 @Override
801 public void startRotation(@NetworkPreference final int networkPreference) {
802 if (!isRotationEnabled()) {
803 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
804 return;
805 }
806
807 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
808 // causes Espresso to hang once the dialog is shown.
809 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
810 int themeResId;
811 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
812 themeResId = R.style.ProgressDialogThemePreL;
813 } else {
814 themeResId = R.style.LightDialogTheme;
815 }
816 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
817
818 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
819 mProgressDialog.setMessage(
820 getResources().getString(R.string.start_rotation_progress_message));
821 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
822 mProgressDialog.show();
823 }
824
825 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
826 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
827 }
828
829 final Context appContext = getActivity().getApplicationContext();
830
831 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
832 appContext,
833 networkPreference,
834 new Listener() {
835 @Override
836 public void onFirstWallpaperInRotationSet() {
837 if (mProgressDialog != null) {
838 mProgressDialog.dismiss();
839 }
840
841 // The fragment may be detached from its containing activity if the user exits the
842 // app before the first wallpaper image in rotation finishes downloading.
843 Activity activity = getActivity();
844
Jon Miranda16ea1b12017-12-12 14:52:48 -0800845
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700846 if (mWallpaperRotationInitializer.startRotation(appContext)) {
847 if (activity != null
848 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
849 try {
850 Toast.makeText(getActivity(),
851 R.string.wallpaper_set_successfully_message,
852 Toast.LENGTH_SHORT).show();
853 } catch (NotFoundException e) {
854 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800855 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800856
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700857 activity.setResult(Activity.RESULT_OK);
858 activity.finish();
859 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
860 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
861 }
862 } else { // Failed to start rotation.
863 showStartRotationErrorDialog(networkPreference);
864
865 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
866 DesktopRotationHolder rotationViewHolder =
867 (DesktopRotationHolder)
868 mImageGrid.findViewHolderForAdapterPosition(
869 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
870 rotationViewHolder.setSelectionState(
871 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800872 }
873 }
874 }
875
876 @Override
877 public void onError() {
878 if (mProgressDialog != null) {
879 mProgressDialog.dismiss();
880 }
881
882 showStartRotationErrorDialog(networkPreference);
883
884 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
885 DesktopRotationHolder rotationViewHolder =
886 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
887 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
888 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
889 }
890 }
891 });
892 }
893
894 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800895 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800896 if (activity != null) {
897 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
898 StartRotationErrorDialogFragment.newInstance(networkPreference);
899 startRotationErrorDialogFragment.setTargetFragment(
900 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
901
902 if (activity.isSafeToCommitFragmentTransaction()) {
903 startRotationErrorDialogFragment.show(
904 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
905 } else {
906 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
907 }
908 }
909 }
910
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800911 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800912 Activity activity = getActivity();
Chuck Liaoe2fe0302020-06-29 21:15:35 +0800913 return activity == null ? 1 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800914 }
915
916 /**
917 * Returns whether rotation is enabled for this category.
918 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800919 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700920 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800921 }
922
923 @Override
924 public void onCurrentWallpaperRefreshed() {
925 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
926 }
927
Chuck Liao69630f12020-03-05 19:01:25 +0800928 @Override
929 public void onSet(int destination) {
930 if (mSelectedWallpaperInfo == null) {
931 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
932 return;
933 }
934
“Chuckffd832c2020-03-22 02:15:58 +0800935 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800936 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
937 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
938 destination, 0, null, mSetWallpaperCallback);
939 } else {
940 mWallpaperSetter.setCurrentWallpaper(
941 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
942 }
“Chuck7ef99722020-03-22 04:34:03 +0800943 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800944 }
945
946 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
947 new WallpaperPersister.SetWallpaperCallback() {
948 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800949 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800950 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800951 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
952 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800953 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
954 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800955 }
956
957 @Override
958 public void onError(@Nullable Throwable throwable) {
959 Log.e(TAG, "Can't apply the wallpaper.");
960 mBottomActionBar.enableActions();
961 }
962 };
963
964 @Override
965 public void onDialogDismissed(boolean withItemSelected) {
966 if (!withItemSelected) {
967 mBottomActionBar.enableActions();
968 }
969 }
970
Chuck Liao33f1af42021-04-13 20:52:35 +0800971 @Override
972 public boolean onMenuItemClick(MenuItem item) {
973 if (item.getItemId() == R.id.daily_rotation) {
Chuck Liaoa7215812021-04-13 22:33:43 +0800974 showRotationDialog();
Chuck Liao33f1af42021-04-13 20:52:35 +0800975 return true;
976 }
977 return super.onMenuItemClick(item);
978 }
979
Jon Miranda16ea1b12017-12-12 14:52:48 -0800980 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800981 * Popups a daily rotation dialog for the uses to confirm.
982 */
983 public void showRotationDialog() {
984 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
985 startRotationDialogFragment.setTargetFragment(
986 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
987 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
988 }
989
990 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800991 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
992 */
993 private void showSetWallpaperErrorDialog() {
994 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
995 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
996 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
997
998 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
999 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
1000 } else {
1001 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
1002 }
1003 }
1004
Ching-Sung Li073812b2020-04-07 21:19:21 +08001005 void updateBottomActions(boolean hasWallpaperSelected) {
chihhangchuang803ea9a2020-04-21 13:03:10 +08001006 if (hasWallpaperSelected) {
Chuck Liao7dc21572020-05-14 18:37:36 +08001007 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
chihhangchuang803ea9a2020-04-21 13:03:10 +08001008 } else {
chihhangchuang08abb582020-04-27 17:20:31 +08001009 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +08001010 }
Chuck Liao69630f12020-03-05 19:01:25 +08001011 }
1012
1013 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +08001014 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +08001015 if (thumbnailUpdater == null) {
1016 return;
1017 }
1018
1019 if (selectedWallpaperInfo != null) {
1020 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
1021 } else {
1022 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +08001023 }
1024 }
1025
“Chuck7ef99722020-03-22 04:34:03 +08001026 private void onWallpaperDestinationSet(int destination) {
1027 WallpaperDestinationCallback wallpaperDestinationCallback =
1028 (WallpaperDestinationCallback) getParentFragment();
1029 if (wallpaperDestinationCallback == null) {
1030 return;
1031 }
1032
1033 wallpaperDestinationCallback.onDestinationSet(destination);
1034 }
1035
Ching-Sung Li073812b2020-04-07 21:19:21 +08001036 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +08001037 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001038 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +08001039 return;
1040 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001041 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +08001042 updateActivatedStatus(mSelectedWallpaperInfo == null
1043 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001044 // Update new selected wallpaper.
1045 updateActivatedStatus(newSelectedWallpaperInfo == null
1046 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
1047
1048 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
1049 updateBottomActions(mSelectedWallpaperInfo != null);
1050 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +08001051 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +08001052 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +08001053 WallpaperInfoHelper.loadExploreIntent(
1054 getContext(),
1055 mSelectedWallpaperInfo,
1056 (actionLabel, exploreIntent) ->
1057 mWallpaperInfoView.populateWallpaperInfo(
Chuck Liao9991d682021-01-20 17:45:14 +08001058 mSelectedWallpaperInfo,
1059 actionLabel,
1060 WallpaperInfoHelper.shouldShowExploreButton(
1061 getContext(), exploreIntent),
1062 v -> onExploreClicked(exploreIntent))
1063 );
Chuck Liaob3829fb2020-04-01 00:47:50 +08001064 }
1065
1066 if (mWallpaperSelectedListener != null) {
1067 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001068 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001069 }
1070
chihhangchuangc687d912020-05-04 14:33:05 +08001071 private void onExploreClicked(Intent exploreIntent) {
1072 if (getContext() == null) {
1073 return;
1074 }
1075 Context context = getContext();
1076 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
1077 mSelectedWallpaperInfo.getActionLabelRes(context));
1078
1079 startActivity(exploreIntent);
1080 }
1081
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001082 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +08001083 if (wallpaperInfo == null) {
1084 return;
1085 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001086 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001087 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001088 ? index + 1 : index;
1089 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1090 if (holder != null) {
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001091 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001092 thumbnail.setClipped(isActivated);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001093 } else {
1094 // Item is not visible, make sure the item is re-bound when it becomes visible.
1095 mAdapter.notifyItemChanged(index);
1096 }
1097 }
1098
“Chuckffd832c2020-03-22 02:15:58 +08001099 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1100 if (wallpaperInfo == null) {
1101 return;
1102 }
1103 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001104 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001105 ? index + 1 : index;
1106 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001107 if (holder == null) {
“Chuckffd832c2020-03-22 02:15:58 +08001108 // Item is not visible, make sure the item is re-bound when it becomes visible.
1109 mAdapter.notifyItemChanged(index);
1110 }
1111 }
1112
Chuck Liao11f4a762020-04-08 13:24:43 +08001113 private void refreshAppliedWallpaper() {
1114 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1115 showCheckMarkAndBorderForAppliedWallpaper(false);
1116
1117 // Update to the new applied wallpaper.
1118 String appliedWallpaperId = getAppliedWallpaperId();
1119 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1120 .stream()
1121 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1122 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1123 .findFirst();
1124 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1125
1126 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1127 showCheckMarkAndBorderForAppliedWallpaper(true);
1128 }
1129
1130 private String getAppliedWallpaperId() {
1131 WallpaperPreferences prefs =
1132 InjectorProvider.getInjector().getPreferences(getContext());
1133 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1134 boolean isDestinationBoth =
1135 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1136
1137 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1138 return wallpaperInfo != null
1139 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1140 } else {
1141 return prefs.getLockWallpaperRemoteId();
1142 }
1143 }
1144
1145 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1146 updateAppliedStatus(mAppliedWallpaperInfo, show);
1147 if (mSelectedWallpaperInfo == null) {
1148 updateActivatedStatus(mAppliedWallpaperInfo, show);
1149 }
1150 }
1151
Chuck Liao5a4243b2020-05-20 23:56:39 +08001152 private boolean shouldShowRotationTile() {
1153 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001154 }
1155
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001156 class EmptySelectionAnimator implements SelectionAnimator{
1157 EmptySelectionAnimator() {}
1158
1159 public boolean isSelected() {
1160 return false;
1161 }
1162
1163 /**
1164 * Sets the UI to selected immediately with no animation.
1165 */
1166 public void selectImmediately() {}
1167
1168 /**
1169 * Sets the UI to deselected immediately with no animation.
1170 */
1171 public void deselectImmediately() {}
1172
1173 /**
1174 * Sets the UI to selected with a smooth animation.
1175 */
1176 public void animateSelected() {}
1177
1178 /**
1179 * Sets the UI to deselected with a smooth animation.
1180 */
1181 public void animateDeselected() {}
1182
1183 /**
1184 * Sets the UI to show a loading indicator.
1185 */
1186 public void showLoading() {}
1187
1188 /**
1189 * Sets the UI to hide the loading indicator.
1190 */
1191 public void showNotLoading() {}
1192
1193 }
1194
Jon Miranda16ea1b12017-12-12 14:52:48 -08001195 /**
1196 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1197 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001198 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1199 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1200 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1201 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001202
1203 private final List<WallpaperInfo> mWallpapers;
1204
1205 private int mPendingSelectedAdapterPosition;
1206 private int mSelectedAdapterPosition;
1207
1208 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1209 mWallpapers = wallpapers;
1210 mPendingSelectedAdapterPosition = -1;
1211 mSelectedAdapterPosition = -1;
1212 }
1213
1214 @Override
1215 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1216 switch (viewType) {
1217 case ITEM_VIEW_TYPE_ROTATION:
1218 return createRotationHolder(parent);
1219 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1220 return createIndividualHolder(parent);
1221 case ITEM_VIEW_TYPE_MY_PHOTOS:
1222 return createMyPhotosHolder(parent);
1223 default:
1224 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1225 return null;
1226 }
1227 }
1228
1229 @Override
1230 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001231 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001232 return ITEM_VIEW_TYPE_ROTATION;
1233 }
1234
1235 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1236 if (mCategory.supportsCustomPhotos()
1237 && !isRotationEnabled()
1238 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1239 return ITEM_VIEW_TYPE_MY_PHOTOS;
1240 }
1241
1242 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1243 }
1244
1245 @Override
1246 public void onBindViewHolder(ViewHolder holder, int position) {
1247 int viewType = getItemViewType(position);
1248
1249 switch (viewType) {
1250 case ITEM_VIEW_TYPE_ROTATION:
1251 onBindRotationHolder(holder, position);
1252 break;
1253 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1254 onBindIndividualHolder(holder, position);
1255 break;
1256 case ITEM_VIEW_TYPE_MY_PHOTOS:
1257 ((MyPhotosViewHolder) holder).bind();
1258 break;
1259 default:
1260 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1261 }
1262 }
1263
1264 @Override
1265 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001266 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001267 ? mWallpapers.size() + 1
1268 : mWallpapers.size();
1269 }
1270
1271 private ViewHolder createRotationHolder(ViewGroup parent) {
1272 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001273 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001274 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Chuck Liao5a4243b2020-05-20 23:56:39 +08001275 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1276 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001277 }
1278
1279 private ViewHolder createIndividualHolder(ViewGroup parent) {
1280 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1281 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1282
1283 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001284 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001285 return new SetIndividualHolder(
1286 getActivity(), mTileSizePx.y, view,
1287 selectionAnimator,
1288 new OnSetListener() {
1289 @Override
1290 public void onPendingWallpaperSet(int adapterPosition) {
1291 // Deselect and hide loading indicator for any previously pending tile.
1292 if (mPendingSelectedAdapterPosition != -1) {
1293 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1294 mPendingSelectedAdapterPosition);
1295 if (oldViewHolder instanceof SelectableHolder) {
1296 ((SelectableHolder) oldViewHolder).setSelectionState(
1297 SelectableHolder.SELECTION_STATE_DESELECTED);
1298 }
1299 }
1300
1301 if (mSelectedAdapterPosition != -1) {
1302 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1303 mSelectedAdapterPosition);
1304 if (oldViewHolder instanceof SelectableHolder) {
1305 ((SelectableHolder) oldViewHolder).setSelectionState(
1306 SelectableHolder.SELECTION_STATE_DESELECTED);
1307 }
1308 }
1309
1310 mPendingSelectedAdapterPosition = adapterPosition;
1311 }
1312
1313 @Override
1314 public void onWallpaperSet(int adapterPosition) {
1315 // No-op -- UI handles a new wallpaper being set by reacting to the
1316 // WallpaperChangedNotifier.
1317 }
1318
1319 @Override
1320 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1321 showSetWallpaperErrorDialog();
1322 mPendingSetIndividualHolder = holder;
1323 }
1324 });
1325 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001326 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001327 }
1328 }
1329
1330 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1331 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1332 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1333
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001334 return new MyPhotosViewHolder(getActivity(),
1335 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1336 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001337 }
1338
1339 /**
1340 * Marks the tile at the given position as selected with a visual indication. Also updates the
1341 * "currently selected" BottomSheet to reflect the newly selected tile.
1342 */
1343 private void updateSelectedTile(int newlySelectedPosition) {
1344 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1345 // succession.
1346 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1347 return;
1348 }
1349
1350 if (mCurrentWallpaperBottomSheetPresenter != null) {
1351 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1352 IndividualPickerFragment.this);
1353
1354 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1355 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1356 }
1357 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1358 @Override
1359 public void run() {
1360 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1361 }
1362 };
1363 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1364 }
1365
1366 // User may have switched to another category, thus detaching this fragment, so check here.
1367 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1368 // still occurs in the UI after the user selects that other category.
1369 if (getActivity() == null) {
1370 return;
1371 }
1372
1373 // Update the newly selected wallpaper ViewHolder and the old one so that if
1374 // selection UI state applies (desktop UI), it is updated.
1375 if (mSelectedAdapterPosition >= 0) {
1376 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1377 mSelectedAdapterPosition);
1378 if (oldViewHolder instanceof SelectableHolder) {
1379 ((SelectableHolder) oldViewHolder).setSelectionState(
1380 SelectableHolder.SELECTION_STATE_DESELECTED);
1381 }
1382 }
1383
1384 // Animate selection of newly selected tile.
1385 ViewHolder newViewHolder = mImageGrid
1386 .findViewHolderForAdapterPosition(newlySelectedPosition);
1387 if (newViewHolder instanceof SelectableHolder) {
1388 ((SelectableHolder) newViewHolder).setSelectionState(
1389 SelectableHolder.SELECTION_STATE_SELECTED);
1390 }
1391
1392 mSelectedAdapterPosition = newlySelectedPosition;
1393
1394 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1395 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1396 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1397 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1398 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1399 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1400
1401 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1402 }
1403
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001404 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001405 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1406 String collectionId = mCategory.getCollectionId();
1407 ((DesktopRotationHolder) holder).bind(collectionId);
1408
1409 if (mWallpaperPreferences.getWallpaperPresentationMode()
1410 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1411 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1412 mSelectedAdapterPosition = position;
1413 }
1414
1415 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1416 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1417 mWasUpdateRunnableRun = true;
1418 }
1419 }
1420 }
1421
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001422 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001423 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001424 ? position - 1 : position;
1425 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1426 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001427 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001428 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1429 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1430 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001431
“Chuckffd832c2020-03-22 02:15:58 +08001432 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001433 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001434 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001435 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001436
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001437 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001438 thumbnail.setClipped(isWallpaperApplied);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001439 }
1440 }
Chuck Liaoba401232020-03-13 20:11:04 +08001441
1442 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1443
1444 private int mPadding;
1445
1446 GridPaddingDecoration(int padding) {
1447 mPadding = padding;
1448 }
1449
1450 @Override
1451 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1452 RecyclerView.State state) {
1453 int position = parent.getChildAdapterPosition(view);
1454 if (position >= 0) {
1455 outRect.left = mPadding;
1456 outRect.right = mPadding;
1457 }
1458 }
1459 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001460}