blob: d2921a326e34dfbfb026b30314aff4db2521cd34 [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
Jon Miranda16ea1b12017-12-12 14:52:48 -080024import android.app.Activity;
25import android.app.ProgressDialog;
“Chuck7ef99722020-03-22 04:34:03 +080026import android.app.WallpaperManager;
Jon Miranda16ea1b12017-12-12 14:52:48 -080027import android.content.Context;
Chihhang Chuang85f099a2020-06-16 18:04:39 +080028import android.content.DialogInterface;
chihhangchuangc687d912020-05-04 14:33:05 +080029import android.content.Intent;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070030import android.content.res.Configuration;
Jon Miranda16ea1b12017-12-12 14:52:48 -080031import android.content.res.Resources.NotFoundException;
32import android.graphics.Point;
Chuck Liaoba401232020-03-13 20:11:04 +080033import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080034import android.os.Build.VERSION;
35import android.os.Build.VERSION_CODES;
36import android.os.Bundle;
37import android.os.Handler;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070038import android.service.wallpaper.WallpaperService;
Jon Miranda16ea1b12017-12-12 14:52:48 -080039import android.util.Log;
40import android.view.LayoutInflater;
41import android.view.View;
42import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080043import android.widget.Toast;
44
Santiago Etchebehereb1854472019-06-06 17:44:54 -070045import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080046import androidx.annotation.Nullable;
Chuck Liaof6b4b192020-08-07 02:31:32 +080047import androidx.core.widget.ContentLoadingProgressBar;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080048import androidx.fragment.app.DialogFragment;
Chuck Liao7e333722021-02-27 02:21:20 +080049import androidx.fragment.app.Fragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080050import androidx.recyclerview.widget.GridLayoutManager;
51import androidx.recyclerview.widget.RecyclerView;
52import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
53import androidx.recyclerview.widget.RecyclerView.ViewHolder;
54
Jon Miranda16ea1b12017-12-12 14:52:48 -080055import com.android.wallpaper.R;
56import com.android.wallpaper.asset.Asset;
57import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070058import com.android.wallpaper.model.Category;
59import com.android.wallpaper.model.CategoryProvider;
60import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080061import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080062import com.android.wallpaper.model.WallpaperCategory;
63import com.android.wallpaper.model.WallpaperInfo;
64import com.android.wallpaper.model.WallpaperReceiver;
65import com.android.wallpaper.model.WallpaperRotationInitializer;
66import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
67import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080068import com.android.wallpaper.module.FormFactorChecker;
69import com.android.wallpaper.module.FormFactorChecker.FormFactor;
70import com.android.wallpaper.module.Injector;
71import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070072import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080073import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080074import com.android.wallpaper.module.WallpaperChangedNotifier;
75import com.android.wallpaper.module.WallpaperPersister;
76import com.android.wallpaper.module.WallpaperPersister.Destination;
77import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080078import com.android.wallpaper.module.WallpaperSetter;
Chuck Liao58aca1c2021-03-17 01:20:55 +080079import com.android.wallpaper.picker.AppbarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080080import com.android.wallpaper.picker.BaseActivity;
81import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Chuck Liaob17d1502020-09-07 21:55:30 +080082import com.android.wallpaper.picker.FragmentTransactionChecker;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080083import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Chuck Liao7dc21572020-05-14 18:37:36 +080084import com.android.wallpaper.picker.PreviewActivity;
Jon Miranda16ea1b12017-12-12 14:52:48 -080085import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080086import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080087import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
88import com.android.wallpaper.picker.StartRotationDialogFragment;
89import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080090import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080091import com.android.wallpaper.picker.WallpapersUiContainer;
92import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
93import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070094import com.android.wallpaper.util.SizeCalculator;
Chuck Liao8ec38e02020-02-26 20:59:32 +080095import com.android.wallpaper.widget.BottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +080096import com.android.wallpaper.widget.WallpaperInfoView;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080097import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
98import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070099
Jon Miranda16ea1b12017-12-12 14:52:48 -0800100import com.bumptech.glide.Glide;
101import com.bumptech.glide.MemoryCategory;
102
103import java.util.ArrayList;
104import java.util.Date;
105import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800106import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800107import java.util.Random;
108
109/**
110 * Displays the Main UI for picking an individual wallpaper image.
111 */
Chuck Liao58aca1c2021-03-17 01:20:55 +0800112public class IndividualPickerFragment extends AppbarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800113 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
114 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800115 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
116 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800117
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800118 /**
119 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
120 * such as "my photos" or "daily rotation".
121 */
122 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
123 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
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 /**
192 * Moves to the previous fragment.
193 */
194 void moveToPreviousFragment();
195 }
196
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800197 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800198 WallpaperPreferences mWallpaperPreferences;
199 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800200 RecyclerView mImageGrid;
201 IndividualAdapter mAdapter;
202 WallpaperCategory mCategory;
203 WallpaperRotationInitializer mWallpaperRotationInitializer;
204 List<WallpaperInfo> mWallpapers;
205 Point mTileSizePx;
206 WallpapersUiContainer mWallpapersUiContainer;
207 @FormFactor
208 int mFormFactor;
209 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800210
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800211 Handler mHandler;
212 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800213 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800214
215 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
216 new WallpaperChangedNotifier.Listener() {
217 @Override
218 public void onWallpaperChanged() {
219 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
220 return;
221 }
222
223 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
224 mAdapter.mSelectedAdapterPosition);
225
226 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
227 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
228 if (selectedViewHolder instanceof SelectableHolder) {
229 ((SelectableHolder) selectedViewHolder).setSelectionState(
230 SelectableHolder.SELECTION_STATE_DESELECTED);
231 }
232 } else {
233 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
234 }
235 }
236 };
237 PackageStatusNotifier.Listener mAppStatusListener;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800238 BottomActionBar mBottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +0800239 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800240 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800241
chihhangchuangc687d912020-05-04 14:33:05 +0800242 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800243 private ProgressDialog mProgressDialog;
244 private boolean mTestingMode;
245 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800246 private SetIndividualHolder mPendingSetIndividualHolder;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800247 private ContentLoadingProgressBar mLoading;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800248
249 /**
250 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
251 * committing fragment transactions.
252 */
253 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
254 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
255
Jon Miranda16ea1b12017-12-12 14:52:48 -0800256 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800257
258 /**
259 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
260 * invocation of the fragment.
261 */
262 private boolean mWasUpdateRunnableRun;
263
264 /**
265 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
266 * mode.
267 */
268 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
269 @Override
270 public void run() {
271 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
272 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
273 if (viewHolder instanceof DesktopRotationHolder) {
274 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
275 } else { // viewHolder is null
276 // If the rotation tile is unavailable (because user has scrolled down, causing the
277 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
278 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
279 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
280 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
281 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
282 }
283 }
284 };
285
Chuck Liao69630f12020-03-05 19:01:25 +0800286 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800287 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800288 private WallpaperManager mWallpaperManager;
289 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800290 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800291
Jon Miranda16ea1b12017-12-12 14:52:48 -0800292 public static IndividualPickerFragment newInstance(String collectionId) {
293 Bundle args = new Bundle();
294 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
295
296 IndividualPickerFragment fragment = new IndividualPickerFragment();
297 fragment.setArguments(args);
298 return fragment;
299 }
300
“Chuck7ef99722020-03-22 04:34:03 +0800301 /**
302 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
303 * would be set.
304 *
305 * @param wallpaperDestination the destination a wallpaper would be set.
306 * It will be either {@link Destination#DEST_HOME_SCREEN}
307 * or {@link Destination#DEST_LOCK_SCREEN}.
308 */
309 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
310 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800311 if (mWallpapers != null) {
312 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800313 }
314 }
315
Jon Miranda16ea1b12017-12-12 14:52:48 -0800316 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
317 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
318 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
319 getActivity());
320 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
321 @Override
322 public void onDrawableLoaded() {
323 if (getActivity() == null) {
324 return;
325 }
326
327 // Schedule the next update of the thumbnail.
328 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
329 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
330 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
331 }
332 });
333 }
334
335 @Override
336 public void onCreate(Bundle savedInstanceState) {
337 super.onCreate(savedInstanceState);
338
339 Injector injector = InjectorProvider.getInjector();
340 Context appContext = getContext().getApplicationContext();
341 mWallpaperPreferences = injector.getPreferences(appContext);
342
343 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
344 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
345
“Chuck7ef99722020-03-22 04:34:03 +0800346 mWallpaperManager = WallpaperManager.getInstance(appContext);
347
Jon Miranda16ea1b12017-12-12 14:52:48 -0800348 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
349
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700350 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
351
chihhangchuangc687d912020-05-04 14:33:05 +0800352 mUserEventLogger = injector.getUserEventLogger(appContext);
353
“Chuckffd832c2020-03-22 02:15:58 +0800354 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800355 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800356 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800357 injector.getPreferences(appContext),
358 injector.getUserEventLogger(appContext),
359 false);
360
Jon Miranda16ea1b12017-12-12 14:52:48 -0800361 mWallpapers = new ArrayList<>();
362 mRandom = new Random();
363 mHandler = new Handler();
364
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700365 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
366 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
367 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
368 Glide.get(getContext()).clearMemory();
369 }
370
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700371 CategoryProvider categoryProvider = injector.getCategoryProvider(appContext);
372 categoryProvider.fetchCategories(new CategoryReceiver() {
373 @Override
374 public void onCategoryReceived(Category category) {
375 // Do nothing.
376 }
377
378 @Override
379 public void doneFetchingCategories() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800380 Category category = categoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700381 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800382 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800383 return;
384 }
385 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700386 if (mCategory == null) {
387 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
388
389 // The absence of this category in the CategoryProvider indicates a broken
390 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800391 getIndividualPickerFragmentHost().moveToPreviousFragment();
392 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
393 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700394 return;
395 }
396 onCategoryLoaded();
397 }
398 }, false);
399 }
400
Chuck Liaof6b4b192020-08-07 02:31:32 +0800401
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700402 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800403 if (getIndividualPickerFragmentHost() == null) {
404 return;
405 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800406 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
407 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
408 } else {
409 setTitle(mCategory.getTitle());
410 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700411 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao58e4a1c2020-05-22 11:35:35 +0800412 // Avoids the "rotation" action is not shown correctly
413 // in a rare case : onCategoryLoaded() is called after onBottomActionBarReady().
414 if (isRotationEnabled() && mBottomActionBar != null
415 && !mBottomActionBar.areActionsShown(ROTATION)) {
416 mBottomActionBar.showActions(ROTATION);
417 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700418 fetchWallpapers(false);
419
420 if (mCategory.supportsThirdParty()) {
421 mAppStatusListener = (packageName, status) -> {
422 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
423 mCategory.containsThirdParty(packageName)) {
424 fetchWallpapers(true);
425 }
426 };
427 mPackageStatusNotifier.addListener(mAppStatusListener,
428 WallpaperService.SERVICE_INTERFACE);
429 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700430
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700431 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700432 }
433
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800434 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700435 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800436 mIsWallpapersReceived = false;
437 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800438 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
439 @Override
440 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800441 mIsWallpapersReceived = true;
442 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800443 for (WallpaperInfo wallpaper : wallpapers) {
444 mWallpapers.add(wallpaper);
445 }
446
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700447 // Wallpapers may load after the adapter is initialized, in which case we have
448 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800449 if (mAdapter != null) {
450 mAdapter.notifyDataSetChanged();
451 }
452
453 if (mWallpapersUiContainer != null) {
454 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700455 } else {
456 if (wallpapers.isEmpty()) {
457 // If there are no more wallpapers and we're on phone, just finish the
458 // Activity.
459 Activity activity = getActivity();
460 if (activity != null
461 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
462 activity.finish();
463 }
464 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800465 }
466 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700467 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800468 }
469
Chuck Liaof6b4b192020-08-07 02:31:32 +0800470 void updateLoading() {
471 if (mLoading == null) {
472 return;
473 }
474
475 if (mIsWallpapersReceived) {
476 mLoading.hide();
477 } else {
478 mLoading.show();
479 }
480 }
481
Jon Miranda16ea1b12017-12-12 14:52:48 -0800482 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700483 public void onSaveInstanceState(@NonNull Bundle outState) {
484 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700485 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700486 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
487 }
488
489 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800490 public View onCreateView(LayoutInflater inflater, ViewGroup container,
491 Bundle savedInstanceState) {
492 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
Chuck Liao58aca1c2021-03-17 01:20:55 +0800493 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
494 view.findViewById(R.id.header_bar).setVisibility(View.GONE);
Kunhung Lid0174362021-04-05 15:31:41 +0800495 setUpArrowEnabled(/* upArrow= */ true);
Chuck Liao58aca1c2021-03-17 01:20:55 +0800496 } else {
497 setUpToolbar(view);
498 if (mCategory != null) {
499 setTitle(mCategory.getTitle());
500 }
501 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800502
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700503 mTileSizePx = SizeCalculator.getIndividualTileSize(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800504
505 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
506 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
507 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
508 updateImageGridPadding(false /* addExtraBottomSpace */);
509 mImageGrid.setScrollBarSize(gridPaddingPx);
510 }
Chuck Liaoba401232020-03-13 20:11:04 +0800511 mImageGrid.addItemDecoration(new GridPaddingDecoration(
512 getResources().getDimensionPixelSize(R.dimen.grid_padding)));
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +0800513 mImageGrid.setAccessibilityDelegateCompat(
514 new WallpaperPickerRecyclerViewAccessibilityDelegate(
515 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Chuck Liaof6b4b192020-08-07 02:31:32 +0800516 mLoading = view.findViewById(R.id.loading_indicator);
517 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700518 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800519 setUpBottomSheet();
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800520 // For nav bar edge-to-edge effect.
521 view.findViewById(R.id.wallpaper_grid).setOnApplyWindowInsetsListener((v, windowInsets) -> {
522 v.setPadding(
523 v.getPaddingLeft(),
524 v.getPaddingTop(),
525 v.getPaddingRight(),
526 windowInsets.getSystemWindowInsetBottom());
527 return windowInsets.consumeSystemWindowInsets();
528 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800529 return view;
530 }
531
532 @Override
533 public void onClickTryAgain(@Destination int unused) {
534 if (mPendingSetIndividualHolder != null) {
535 mPendingSetIndividualHolder.setWallpaper();
536 }
537 }
538
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800539 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800540 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
541 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
542 R.dimen.current_wallpaper_bottom_sheet_layout_height);
543 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
544 // Only left and top may be set in order for the GridMarginDecoration to work properly.
545 mImageGrid.setPadding(
546 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
547 }
548
Chuck Liaof6b4b192020-08-07 02:31:32 +0800549 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
Chuck Liao7e333722021-02-27 02:21:20 +0800550 Fragment parentFragment = getParentFragment();
551 if (parentFragment != null) {
552 return (IndividualPickerFragmentHost) parentFragment;
553 } else {
554 return (IndividualPickerFragmentHost) getActivity();
555 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800556 }
557
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700558 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700559 // Skip if mImageGrid been initialized yet
560 if (mImageGrid == null) {
561 return;
562 }
563 // Skip if category hasn't loaded yet
564 if (mCategory == null) {
565 return;
566 }
567 // Skip if the adapter was already created
568 if (mAdapter != null) {
569 return;
570 }
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700571 setUpImageGrid();
572 }
573
574 /**
575 * Create the adapter and assign it to mImageGrid.
576 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
577 */
578 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800579 mAdapter = new IndividualAdapter(mWallpapers);
580 mImageGrid.setAdapter(mAdapter);
581 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
582 }
583
Jon Miranda16ea1b12017-12-12 14:52:48 -0800584 /**
585 * Enables and populates the "Currently set" wallpaper BottomSheet.
586 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800587 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800588 mImageGrid.addOnScrollListener(new OnScrollListener() {
589 @Override
590 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
591 if (mCurrentWallpaperBottomSheetPresenter == null) {
592 return;
593 }
594
595 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
596 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
597 }
598 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
599 @Override
600 public void run() {
601 if (dy > 0) {
602 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
603 } else {
604 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
605 }
606 }
607 };
608 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
609 }
610 });
611 }
612
chihhangchuang3efb6832020-04-17 02:06:25 +0800613 @Override
614 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Kunhung Lic6701492021-01-28 17:35:56 +0800615 super.onBottomActionBarReady(bottomActionBar);
Chuck Liao342f4ab2020-05-26 18:28:09 +0800616 mBottomActionBar = bottomActionBar;
Kunhung Lic6701492021-01-28 17:35:56 +0800617 boolean isRotationEnabled = isRotationEnabled();
618 if (isRotationEnabled) {
Chuck Liao342f4ab2020-05-26 18:28:09 +0800619 mBottomActionBar.showActionsOnly(ROTATION);
Ching-Sung Li073812b2020-04-07 21:19:21 +0800620 }
Chuck Liao342f4ab2020-05-26 18:28:09 +0800621 mBottomActionBar.setActionClickListener(ROTATION, unused -> {
622 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
623 startRotationDialogFragment.setTargetFragment(
624 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
625 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
626 });
627 mBottomActionBar.setActionClickListener(APPLY, unused -> {
628 mBottomActionBar.disableActions();
629 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
630 mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
631 });
632
633 mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
634 .inflate(R.layout.wallpaper_info_view, /* root= */ null);
635 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
636 mBottomActionBar.setActionClickListener(EDIT, unused -> {
637 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
638 mSelectedWallpaperInfo.showPreview(getActivity(),
639 new PreviewActivity.PreviewActivityIntentFactory(),
640 PREVIEW_WALLPAPER_REQUEST_CODE);
641 });
Kunhung Lic6701492021-01-28 17:35:56 +0800642 if (isRotationEnabled || (mBottomActionBar.getBackButtonVisibility() == View.VISIBLE)) {
643 mBottomActionBar.show();
644 }
Ching-Sung Li073812b2020-04-07 21:19:21 +0800645 }
646
Jon Miranda16ea1b12017-12-12 14:52:48 -0800647 @Override
648 public void onResume() {
649 super.onResume();
650
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700651 WallpaperPreferences preferences = InjectorProvider.getInjector()
652 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800653 preferences.setLastAppActiveTimestamp(new Date().getTime());
654
655 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
656 // PreviewFragment.
657 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
658
659 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
660 // shown earlier when this fragment's hosting activity didn't allow committing fragment
661 // transactions.
662 if (mStagedStartRotationErrorDialogFragment != null) {
663 mStagedStartRotationErrorDialogFragment.show(
664 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
665 mStagedStartRotationErrorDialogFragment = null;
666 }
667
668 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
669 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
670 // committing fragment transactions.
671 if (mStagedSetWallpaperErrorDialogFragment != null) {
672 mStagedSetWallpaperErrorDialogFragment.show(
673 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
674 mStagedSetWallpaperErrorDialogFragment = null;
675 }
676
Chuck Liao5a4243b2020-05-20 23:56:39 +0800677 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
678 // Must be resuming from a previously stopped state, so re-schedule the update of the
679 // daily wallpapers tile thumbnail.
680 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800681 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800682 }
683
684 @Override
685 public void onStop() {
686 super.onStop();
687 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
688 }
689
690 @Override
691 public void onDestroy() {
692 super.onDestroy();
693 if (mProgressDialog != null) {
694 mProgressDialog.dismiss();
695 }
696 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700697 if (mAppStatusListener != null) {
698 mPackageStatusNotifier.removeListener(mAppStatusListener);
699 }
Chuck Liao69630f12020-03-05 19:01:25 +0800700 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800701 }
702
703 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800704 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800705 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
706 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
707 // still holds a reference to the destroyed Fragment and is calling
708 // onStartRotationDialogDismissed on that destroyed Fragment.
709 if (mBottomActionBar != null) {
710 mBottomActionBar.deselectAction(ROTATION);
711 }
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800712 }
713
714 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800715 public void retryStartRotation(@NetworkPreference int networkPreference) {
716 startRotation(networkPreference);
717 }
718
chihhangchuang7feb3752020-04-24 02:48:56 +0800719 @Override
720 public boolean onBackPressed() {
721 if (mSelectedWallpaperInfo != null) {
722 onWallpaperSelected(null, 0);
723 return true;
724 }
725 return false;
726 }
727
Jon Miranda16ea1b12017-12-12 14:52:48 -0800728 public void setCurrentWallpaperBottomSheetPresenter(
729 CurrentWallpaperBottomSheetPresenter presenter) {
730 mCurrentWallpaperBottomSheetPresenter = presenter;
731 }
732
733 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
734 mWallpapersUiContainer = uiContainer;
735 }
736
Chuck Liaob3829fb2020-04-01 00:47:50 +0800737 public void setOnWallpaperSelectedListener(
738 WallpaperSelectedListener wallpaperSelectedListener) {
739 mWallpaperSelectedListener = wallpaperSelectedListener;
740 }
741
742 /**
743 * Resizes the layout's height.
744 */
745 public void resizeLayout(int height) {
746 mImageGrid.getLayoutParams().height = height;
747 mImageGrid.requestLayout();
748 }
749
750 /**
751 * Scrolls to the specific item.
752 *
753 * @param position the position of the item
754 */
755 public void scrollToPosition(int position) {
756 ((GridLayoutManager) mImageGrid.getLayoutManager())
757 .scrollToPositionWithOffset(position, /* offset= */ 0);
758 }
759
Jon Miranda16ea1b12017-12-12 14:52:48 -0800760 /**
761 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
762 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
763 * constantly keeps the UI thread alive and blocks a test forever.
764 *
765 * @param testingMode
766 */
767 void setTestingMode(boolean testingMode) {
768 mTestingMode = testingMode;
769 }
770
Jon Miranda16ea1b12017-12-12 14:52:48 -0800771 @Override
772 public void startRotation(@NetworkPreference final int networkPreference) {
773 if (!isRotationEnabled()) {
774 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
775 return;
776 }
777
778 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
779 // causes Espresso to hang once the dialog is shown.
780 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
781 int themeResId;
782 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
783 themeResId = R.style.ProgressDialogThemePreL;
784 } else {
785 themeResId = R.style.LightDialogTheme;
786 }
787 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
788
789 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
790 mProgressDialog.setMessage(
791 getResources().getString(R.string.start_rotation_progress_message));
792 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
793 mProgressDialog.show();
794 }
795
796 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
797 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
798 }
799
800 final Context appContext = getActivity().getApplicationContext();
801
802 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
803 appContext,
804 networkPreference,
805 new Listener() {
806 @Override
807 public void onFirstWallpaperInRotationSet() {
808 if (mProgressDialog != null) {
809 mProgressDialog.dismiss();
810 }
811
812 // The fragment may be detached from its containing activity if the user exits the
813 // app before the first wallpaper image in rotation finishes downloading.
814 Activity activity = getActivity();
815
Jon Miranda16ea1b12017-12-12 14:52:48 -0800816
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700817 if (mWallpaperRotationInitializer.startRotation(appContext)) {
818 if (activity != null
819 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
820 try {
821 Toast.makeText(getActivity(),
822 R.string.wallpaper_set_successfully_message,
823 Toast.LENGTH_SHORT).show();
824 } catch (NotFoundException e) {
825 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800826 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800827
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700828 activity.setResult(Activity.RESULT_OK);
829 activity.finish();
830 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
831 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
832 }
833 } else { // Failed to start rotation.
834 showStartRotationErrorDialog(networkPreference);
835
836 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
837 DesktopRotationHolder rotationViewHolder =
838 (DesktopRotationHolder)
839 mImageGrid.findViewHolderForAdapterPosition(
840 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
841 rotationViewHolder.setSelectionState(
842 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800843 }
844 }
845 }
846
847 @Override
848 public void onError() {
849 if (mProgressDialog != null) {
850 mProgressDialog.dismiss();
851 }
852
853 showStartRotationErrorDialog(networkPreference);
854
855 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
856 DesktopRotationHolder rotationViewHolder =
857 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
858 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
859 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
860 }
861 }
862 });
863 }
864
865 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800866 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800867 if (activity != null) {
868 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
869 StartRotationErrorDialogFragment.newInstance(networkPreference);
870 startRotationErrorDialogFragment.setTargetFragment(
871 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
872
873 if (activity.isSafeToCommitFragmentTransaction()) {
874 startRotationErrorDialogFragment.show(
875 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
876 } else {
877 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
878 }
879 }
880 }
881
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800882 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800883 Activity activity = getActivity();
Chuck Liaoe2fe0302020-06-29 21:15:35 +0800884 return activity == null ? 1 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800885 }
886
887 /**
888 * Returns whether rotation is enabled for this category.
889 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800890 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700891 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800892 }
893
894 @Override
895 public void onCurrentWallpaperRefreshed() {
896 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
897 }
898
Chuck Liao69630f12020-03-05 19:01:25 +0800899 @Override
900 public void onSet(int destination) {
901 if (mSelectedWallpaperInfo == null) {
902 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
903 return;
904 }
905
“Chuckffd832c2020-03-22 02:15:58 +0800906 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800907 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
908 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
909 destination, 0, null, mSetWallpaperCallback);
910 } else {
911 mWallpaperSetter.setCurrentWallpaper(
912 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
913 }
“Chuck7ef99722020-03-22 04:34:03 +0800914 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800915 }
916
917 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
918 new WallpaperPersister.SetWallpaperCallback() {
919 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800920 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800921 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800922 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
923 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800924 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
925 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800926 }
927
928 @Override
929 public void onError(@Nullable Throwable throwable) {
930 Log.e(TAG, "Can't apply the wallpaper.");
931 mBottomActionBar.enableActions();
932 }
933 };
934
935 @Override
936 public void onDialogDismissed(boolean withItemSelected) {
937 if (!withItemSelected) {
938 mBottomActionBar.enableActions();
939 }
940 }
941
Jon Miranda16ea1b12017-12-12 14:52:48 -0800942 /**
943 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
944 */
945 private void showSetWallpaperErrorDialog() {
946 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
947 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
948 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
949
950 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
951 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
952 } else {
953 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
954 }
955 }
956
Ching-Sung Li073812b2020-04-07 21:19:21 +0800957 void updateBottomActions(boolean hasWallpaperSelected) {
chihhangchuang803ea9a2020-04-21 13:03:10 +0800958 if (hasWallpaperSelected) {
Chuck Liao7dc21572020-05-14 18:37:36 +0800959 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800960 } else {
chihhangchuang08abb582020-04-27 17:20:31 +0800961 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800962 }
Chuck Liao69630f12020-03-05 19:01:25 +0800963 }
964
965 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800966 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800967 if (thumbnailUpdater == null) {
968 return;
969 }
970
971 if (selectedWallpaperInfo != null) {
972 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
973 } else {
974 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800975 }
976 }
977
“Chuck7ef99722020-03-22 04:34:03 +0800978 private void onWallpaperDestinationSet(int destination) {
979 WallpaperDestinationCallback wallpaperDestinationCallback =
980 (WallpaperDestinationCallback) getParentFragment();
981 if (wallpaperDestinationCallback == null) {
982 return;
983 }
984
985 wallpaperDestinationCallback.onDestinationSet(destination);
986 }
987
Ching-Sung Li073812b2020-04-07 21:19:21 +0800988 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800989 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800990 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800991 return;
992 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800993 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +0800994 updateActivatedStatus(mSelectedWallpaperInfo == null
995 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800996 // Update new selected wallpaper.
997 updateActivatedStatus(newSelectedWallpaperInfo == null
998 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
999
1000 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
1001 updateBottomActions(mSelectedWallpaperInfo != null);
1002 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +08001003 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +08001004 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +08001005 WallpaperInfoHelper.loadExploreIntent(
1006 getContext(),
1007 mSelectedWallpaperInfo,
1008 (actionLabel, exploreIntent) ->
1009 mWallpaperInfoView.populateWallpaperInfo(
Chuck Liao9991d682021-01-20 17:45:14 +08001010 mSelectedWallpaperInfo,
1011 actionLabel,
1012 WallpaperInfoHelper.shouldShowExploreButton(
1013 getContext(), exploreIntent),
1014 v -> onExploreClicked(exploreIntent))
1015 );
Chuck Liaob3829fb2020-04-01 00:47:50 +08001016 }
1017
1018 if (mWallpaperSelectedListener != null) {
1019 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001020 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001021 }
1022
chihhangchuangc687d912020-05-04 14:33:05 +08001023 private void onExploreClicked(Intent exploreIntent) {
1024 if (getContext() == null) {
1025 return;
1026 }
1027 Context context = getContext();
1028 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
1029 mSelectedWallpaperInfo.getActionLabelRes(context));
1030
1031 startActivity(exploreIntent);
1032 }
1033
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001034 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +08001035 if (wallpaperInfo == null) {
1036 return;
1037 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001038 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001039 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001040 ? index + 1 : index;
1041 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1042 if (holder != null) {
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001043 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001044 thumbnail.setClipped(isActivated);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001045 } else {
1046 // Item is not visible, make sure the item is re-bound when it becomes visible.
1047 mAdapter.notifyItemChanged(index);
1048 }
1049 }
1050
“Chuckffd832c2020-03-22 02:15:58 +08001051 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1052 if (wallpaperInfo == null) {
1053 return;
1054 }
1055 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001056 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001057 ? index + 1 : index;
1058 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001059 if (holder == null) {
“Chuckffd832c2020-03-22 02:15:58 +08001060 // Item is not visible, make sure the item is re-bound when it becomes visible.
1061 mAdapter.notifyItemChanged(index);
1062 }
1063 }
1064
Chuck Liao11f4a762020-04-08 13:24:43 +08001065 private void refreshAppliedWallpaper() {
1066 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1067 showCheckMarkAndBorderForAppliedWallpaper(false);
1068
1069 // Update to the new applied wallpaper.
1070 String appliedWallpaperId = getAppliedWallpaperId();
1071 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1072 .stream()
1073 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1074 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1075 .findFirst();
1076 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1077
1078 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1079 showCheckMarkAndBorderForAppliedWallpaper(true);
1080 }
1081
1082 private String getAppliedWallpaperId() {
1083 WallpaperPreferences prefs =
1084 InjectorProvider.getInjector().getPreferences(getContext());
1085 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1086 boolean isDestinationBoth =
1087 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1088
1089 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1090 return wallpaperInfo != null
1091 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1092 } else {
1093 return prefs.getLockWallpaperRemoteId();
1094 }
1095 }
1096
1097 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1098 updateAppliedStatus(mAppliedWallpaperInfo, show);
1099 if (mSelectedWallpaperInfo == null) {
1100 updateActivatedStatus(mAppliedWallpaperInfo, show);
1101 }
1102 }
1103
Chuck Liao5a4243b2020-05-20 23:56:39 +08001104 private boolean shouldShowRotationTile() {
1105 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001106 }
1107
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001108 class EmptySelectionAnimator implements SelectionAnimator{
1109 EmptySelectionAnimator() {}
1110
1111 public boolean isSelected() {
1112 return false;
1113 }
1114
1115 /**
1116 * Sets the UI to selected immediately with no animation.
1117 */
1118 public void selectImmediately() {}
1119
1120 /**
1121 * Sets the UI to deselected immediately with no animation.
1122 */
1123 public void deselectImmediately() {}
1124
1125 /**
1126 * Sets the UI to selected with a smooth animation.
1127 */
1128 public void animateSelected() {}
1129
1130 /**
1131 * Sets the UI to deselected with a smooth animation.
1132 */
1133 public void animateDeselected() {}
1134
1135 /**
1136 * Sets the UI to show a loading indicator.
1137 */
1138 public void showLoading() {}
1139
1140 /**
1141 * Sets the UI to hide the loading indicator.
1142 */
1143 public void showNotLoading() {}
1144
1145 }
1146
Jon Miranda16ea1b12017-12-12 14:52:48 -08001147 /**
1148 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1149 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001150 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1151 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1152 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1153 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001154
1155 private final List<WallpaperInfo> mWallpapers;
1156
1157 private int mPendingSelectedAdapterPosition;
1158 private int mSelectedAdapterPosition;
1159
1160 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1161 mWallpapers = wallpapers;
1162 mPendingSelectedAdapterPosition = -1;
1163 mSelectedAdapterPosition = -1;
1164 }
1165
1166 @Override
1167 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1168 switch (viewType) {
1169 case ITEM_VIEW_TYPE_ROTATION:
1170 return createRotationHolder(parent);
1171 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1172 return createIndividualHolder(parent);
1173 case ITEM_VIEW_TYPE_MY_PHOTOS:
1174 return createMyPhotosHolder(parent);
1175 default:
1176 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1177 return null;
1178 }
1179 }
1180
1181 @Override
1182 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001183 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001184 return ITEM_VIEW_TYPE_ROTATION;
1185 }
1186
1187 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1188 if (mCategory.supportsCustomPhotos()
1189 && !isRotationEnabled()
1190 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1191 return ITEM_VIEW_TYPE_MY_PHOTOS;
1192 }
1193
1194 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1195 }
1196
1197 @Override
1198 public void onBindViewHolder(ViewHolder holder, int position) {
1199 int viewType = getItemViewType(position);
1200
1201 switch (viewType) {
1202 case ITEM_VIEW_TYPE_ROTATION:
1203 onBindRotationHolder(holder, position);
1204 break;
1205 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1206 onBindIndividualHolder(holder, position);
1207 break;
1208 case ITEM_VIEW_TYPE_MY_PHOTOS:
1209 ((MyPhotosViewHolder) holder).bind();
1210 break;
1211 default:
1212 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1213 }
1214 }
1215
1216 @Override
1217 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001218 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001219 ? mWallpapers.size() + 1
1220 : mWallpapers.size();
1221 }
1222
1223 private ViewHolder createRotationHolder(ViewGroup parent) {
1224 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001225 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001226 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Chuck Liao5a4243b2020-05-20 23:56:39 +08001227 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1228 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001229 }
1230
1231 private ViewHolder createIndividualHolder(ViewGroup parent) {
1232 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1233 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1234
1235 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001236 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001237 return new SetIndividualHolder(
1238 getActivity(), mTileSizePx.y, view,
1239 selectionAnimator,
1240 new OnSetListener() {
1241 @Override
1242 public void onPendingWallpaperSet(int adapterPosition) {
1243 // Deselect and hide loading indicator for any previously pending tile.
1244 if (mPendingSelectedAdapterPosition != -1) {
1245 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1246 mPendingSelectedAdapterPosition);
1247 if (oldViewHolder instanceof SelectableHolder) {
1248 ((SelectableHolder) oldViewHolder).setSelectionState(
1249 SelectableHolder.SELECTION_STATE_DESELECTED);
1250 }
1251 }
1252
1253 if (mSelectedAdapterPosition != -1) {
1254 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1255 mSelectedAdapterPosition);
1256 if (oldViewHolder instanceof SelectableHolder) {
1257 ((SelectableHolder) oldViewHolder).setSelectionState(
1258 SelectableHolder.SELECTION_STATE_DESELECTED);
1259 }
1260 }
1261
1262 mPendingSelectedAdapterPosition = adapterPosition;
1263 }
1264
1265 @Override
1266 public void onWallpaperSet(int adapterPosition) {
1267 // No-op -- UI handles a new wallpaper being set by reacting to the
1268 // WallpaperChangedNotifier.
1269 }
1270
1271 @Override
1272 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1273 showSetWallpaperErrorDialog();
1274 mPendingSetIndividualHolder = holder;
1275 }
1276 });
1277 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001278 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001279 }
1280 }
1281
1282 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1283 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1284 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1285
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001286 return new MyPhotosViewHolder(getActivity(),
1287 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1288 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001289 }
1290
1291 /**
1292 * Marks the tile at the given position as selected with a visual indication. Also updates the
1293 * "currently selected" BottomSheet to reflect the newly selected tile.
1294 */
1295 private void updateSelectedTile(int newlySelectedPosition) {
1296 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1297 // succession.
1298 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1299 return;
1300 }
1301
1302 if (mCurrentWallpaperBottomSheetPresenter != null) {
1303 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1304 IndividualPickerFragment.this);
1305
1306 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1307 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1308 }
1309 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1310 @Override
1311 public void run() {
1312 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1313 }
1314 };
1315 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1316 }
1317
1318 // User may have switched to another category, thus detaching this fragment, so check here.
1319 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1320 // still occurs in the UI after the user selects that other category.
1321 if (getActivity() == null) {
1322 return;
1323 }
1324
1325 // Update the newly selected wallpaper ViewHolder and the old one so that if
1326 // selection UI state applies (desktop UI), it is updated.
1327 if (mSelectedAdapterPosition >= 0) {
1328 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1329 mSelectedAdapterPosition);
1330 if (oldViewHolder instanceof SelectableHolder) {
1331 ((SelectableHolder) oldViewHolder).setSelectionState(
1332 SelectableHolder.SELECTION_STATE_DESELECTED);
1333 }
1334 }
1335
1336 // Animate selection of newly selected tile.
1337 ViewHolder newViewHolder = mImageGrid
1338 .findViewHolderForAdapterPosition(newlySelectedPosition);
1339 if (newViewHolder instanceof SelectableHolder) {
1340 ((SelectableHolder) newViewHolder).setSelectionState(
1341 SelectableHolder.SELECTION_STATE_SELECTED);
1342 }
1343
1344 mSelectedAdapterPosition = newlySelectedPosition;
1345
1346 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1347 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1348 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1349 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1350 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1351 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1352
1353 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1354 }
1355
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001356 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001357 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1358 String collectionId = mCategory.getCollectionId();
1359 ((DesktopRotationHolder) holder).bind(collectionId);
1360
1361 if (mWallpaperPreferences.getWallpaperPresentationMode()
1362 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1363 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1364 mSelectedAdapterPosition = position;
1365 }
1366
1367 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1368 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1369 mWasUpdateRunnableRun = true;
1370 }
1371 }
1372 }
1373
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001374 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001375 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001376 ? position - 1 : position;
1377 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1378 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001379 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001380 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1381 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1382 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001383
“Chuckffd832c2020-03-22 02:15:58 +08001384 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001385 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001386 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001387 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001388
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001389 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001390 thumbnail.setClipped(isWallpaperApplied);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001391 }
1392 }
Chuck Liaoba401232020-03-13 20:11:04 +08001393
1394 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1395
1396 private int mPadding;
1397
1398 GridPaddingDecoration(int padding) {
1399 mPadding = padding;
1400 }
1401
1402 @Override
1403 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1404 RecyclerView.State state) {
1405 int position = parent.getChildAdapterPosition(view);
1406 if (position >= 0) {
1407 outRect.left = mPadding;
1408 outRect.right = mPadding;
1409 }
1410 }
1411 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001412}