blob: 60c0fc8f329b065edb10250118060682c94f6d94 [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.wallpaper.picker.individual;
17
Chuck Liaoa7215812021-04-13 22:33:43 +080018import android.annotation.MenuRes;
Jon Miranda16ea1b12017-12-12 14:52:48 -080019import android.app.Activity;
20import android.app.ProgressDialog;
“Chuck7ef99722020-03-22 04:34:03 +080021import android.app.WallpaperManager;
Jon Miranda16ea1b12017-12-12 14:52:48 -080022import android.content.Context;
Chihhang Chuang85f099a2020-06-16 18:04:39 +080023import android.content.DialogInterface;
chihhangchuangc687d912020-05-04 14:33:05 +080024import android.content.Intent;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070025import android.content.res.Configuration;
Jon Miranda16ea1b12017-12-12 14:52:48 -080026import android.content.res.Resources.NotFoundException;
27import android.graphics.Point;
Chuck Liaoba401232020-03-13 20:11:04 +080028import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080029import android.os.Build.VERSION;
30import android.os.Build.VERSION_CODES;
31import android.os.Bundle;
32import android.os.Handler;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070033import android.service.wallpaper.WallpaperService;
Chuck Liaoec053b02021-04-24 01:30:50 +080034import android.text.TextUtils;
35import android.util.ArraySet;
Jon Miranda16ea1b12017-12-12 14:52:48 -080036import android.util.Log;
37import android.view.LayoutInflater;
Chuck Liao33f1af42021-04-13 20:52:35 +080038import android.view.MenuItem;
Jon Miranda16ea1b12017-12-12 14:52:48 -080039import android.view.View;
40import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080041import android.widget.Toast;
42
Santiago Etchebehereb1854472019-06-06 17:44:54 -070043import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080044import androidx.annotation.Nullable;
Ching-Sung Li1ac024f2021-06-08 14:43:28 +080045import androidx.cardview.widget.CardView;
Chuck Liaof6b4b192020-08-07 02:31:32 +080046import androidx.core.widget.ContentLoadingProgressBar;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080047import androidx.fragment.app.DialogFragment;
Chuck Liao7e333722021-02-27 02:21:20 +080048import androidx.fragment.app.Fragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080049import androidx.recyclerview.widget.GridLayoutManager;
50import androidx.recyclerview.widget.RecyclerView;
51import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
52import androidx.recyclerview.widget.RecyclerView.ViewHolder;
53
Jon Miranda16ea1b12017-12-12 14:52:48 -080054import com.android.wallpaper.R;
55import com.android.wallpaper.asset.Asset;
56import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070057import com.android.wallpaper.model.Category;
58import com.android.wallpaper.model.CategoryProvider;
59import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080060import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080061import com.android.wallpaper.model.WallpaperCategory;
62import com.android.wallpaper.model.WallpaperInfo;
63import com.android.wallpaper.model.WallpaperReceiver;
64import com.android.wallpaper.model.WallpaperRotationInitializer;
65import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
66import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080067import com.android.wallpaper.module.FormFactorChecker;
68import com.android.wallpaper.module.FormFactorChecker.FormFactor;
69import com.android.wallpaper.module.Injector;
70import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070071import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080072import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080073import com.android.wallpaper.module.WallpaperChangedNotifier;
74import com.android.wallpaper.module.WallpaperPersister;
75import com.android.wallpaper.module.WallpaperPersister.Destination;
76import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080077import com.android.wallpaper.module.WallpaperSetter;
Chuck Liao58aca1c2021-03-17 01:20:55 +080078import com.android.wallpaper.picker.AppbarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080079import com.android.wallpaper.picker.BaseActivity;
80import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Chuck Liaob17d1502020-09-07 21:55:30 +080081import com.android.wallpaper.picker.FragmentTransactionChecker;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080082import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -080083import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080084import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080085import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
86import com.android.wallpaper.picker.StartRotationDialogFragment;
87import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080088import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080089import com.android.wallpaper.picker.WallpapersUiContainer;
90import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
91import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070092import com.android.wallpaper.util.SizeCalculator;
chihhangchuang1a29e752020-04-28 18:22:53 +080093import com.android.wallpaper.widget.WallpaperInfoView;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080094import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
95import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070096
Jon Miranda16ea1b12017-12-12 14:52:48 -080097import com.bumptech.glide.Glide;
98import com.bumptech.glide.MemoryCategory;
99
100import java.util.ArrayList;
101import java.util.Date;
102import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800103import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800104import java.util.Random;
Chuck Liaoec053b02021-04-24 01:30:50 +0800105import java.util.Set;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800106
107/**
108 * Displays the Main UI for picking an individual wallpaper image.
109 */
Chuck Liao58aca1c2021-03-17 01:20:55 +0800110public class IndividualPickerFragment extends AppbarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800111 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
112 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800113 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
114 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800115
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800116 /**
117 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
118 * such as "my photos" or "daily rotation".
119 */
120 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
121 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
122
Jon Miranda16ea1b12017-12-12 14:52:48 -0800123 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800124 private static final int UNUSED_REQUEST_CODE = 1;
125 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
126 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
127 private static final String PROGRESS_DIALOG_NO_TITLE = null;
128 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
129 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
130 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700131 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Chuck Liaob10d2a32021-04-27 20:59:53 +0800132 private static final int MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT = 8;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800133
Chuck Liao8ec38e02020-02-26 20:59:32 +0800134 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800135 * An interface for updating the thumbnail with the specific wallpaper.
136 */
137 public interface ThumbnailUpdater {
138 /**
139 * Updates the thumbnail with the specific wallpaper.
140 */
141 void updateThumbnail(WallpaperInfo wallpaperInfo);
142
143 /**
144 * Restores to the thumbnails of the wallpapers which were applied.
145 */
146 void restoreThumbnails();
147 }
148
“Chuck7ef99722020-03-22 04:34:03 +0800149 /**
150 * An interface for receiving the destination of the new applied wallpaper.
151 */
152 public interface WallpaperDestinationCallback {
153 /**
154 * Called when the destination of the wallpaper is set.
155 *
156 * @param destination the destination which a wallpaper may be set.
157 * See {@link Destination} for more details.
158 */
159 void onDestinationSet(@Destination int destination);
160 }
161
Chuck Liaob3829fb2020-04-01 00:47:50 +0800162 /**
163 * The listener which will be notified when the wallpaper is selected.
164 */
165 public interface WallpaperSelectedListener {
166 /**
167 * Called when the wallpaper is selected.
168 *
169 * @param position the position of the selected wallpaper
170 */
171 void onWallpaperSelected(int position);
172 }
173
Chuck Liaof6b4b192020-08-07 02:31:32 +0800174 /**
Chuck Liao7e333722021-02-27 02:21:20 +0800175 * Interface to be implemented by a Fragment(or an Activity) hosting
176 * a {@link IndividualPickerFragment}.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800177 */
178 public interface IndividualPickerFragmentHost {
179 /**
Chuck Liao58aca1c2021-03-17 01:20:55 +0800180 * Indicates if the host has toolbar to show the title. If it does, we should set the title
181 * there.
182 */
183 boolean isHostToolbarShown();
184
185 /**
186 * Sets the title in the host's toolbar.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800187 */
188 void setToolbarTitle(CharSequence title);
189
190 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800191 * Configures the menu in the toolbar.
192 *
193 * @param menuResId the resource id of the menu
194 */
195 void setToolbarMenu(@MenuRes int menuResId);
196
197 /**
198 * Removes the menu in the toolbar.
199 */
200 void removeToolbarMenu();
201
202 /**
Chuck Liaof6b4b192020-08-07 02:31:32 +0800203 * Moves to the previous fragment.
204 */
205 void moveToPreviousFragment();
206 }
207
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800208 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800209 WallpaperPreferences mWallpaperPreferences;
210 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800211 RecyclerView mImageGrid;
212 IndividualAdapter mAdapter;
213 WallpaperCategory mCategory;
214 WallpaperRotationInitializer mWallpaperRotationInitializer;
215 List<WallpaperInfo> mWallpapers;
216 Point mTileSizePx;
217 WallpapersUiContainer mWallpapersUiContainer;
218 @FormFactor
219 int mFormFactor;
220 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800221
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800222 Handler mHandler;
223 Random mRandom;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800224 boolean mIsWallpapersReceived;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800225
226 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
227 new WallpaperChangedNotifier.Listener() {
228 @Override
229 public void onWallpaperChanged() {
230 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
231 return;
232 }
233
234 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
235 mAdapter.mSelectedAdapterPosition);
236
237 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
238 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
239 if (selectedViewHolder instanceof SelectableHolder) {
240 ((SelectableHolder) selectedViewHolder).setSelectionState(
241 SelectableHolder.SELECTION_STATE_DESELECTED);
242 }
243 } else {
244 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
245 }
246 }
247 };
248 PackageStatusNotifier.Listener mAppStatusListener;
chihhangchuang1a29e752020-04-28 18:22:53 +0800249 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800250 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800251
chihhangchuangc687d912020-05-04 14:33:05 +0800252 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800253 private ProgressDialog mProgressDialog;
254 private boolean mTestingMode;
255 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800256 private SetIndividualHolder mPendingSetIndividualHolder;
Chuck Liaof6b4b192020-08-07 02:31:32 +0800257 private ContentLoadingProgressBar mLoading;
Chuck Liaoddf2b522021-04-15 00:36:25 +0800258 private CategoryProvider mCategoryProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800259
260 /**
261 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
262 * committing fragment transactions.
263 */
264 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
265 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
266
Jon Miranda16ea1b12017-12-12 14:52:48 -0800267 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800268
269 /**
270 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
271 * invocation of the fragment.
272 */
273 private boolean mWasUpdateRunnableRun;
274
275 /**
276 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
277 * mode.
278 */
279 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
280 @Override
281 public void run() {
282 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
283 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
284 if (viewHolder instanceof DesktopRotationHolder) {
285 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
286 } else { // viewHolder is null
287 // If the rotation tile is unavailable (because user has scrolled down, causing the
288 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
289 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
290 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
291 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
292 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
293 }
294 }
295 };
296
Chuck Liao69630f12020-03-05 19:01:25 +0800297 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800298 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800299 private WallpaperManager mWallpaperManager;
300 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800301 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liaoec053b02021-04-24 01:30:50 +0800302 private Set<String> mAppliedWallpaperIds;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800303
Jon Miranda16ea1b12017-12-12 14:52:48 -0800304 public static IndividualPickerFragment newInstance(String collectionId) {
305 Bundle args = new Bundle();
306 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
307
308 IndividualPickerFragment fragment = new IndividualPickerFragment();
309 fragment.setArguments(args);
310 return fragment;
311 }
312
“Chuck7ef99722020-03-22 04:34:03 +0800313 /**
314 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
315 * would be set.
316 *
317 * @param wallpaperDestination the destination a wallpaper would be set.
318 * It will be either {@link Destination#DEST_HOME_SCREEN}
319 * or {@link Destination#DEST_LOCK_SCREEN}.
320 */
321 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
322 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800323 if (mWallpapers != null) {
324 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800325 }
326 }
327
Jon Miranda16ea1b12017-12-12 14:52:48 -0800328 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
329 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
330 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
331 getActivity());
332 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
333 @Override
334 public void onDrawableLoaded() {
335 if (getActivity() == null) {
336 return;
337 }
338
339 // Schedule the next update of the thumbnail.
340 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
341 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
342 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
343 }
344 });
345 }
346
347 @Override
348 public void onCreate(Bundle savedInstanceState) {
349 super.onCreate(savedInstanceState);
350
351 Injector injector = InjectorProvider.getInjector();
352 Context appContext = getContext().getApplicationContext();
353 mWallpaperPreferences = injector.getPreferences(appContext);
354
355 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
356 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
357
“Chuck7ef99722020-03-22 04:34:03 +0800358 mWallpaperManager = WallpaperManager.getInstance(appContext);
359
Jon Miranda16ea1b12017-12-12 14:52:48 -0800360 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
361
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700362 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
363
chihhangchuangc687d912020-05-04 14:33:05 +0800364 mUserEventLogger = injector.getUserEventLogger(appContext);
365
“Chuckffd832c2020-03-22 02:15:58 +0800366 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800367 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800368 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800369 injector.getPreferences(appContext),
370 injector.getUserEventLogger(appContext),
371 false);
372
Jon Miranda16ea1b12017-12-12 14:52:48 -0800373 mWallpapers = new ArrayList<>();
374 mRandom = new Random();
375 mHandler = new Handler();
376
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700377 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
378 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
379 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
380 Glide.get(getContext()).clearMemory();
381 }
382
Chuck Liaoddf2b522021-04-15 00:36:25 +0800383 mCategoryProvider = injector.getCategoryProvider(appContext);
384 mCategoryProvider.fetchCategories(new CategoryReceiver() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700385 @Override
386 public void onCategoryReceived(Category category) {
387 // Do nothing.
388 }
389
390 @Override
391 public void doneFetchingCategories() {
Chuck Liaoddf2b522021-04-15 00:36:25 +0800392 Category category = mCategoryProvider.getCategory(
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700393 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
Chuck Liaof44433e2020-09-21 22:58:26 +0800394 if (category != null && !(category instanceof WallpaperCategory)) {
Chuck Liao7e36d172020-09-18 01:09:17 +0800395 return;
396 }
397 mCategory = (WallpaperCategory) category;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700398 if (mCategory == null) {
399 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
400
401 // The absence of this category in the CategoryProvider indicates a broken
402 // state, see b/38030129. Hence, finish the activity and return.
Chuck Liaof6b4b192020-08-07 02:31:32 +0800403 getIndividualPickerFragmentHost().moveToPreviousFragment();
404 Toast.makeText(getContext(), R.string.collection_not_exist_msg,
405 Toast.LENGTH_SHORT).show();
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700406 return;
407 }
408 onCategoryLoaded();
409 }
410 }, false);
411 }
412
Chuck Liaof6b4b192020-08-07 02:31:32 +0800413
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700414 protected void onCategoryLoaded() {
Chuck Liao7e36d172020-09-18 01:09:17 +0800415 if (getIndividualPickerFragmentHost() == null) {
416 return;
417 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800418 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
419 getIndividualPickerFragmentHost().setToolbarTitle(mCategory.getTitle());
420 } else {
421 setTitle(mCategory.getTitle());
422 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700423 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao33f1af42021-04-13 20:52:35 +0800424 if (mToolbar != null && isRotationEnabled()) {
425 setUpToolbarMenu(R.menu.individual_picker_menu);
426 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700427 fetchWallpapers(false);
428
429 if (mCategory.supportsThirdParty()) {
430 mAppStatusListener = (packageName, status) -> {
431 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
432 mCategory.containsThirdParty(packageName)) {
433 fetchWallpapers(true);
434 }
435 };
436 mPackageStatusNotifier.addListener(mAppStatusListener,
437 WallpaperService.SERVICE_INTERFACE);
438 }
439 }
440
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800441 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700442 mWallpapers.clear();
Chuck Liaof6b4b192020-08-07 02:31:32 +0800443 mIsWallpapersReceived = false;
444 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800445 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
446 @Override
447 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
Chuck Liaof6b4b192020-08-07 02:31:32 +0800448 mIsWallpapersReceived = true;
449 updateLoading();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800450 for (WallpaperInfo wallpaper : wallpapers) {
451 mWallpapers.add(wallpaper);
452 }
Chuck Liaob10d2a32021-04-27 20:59:53 +0800453 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800454
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700455 // Wallpapers may load after the adapter is initialized, in which case we have
456 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800457 if (mAdapter != null) {
458 mAdapter.notifyDataSetChanged();
459 }
460
461 if (mWallpapersUiContainer != null) {
462 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700463 } else {
464 if (wallpapers.isEmpty()) {
465 // If there are no more wallpapers and we're on phone, just finish the
466 // Activity.
467 Activity activity = getActivity();
468 if (activity != null
469 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
470 activity.finish();
471 }
472 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800473 }
474 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700475 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800476 }
477
Chuck Liaof6b4b192020-08-07 02:31:32 +0800478 void updateLoading() {
479 if (mLoading == null) {
480 return;
481 }
482
483 if (mIsWallpapersReceived) {
484 mLoading.hide();
485 } else {
486 mLoading.show();
487 }
488 }
489
Jon Miranda16ea1b12017-12-12 14:52:48 -0800490 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700491 public void onSaveInstanceState(@NonNull Bundle outState) {
492 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700493 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700494 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
495 }
496
497 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800498 public View onCreateView(LayoutInflater inflater, ViewGroup container,
499 Bundle savedInstanceState) {
500 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
Chuck Liao58aca1c2021-03-17 01:20:55 +0800501 if (getIndividualPickerFragmentHost().isHostToolbarShown()) {
502 view.findViewById(R.id.header_bar).setVisibility(View.GONE);
Kunhung Lid0174362021-04-05 15:31:41 +0800503 setUpArrowEnabled(/* upArrow= */ true);
Chuck Liaoa7215812021-04-13 22:33:43 +0800504 if (isRotationEnabled()) {
505 getIndividualPickerFragmentHost().setToolbarMenu(R.menu.individual_picker_menu);
506 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800507 } else {
508 setUpToolbar(view);
Chuck Liao33f1af42021-04-13 20:52:35 +0800509 if (isRotationEnabled()) {
510 setUpToolbarMenu(R.menu.individual_picker_menu);
511 }
Chuck Liao58aca1c2021-03-17 01:20:55 +0800512 if (mCategory != null) {
513 setTitle(mCategory.getTitle());
514 }
515 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800516
Chuck Liaoec053b02021-04-24 01:30:50 +0800517 mAppliedWallpaperIds = getAppliedWallpaperIds();
518
Jon Miranda16ea1b12017-12-12 14:52:48 -0800519 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
520 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
521 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
522 updateImageGridPadding(false /* addExtraBottomSpace */);
523 mImageGrid.setScrollBarSize(gridPaddingPx);
524 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800525 mLoading = view.findViewById(R.id.loading_indicator);
526 updateLoading();
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700527 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800528 setUpBottomSheet();
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800529 // For nav bar edge-to-edge effect.
Kunhung Li1b0cb472021-04-26 20:52:04 +0800530 view.setOnApplyWindowInsetsListener((v, windowInsets) -> {
531 // For status bar height.
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800532 v.setPadding(
533 v.getPaddingLeft(),
Kunhung Li1b0cb472021-04-26 20:52:04 +0800534 windowInsets.getSystemWindowInsetTop(),
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800535 v.getPaddingRight(),
Kunhung Li1b0cb472021-04-26 20:52:04 +0800536 v.getPaddingBottom());
537
538 View gridView = v.findViewById(R.id.wallpaper_grid);
539 gridView.setPadding(
540 gridView.getPaddingLeft(),
541 gridView.getPaddingTop(),
542 gridView.getPaddingRight(),
Chihhang Chuang5ebbfea2021-04-15 09:50:24 +0800543 windowInsets.getSystemWindowInsetBottom());
544 return windowInsets.consumeSystemWindowInsets();
545 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800546 return view;
547 }
548
549 @Override
550 public void onClickTryAgain(@Destination int unused) {
551 if (mPendingSetIndividualHolder != null) {
552 mPendingSetIndividualHolder.setWallpaper();
553 }
554 }
555
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800556 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800557 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
558 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
559 R.dimen.current_wallpaper_bottom_sheet_layout_height);
560 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
561 // Only left and top may be set in order for the GridMarginDecoration to work properly.
562 mImageGrid.setPadding(
563 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
564 }
565
Chuck Liaof6b4b192020-08-07 02:31:32 +0800566 private IndividualPickerFragmentHost getIndividualPickerFragmentHost() {
Chuck Liao7e333722021-02-27 02:21:20 +0800567 Fragment parentFragment = getParentFragment();
568 if (parentFragment != null) {
569 return (IndividualPickerFragmentHost) parentFragment;
570 } else {
571 return (IndividualPickerFragmentHost) getActivity();
572 }
Chuck Liaof6b4b192020-08-07 02:31:32 +0800573 }
574
Chuck Liaob10d2a32021-04-27 20:59:53 +0800575 protected void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700576 // Skip if mImageGrid been initialized yet
577 if (mImageGrid == null) {
578 return;
579 }
580 // Skip if category hasn't loaded yet
581 if (mCategory == null) {
582 return;
583 }
584 // Skip if the adapter was already created
585 if (mAdapter != null) {
586 return;
587 }
Chuck Liao1e6cb682021-04-22 00:51:11 +0800588 mImageGrid.addItemDecoration(new GridPaddingDecoration(getGridItemPaddingHorizontal(),
589 getGridItemPaddingBottom()));
590 int edgePadding = getEdgePadding();
591 mImageGrid.setPadding(edgePadding, mImageGrid.getPaddingTop(), edgePadding,
592 mImageGrid.getPaddingBottom());
Chuck Liaob10d2a32021-04-27 20:59:53 +0800593 mTileSizePx = isFewerColumnLayout()
Chuck Liaoddf2b522021-04-15 00:36:25 +0800594 ? SizeCalculator.getFeaturedIndividualTileSize(getActivity())
595 : SizeCalculator.getIndividualTileSize(getActivity());
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700596 setUpImageGrid();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800597 mImageGrid.setAccessibilityDelegateCompat(
598 new WallpaperPickerRecyclerViewAccessibilityDelegate(
599 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700600 }
601
Ching-Sung Lib69e9932021-05-25 20:03:01 +0800602 boolean isFewerColumnLayout() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800603 return mWallpapers != null && mWallpapers.size() <= MAX_CAPACITY_IN_FEWER_COLUMN_LAYOUT;
604 }
605
Chuck Liao1e6cb682021-04-22 00:51:11 +0800606 private int getGridItemPaddingHorizontal() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800607 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800608 ? getResources().getDimensionPixelSize(
609 R.dimen.grid_item_featured_individual_padding_horizontal)
610 : getResources().getDimensionPixelSize(
611 R.dimen.grid_item_individual_padding_horizontal);
612 }
613
614 private int getGridItemPaddingBottom() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800615 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800616 ? getResources().getDimensionPixelSize(
617 R.dimen.grid_item_featured_individual_padding_bottom)
618 : getResources().getDimensionPixelSize(R.dimen.grid_item_individual_padding_bottom);
619 }
620
621 private int getEdgePadding() {
Chuck Liaob10d2a32021-04-27 20:59:53 +0800622 return isFewerColumnLayout()
Chuck Liao1e6cb682021-04-22 00:51:11 +0800623 ? getResources().getDimensionPixelSize(R.dimen.featured_wallpaper_grid_edge_space)
624 : getResources().getDimensionPixelSize(R.dimen.wallpaper_grid_edge_space);
625 }
626
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700627 /**
628 * Create the adapter and assign it to mImageGrid.
629 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
630 */
631 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800632 mAdapter = new IndividualAdapter(mWallpapers);
633 mImageGrid.setAdapter(mAdapter);
634 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
635 }
636
Jon Miranda16ea1b12017-12-12 14:52:48 -0800637 /**
638 * Enables and populates the "Currently set" wallpaper BottomSheet.
639 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800640 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800641 mImageGrid.addOnScrollListener(new OnScrollListener() {
642 @Override
643 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
644 if (mCurrentWallpaperBottomSheetPresenter == null) {
645 return;
646 }
647
648 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
649 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
650 }
651 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
652 @Override
653 public void run() {
654 if (dy > 0) {
655 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
656 } else {
657 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
658 }
659 }
660 };
661 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
662 }
663 });
664 }
665
chihhangchuang3efb6832020-04-17 02:06:25 +0800666 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800667 public void onResume() {
668 super.onResume();
669
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700670 WallpaperPreferences preferences = InjectorProvider.getInjector()
671 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800672 preferences.setLastAppActiveTimestamp(new Date().getTime());
673
674 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
675 // PreviewFragment.
676 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
677
678 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
679 // shown earlier when this fragment's hosting activity didn't allow committing fragment
680 // transactions.
681 if (mStagedStartRotationErrorDialogFragment != null) {
682 mStagedStartRotationErrorDialogFragment.show(
683 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
684 mStagedStartRotationErrorDialogFragment = null;
685 }
686
687 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
688 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
689 // committing fragment transactions.
690 if (mStagedSetWallpaperErrorDialogFragment != null) {
691 mStagedSetWallpaperErrorDialogFragment.show(
692 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
693 mStagedSetWallpaperErrorDialogFragment = null;
694 }
695
Chuck Liao5a4243b2020-05-20 23:56:39 +0800696 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
697 // Must be resuming from a previously stopped state, so re-schedule the update of the
698 // daily wallpapers tile thumbnail.
699 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800700 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800701 }
702
703 @Override
704 public void onStop() {
705 super.onStop();
706 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
707 }
708
709 @Override
Chuck Liaoa7215812021-04-13 22:33:43 +0800710 public void onDestroyView() {
711 super.onDestroyView();
712 getIndividualPickerFragmentHost().removeToolbarMenu();
713 }
714
715 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800716 public void onDestroy() {
717 super.onDestroy();
718 if (mProgressDialog != null) {
719 mProgressDialog.dismiss();
720 }
721 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700722 if (mAppStatusListener != null) {
723 mPackageStatusNotifier.removeListener(mAppStatusListener);
724 }
Chuck Liao69630f12020-03-05 19:01:25 +0800725 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800726 }
727
728 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800729 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
chihhangchuang8f9da5f2020-06-17 00:32:18 +0800730 // TODO(b/159310028): Refactor fragment layer to make it able to restore from config change.
731 // This is to handle config change with StartRotationDialog popup, the StartRotationDialog
732 // still holds a reference to the destroyed Fragment and is calling
733 // onStartRotationDialogDismissed on that destroyed Fragment.
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800734 }
735
736 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800737 public void retryStartRotation(@NetworkPreference int networkPreference) {
738 startRotation(networkPreference);
739 }
740
741 public void setCurrentWallpaperBottomSheetPresenter(
742 CurrentWallpaperBottomSheetPresenter presenter) {
743 mCurrentWallpaperBottomSheetPresenter = presenter;
744 }
745
746 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
747 mWallpapersUiContainer = uiContainer;
748 }
749
Chuck Liaob3829fb2020-04-01 00:47:50 +0800750 public void setOnWallpaperSelectedListener(
751 WallpaperSelectedListener wallpaperSelectedListener) {
752 mWallpaperSelectedListener = wallpaperSelectedListener;
753 }
754
755 /**
756 * Resizes the layout's height.
757 */
758 public void resizeLayout(int height) {
759 mImageGrid.getLayoutParams().height = height;
760 mImageGrid.requestLayout();
761 }
762
763 /**
764 * Scrolls to the specific item.
765 *
766 * @param position the position of the item
767 */
768 public void scrollToPosition(int position) {
769 ((GridLayoutManager) mImageGrid.getLayoutManager())
770 .scrollToPositionWithOffset(position, /* offset= */ 0);
771 }
772
Jon Miranda16ea1b12017-12-12 14:52:48 -0800773 /**
774 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
775 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
776 * constantly keeps the UI thread alive and blocks a test forever.
777 *
778 * @param testingMode
779 */
780 void setTestingMode(boolean testingMode) {
781 mTestingMode = testingMode;
782 }
783
Jon Miranda16ea1b12017-12-12 14:52:48 -0800784 @Override
785 public void startRotation(@NetworkPreference final int networkPreference) {
786 if (!isRotationEnabled()) {
787 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
788 return;
789 }
790
791 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
792 // causes Espresso to hang once the dialog is shown.
793 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
794 int themeResId;
795 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
796 themeResId = R.style.ProgressDialogThemePreL;
797 } else {
798 themeResId = R.style.LightDialogTheme;
799 }
800 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
801
802 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
803 mProgressDialog.setMessage(
804 getResources().getString(R.string.start_rotation_progress_message));
805 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
806 mProgressDialog.show();
807 }
808
809 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
810 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
811 }
812
813 final Context appContext = getActivity().getApplicationContext();
814
815 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
816 appContext,
817 networkPreference,
818 new Listener() {
819 @Override
820 public void onFirstWallpaperInRotationSet() {
821 if (mProgressDialog != null) {
822 mProgressDialog.dismiss();
823 }
824
825 // The fragment may be detached from its containing activity if the user exits the
826 // app before the first wallpaper image in rotation finishes downloading.
827 Activity activity = getActivity();
828
Jon Miranda16ea1b12017-12-12 14:52:48 -0800829
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700830 if (mWallpaperRotationInitializer.startRotation(appContext)) {
831 if (activity != null
832 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
833 try {
834 Toast.makeText(getActivity(),
835 R.string.wallpaper_set_successfully_message,
836 Toast.LENGTH_SHORT).show();
837 } catch (NotFoundException e) {
838 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800839 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800840
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700841 activity.setResult(Activity.RESULT_OK);
842 activity.finish();
843 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
844 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
845 }
846 } else { // Failed to start rotation.
847 showStartRotationErrorDialog(networkPreference);
848
849 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
850 DesktopRotationHolder rotationViewHolder =
851 (DesktopRotationHolder)
852 mImageGrid.findViewHolderForAdapterPosition(
853 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
854 rotationViewHolder.setSelectionState(
855 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800856 }
857 }
858 }
859
860 @Override
861 public void onError() {
862 if (mProgressDialog != null) {
863 mProgressDialog.dismiss();
864 }
865
866 showStartRotationErrorDialog(networkPreference);
867
868 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
869 DesktopRotationHolder rotationViewHolder =
870 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
871 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
872 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
873 }
874 }
875 });
876 }
877
878 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
Chuck Liaob17d1502020-09-07 21:55:30 +0800879 FragmentTransactionChecker activity = (FragmentTransactionChecker) getActivity();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800880 if (activity != null) {
881 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
882 StartRotationErrorDialogFragment.newInstance(networkPreference);
883 startRotationErrorDialogFragment.setTargetFragment(
884 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
885
886 if (activity.isSafeToCommitFragmentTransaction()) {
887 startRotationErrorDialogFragment.show(
888 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
889 } else {
890 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
891 }
892 }
893 }
894
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800895 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800896 Activity activity = getActivity();
Chuck Liaoddf2b522021-04-15 00:36:25 +0800897 if (activity == null) {
898 return 1;
899 }
Chuck Liaob10d2a32021-04-27 20:59:53 +0800900 return isFewerColumnLayout()
Chuck Liaoddf2b522021-04-15 00:36:25 +0800901 ? SizeCalculator.getNumFeaturedIndividualColumns(activity)
902 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800903 }
904
905 /**
906 * Returns whether rotation is enabled for this category.
907 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800908 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700909 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800910 }
911
912 @Override
913 public void onCurrentWallpaperRefreshed() {
914 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
915 }
916
Chuck Liao69630f12020-03-05 19:01:25 +0800917 @Override
918 public void onSet(int destination) {
919 if (mSelectedWallpaperInfo == null) {
920 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
921 return;
922 }
923
“Chuckffd832c2020-03-22 02:15:58 +0800924 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800925 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
926 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
927 destination, 0, null, mSetWallpaperCallback);
928 } else {
929 mWallpaperSetter.setCurrentWallpaper(
930 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
931 }
“Chuck7ef99722020-03-22 04:34:03 +0800932 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800933 }
934
935 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
936 new WallpaperPersister.SetWallpaperCallback() {
937 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800938 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800939 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800940 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
941 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800942 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
943 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800944 }
945
946 @Override
947 public void onError(@Nullable Throwable throwable) {
948 Log.e(TAG, "Can't apply the wallpaper.");
Chuck Liao69630f12020-03-05 19:01:25 +0800949 }
950 };
951
952 @Override
Chuck Liao33f1af42021-04-13 20:52:35 +0800953 public boolean onMenuItemClick(MenuItem item) {
954 if (item.getItemId() == R.id.daily_rotation) {
Chuck Liaoa7215812021-04-13 22:33:43 +0800955 showRotationDialog();
Chuck Liao33f1af42021-04-13 20:52:35 +0800956 return true;
957 }
958 return super.onMenuItemClick(item);
959 }
960
Jon Miranda16ea1b12017-12-12 14:52:48 -0800961 /**
Chuck Liaoa7215812021-04-13 22:33:43 +0800962 * Popups a daily rotation dialog for the uses to confirm.
963 */
964 public void showRotationDialog() {
965 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
966 startRotationDialogFragment.setTargetFragment(
967 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
968 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
969 }
970
971 /**
Jon Miranda16ea1b12017-12-12 14:52:48 -0800972 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
973 */
974 private void showSetWallpaperErrorDialog() {
975 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
976 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
977 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
978
979 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
980 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
981 } else {
982 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
983 }
984 }
985
Chuck Liao69630f12020-03-05 19:01:25 +0800986 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800987 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800988 if (thumbnailUpdater == null) {
989 return;
990 }
991
992 if (selectedWallpaperInfo != null) {
993 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
994 } else {
995 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800996 }
997 }
998
“Chuck7ef99722020-03-22 04:34:03 +0800999 private void onWallpaperDestinationSet(int destination) {
1000 WallpaperDestinationCallback wallpaperDestinationCallback =
1001 (WallpaperDestinationCallback) getParentFragment();
1002 if (wallpaperDestinationCallback == null) {
1003 return;
1004 }
1005
1006 wallpaperDestinationCallback.onDestinationSet(destination);
1007 }
1008
Ching-Sung Li073812b2020-04-07 21:19:21 +08001009 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +08001010 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001011 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +08001012 return;
1013 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001014 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +08001015 updateActivatedStatus(mSelectedWallpaperInfo == null
1016 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001017 // Update new selected wallpaper.
1018 updateActivatedStatus(newSelectedWallpaperInfo == null
1019 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
1020
1021 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001022 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +08001023 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +08001024 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +08001025 WallpaperInfoHelper.loadExploreIntent(
1026 getContext(),
1027 mSelectedWallpaperInfo,
1028 (actionLabel, exploreIntent) ->
1029 mWallpaperInfoView.populateWallpaperInfo(
Chuck Liao9991d682021-01-20 17:45:14 +08001030 mSelectedWallpaperInfo,
1031 actionLabel,
1032 WallpaperInfoHelper.shouldShowExploreButton(
1033 getContext(), exploreIntent),
1034 v -> onExploreClicked(exploreIntent))
1035 );
Chuck Liaob3829fb2020-04-01 00:47:50 +08001036 }
1037
1038 if (mWallpaperSelectedListener != null) {
1039 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +08001040 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001041 }
1042
chihhangchuangc687d912020-05-04 14:33:05 +08001043 private void onExploreClicked(Intent exploreIntent) {
1044 if (getContext() == null) {
1045 return;
1046 }
1047 Context context = getContext();
1048 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
1049 mSelectedWallpaperInfo.getActionLabelRes(context));
1050
1051 startActivity(exploreIntent);
1052 }
1053
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001054 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +08001055 if (wallpaperInfo == null) {
1056 return;
1057 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001058 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001059 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001060 ? index + 1 : index;
1061 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
1062 if (holder != null) {
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001063 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001064 thumbnail.setClipped(isActivated);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001065 } else {
1066 // Item is not visible, make sure the item is re-bound when it becomes visible.
1067 mAdapter.notifyItemChanged(index);
1068 }
1069 }
1070
“Chuckffd832c2020-03-22 02:15:58 +08001071 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
1072 if (wallpaperInfo == null) {
1073 return;
1074 }
1075 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +08001076 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +08001077 ? index + 1 : index;
1078 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001079 if (holder == null) {
“Chuckffd832c2020-03-22 02:15:58 +08001080 // Item is not visible, make sure the item is re-bound when it becomes visible.
1081 mAdapter.notifyItemChanged(index);
1082 }
1083 }
1084
Chuck Liao11f4a762020-04-08 13:24:43 +08001085 private void refreshAppliedWallpaper() {
1086 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
1087 showCheckMarkAndBorderForAppliedWallpaper(false);
1088
1089 // Update to the new applied wallpaper.
1090 String appliedWallpaperId = getAppliedWallpaperId();
1091 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
1092 .stream()
1093 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
1094 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
1095 .findFirst();
1096 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
1097
1098 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
1099 showCheckMarkAndBorderForAppliedWallpaper(true);
1100 }
1101
1102 private String getAppliedWallpaperId() {
1103 WallpaperPreferences prefs =
1104 InjectorProvider.getInjector().getPreferences(getContext());
1105 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1106 boolean isDestinationBoth =
1107 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1108
1109 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1110 return wallpaperInfo != null
1111 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1112 } else {
1113 return prefs.getLockWallpaperRemoteId();
1114 }
1115 }
1116
Chuck Liaoec053b02021-04-24 01:30:50 +08001117 private Set<String> getAppliedWallpaperIds() {
1118 WallpaperPreferences prefs =
1119 InjectorProvider.getInjector().getPreferences(getContext());
1120 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
1121 Set<String> appliedWallpaperIds = new ArraySet<>();
1122
1123 String homeWallpaperId = wallpaperInfo != null ? wallpaperInfo.getServiceName()
1124 : prefs.getHomeWallpaperRemoteId();
1125 if (!TextUtils.isEmpty(homeWallpaperId)) {
1126 appliedWallpaperIds.add(homeWallpaperId);
1127 }
1128
1129 boolean isLockWallpaperApplied =
1130 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) >= 0;
1131 String lockWallpaperId = prefs.getLockWallpaperRemoteId();
1132 if (isLockWallpaperApplied && !TextUtils.isEmpty(lockWallpaperId)) {
1133 appliedWallpaperIds.add(lockWallpaperId);
1134 }
1135
1136 return appliedWallpaperIds;
1137 }
1138
Chuck Liao11f4a762020-04-08 13:24:43 +08001139 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1140 updateAppliedStatus(mAppliedWallpaperInfo, show);
1141 if (mSelectedWallpaperInfo == null) {
1142 updateActivatedStatus(mAppliedWallpaperInfo, show);
1143 }
1144 }
1145
Ching-Sung Lib69e9932021-05-25 20:03:01 +08001146 boolean shouldShowRotationTile() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001147 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001148 }
1149
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001150 class EmptySelectionAnimator implements SelectionAnimator{
1151 EmptySelectionAnimator() {}
1152
1153 public boolean isSelected() {
1154 return false;
1155 }
1156
1157 /**
1158 * Sets the UI to selected immediately with no animation.
1159 */
1160 public void selectImmediately() {}
1161
1162 /**
1163 * Sets the UI to deselected immediately with no animation.
1164 */
1165 public void deselectImmediately() {}
1166
1167 /**
1168 * Sets the UI to selected with a smooth animation.
1169 */
1170 public void animateSelected() {}
1171
1172 /**
1173 * Sets the UI to deselected with a smooth animation.
1174 */
1175 public void animateDeselected() {}
1176
1177 /**
1178 * Sets the UI to show a loading indicator.
1179 */
1180 public void showLoading() {}
1181
1182 /**
1183 * Sets the UI to hide the loading indicator.
1184 */
1185 public void showNotLoading() {}
1186
1187 }
1188
Jon Miranda16ea1b12017-12-12 14:52:48 -08001189 /**
1190 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1191 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001192 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1193 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1194 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1195 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001196
1197 private final List<WallpaperInfo> mWallpapers;
1198
1199 private int mPendingSelectedAdapterPosition;
1200 private int mSelectedAdapterPosition;
1201
1202 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1203 mWallpapers = wallpapers;
1204 mPendingSelectedAdapterPosition = -1;
1205 mSelectedAdapterPosition = -1;
1206 }
1207
1208 @Override
1209 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1210 switch (viewType) {
1211 case ITEM_VIEW_TYPE_ROTATION:
1212 return createRotationHolder(parent);
1213 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1214 return createIndividualHolder(parent);
1215 case ITEM_VIEW_TYPE_MY_PHOTOS:
1216 return createMyPhotosHolder(parent);
1217 default:
1218 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1219 return null;
1220 }
1221 }
1222
1223 @Override
1224 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001225 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001226 return ITEM_VIEW_TYPE_ROTATION;
1227 }
1228
1229 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1230 if (mCategory.supportsCustomPhotos()
1231 && !isRotationEnabled()
1232 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1233 return ITEM_VIEW_TYPE_MY_PHOTOS;
1234 }
1235
1236 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1237 }
1238
1239 @Override
1240 public void onBindViewHolder(ViewHolder holder, int position) {
1241 int viewType = getItemViewType(position);
1242
1243 switch (viewType) {
1244 case ITEM_VIEW_TYPE_ROTATION:
1245 onBindRotationHolder(holder, position);
1246 break;
1247 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1248 onBindIndividualHolder(holder, position);
1249 break;
1250 case ITEM_VIEW_TYPE_MY_PHOTOS:
1251 ((MyPhotosViewHolder) holder).bind();
1252 break;
1253 default:
1254 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1255 }
1256 }
1257
1258 @Override
1259 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001260 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001261 ? mWallpapers.size() + 1
1262 : mWallpapers.size();
1263 }
1264
1265 private ViewHolder createRotationHolder(ViewGroup parent) {
1266 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001267 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001268 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Chuck Liao5a4243b2020-05-20 23:56:39 +08001269 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1270 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001271 }
1272
1273 private ViewHolder createIndividualHolder(ViewGroup parent) {
1274 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1275 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1276
1277 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001278 SelectionAnimator selectionAnimator = new EmptySelectionAnimator();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001279 return new SetIndividualHolder(
1280 getActivity(), mTileSizePx.y, view,
1281 selectionAnimator,
1282 new OnSetListener() {
1283 @Override
1284 public void onPendingWallpaperSet(int adapterPosition) {
1285 // Deselect and hide loading indicator for any previously pending tile.
1286 if (mPendingSelectedAdapterPosition != -1) {
1287 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1288 mPendingSelectedAdapterPosition);
1289 if (oldViewHolder instanceof SelectableHolder) {
1290 ((SelectableHolder) oldViewHolder).setSelectionState(
1291 SelectableHolder.SELECTION_STATE_DESELECTED);
1292 }
1293 }
1294
1295 if (mSelectedAdapterPosition != -1) {
1296 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1297 mSelectedAdapterPosition);
1298 if (oldViewHolder instanceof SelectableHolder) {
1299 ((SelectableHolder) oldViewHolder).setSelectionState(
1300 SelectableHolder.SELECTION_STATE_DESELECTED);
1301 }
1302 }
1303
1304 mPendingSelectedAdapterPosition = adapterPosition;
1305 }
1306
1307 @Override
1308 public void onWallpaperSet(int adapterPosition) {
1309 // No-op -- UI handles a new wallpaper being set by reacting to the
1310 // WallpaperChangedNotifier.
1311 }
1312
1313 @Override
1314 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1315 showSetWallpaperErrorDialog();
1316 mPendingSetIndividualHolder = holder;
1317 }
1318 });
1319 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001320 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001321 }
1322 }
1323
1324 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1325 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1326 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1327
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001328 return new MyPhotosViewHolder(getActivity(),
1329 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1330 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001331 }
1332
1333 /**
1334 * Marks the tile at the given position as selected with a visual indication. Also updates the
1335 * "currently selected" BottomSheet to reflect the newly selected tile.
1336 */
1337 private void updateSelectedTile(int newlySelectedPosition) {
1338 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1339 // succession.
1340 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1341 return;
1342 }
1343
1344 if (mCurrentWallpaperBottomSheetPresenter != null) {
1345 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1346 IndividualPickerFragment.this);
1347
1348 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1349 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1350 }
1351 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1352 @Override
1353 public void run() {
1354 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1355 }
1356 };
1357 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1358 }
1359
1360 // User may have switched to another category, thus detaching this fragment, so check here.
1361 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1362 // still occurs in the UI after the user selects that other category.
1363 if (getActivity() == null) {
1364 return;
1365 }
1366
1367 // Update the newly selected wallpaper ViewHolder and the old one so that if
1368 // selection UI state applies (desktop UI), it is updated.
1369 if (mSelectedAdapterPosition >= 0) {
1370 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1371 mSelectedAdapterPosition);
1372 if (oldViewHolder instanceof SelectableHolder) {
1373 ((SelectableHolder) oldViewHolder).setSelectionState(
1374 SelectableHolder.SELECTION_STATE_DESELECTED);
1375 }
1376 }
1377
1378 // Animate selection of newly selected tile.
1379 ViewHolder newViewHolder = mImageGrid
1380 .findViewHolderForAdapterPosition(newlySelectedPosition);
1381 if (newViewHolder instanceof SelectableHolder) {
1382 ((SelectableHolder) newViewHolder).setSelectionState(
1383 SelectableHolder.SELECTION_STATE_SELECTED);
1384 }
1385
1386 mSelectedAdapterPosition = newlySelectedPosition;
1387
1388 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1389 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1390 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1391 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1392 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1393 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1394
1395 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1396 }
1397
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001398 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001399 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1400 String collectionId = mCategory.getCollectionId();
1401 ((DesktopRotationHolder) holder).bind(collectionId);
1402
1403 if (mWallpaperPreferences.getWallpaperPresentationMode()
1404 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1405 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1406 mSelectedAdapterPosition = position;
1407 }
1408
1409 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1410 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1411 mWasUpdateRunnableRun = true;
1412 }
1413 }
1414 }
1415
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001416 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001417 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001418 ? position - 1 : position;
1419 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
Santiago Etchebeherea5736dc2021-06-14 14:32:32 -07001420 wallpaper.computePlaceholderColor(holder.itemView.getContext());
Jon Miranda16ea1b12017-12-12 14:52:48 -08001421 ((IndividualHolder) holder).bindWallpaper(wallpaper);
Chuck Liaoec053b02021-04-24 01:30:50 +08001422 boolean isWallpaperApplied = mAppliedWallpaperIds.contains(wallpaper.getWallpaperId());
Jon Miranda16ea1b12017-12-12 14:52:48 -08001423
“Chuckffd832c2020-03-22 02:15:58 +08001424 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001425 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001426 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001427 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001428
Ching-Sung Li1ac024f2021-06-08 14:43:28 +08001429 CardView container = holder.itemView.findViewById(R.id.wallpaper_container);
1430 int radiusId = isFewerColumnLayout() ? R.dimen.grid_item_all_radius
1431 : R.dimen.grid_item_all_radius_small;
1432 container.setRadius(getResources().getDimension(radiusId));
Tianguang Zhang6406edb2021-03-18 14:42:36 +01001433 CustomShapeImageView thumbnail = holder.itemView.findViewById(R.id.thumbnail);
Tianguang Zhangd913daf2021-03-03 20:21:26 +01001434 thumbnail.setClipped(isWallpaperApplied);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001435 }
1436 }
Chuck Liaoba401232020-03-13 20:11:04 +08001437
1438 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1439
Chuck Liao1e6cb682021-04-22 00:51:11 +08001440 private final int mPaddingHorizontal;
1441 private final int mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001442
Chuck Liao1e6cb682021-04-22 00:51:11 +08001443 GridPaddingDecoration(int paddingHorizontal, int paddingBottom) {
1444 mPaddingHorizontal = paddingHorizontal;
1445 mPaddingBottom = paddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001446 }
1447
1448 @Override
1449 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1450 RecyclerView.State state) {
1451 int position = parent.getChildAdapterPosition(view);
1452 if (position >= 0) {
Chuck Liao1e6cb682021-04-22 00:51:11 +08001453 outRect.left = mPaddingHorizontal;
1454 outRect.right = mPaddingHorizontal;
1455 outRect.bottom = mPaddingBottom;
Chuck Liaoba401232020-03-13 20:11:04 +08001456 }
1457 }
1458 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001459}