blob: 6da3372a67ea74072345b1c83c83459c59fa0d4d [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 Liao69630f12020-03-05 19:01:25 +080018import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
chihhangchuang22aa0cc2020-03-25 19:12:42 +080019import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
Chuck Liao8ec38e02020-02-26 20:59:32 +080020import static com.android.wallpaper.widget.BottomActionBar.BottomAction.ROTATION;
21
Jon Miranda16ea1b12017-12-12 14:52:48 -080022import android.app.Activity;
23import android.app.ProgressDialog;
“Chuck7ef99722020-03-22 04:34:03 +080024import android.app.WallpaperManager;
Jon Miranda16ea1b12017-12-12 14:52:48 -080025import android.content.Context;
chihhangchuangc687d912020-05-04 14:33:05 +080026import android.content.Intent;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070027import android.content.res.Configuration;
Jon Miranda16ea1b12017-12-12 14:52:48 -080028import android.content.res.Resources.NotFoundException;
29import android.graphics.Point;
Chuck Liaoba401232020-03-13 20:11:04 +080030import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080031import android.os.Build.VERSION;
32import android.os.Build.VERSION_CODES;
33import android.os.Bundle;
34import android.os.Handler;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070035import android.service.wallpaper.WallpaperService;
Jon Miranda16ea1b12017-12-12 14:52:48 -080036import android.util.Log;
37import android.view.LayoutInflater;
38import android.view.View;
39import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080040import android.widget.Toast;
41
Santiago Etchebehereb1854472019-06-06 17:44:54 -070042import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080043import androidx.annotation.Nullable;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080044import androidx.fragment.app.DialogFragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080045import androidx.recyclerview.widget.GridLayoutManager;
46import androidx.recyclerview.widget.RecyclerView;
47import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
48import androidx.recyclerview.widget.RecyclerView.ViewHolder;
49
Jon Miranda16ea1b12017-12-12 14:52:48 -080050import com.android.wallpaper.R;
51import com.android.wallpaper.asset.Asset;
52import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070053import com.android.wallpaper.model.Category;
54import com.android.wallpaper.model.CategoryProvider;
55import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080056import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080057import com.android.wallpaper.model.WallpaperCategory;
58import com.android.wallpaper.model.WallpaperInfo;
59import com.android.wallpaper.model.WallpaperReceiver;
60import com.android.wallpaper.model.WallpaperRotationInitializer;
61import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
62import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080063import com.android.wallpaper.module.FormFactorChecker;
64import com.android.wallpaper.module.FormFactorChecker.FormFactor;
65import com.android.wallpaper.module.Injector;
66import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070067import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080068import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080069import com.android.wallpaper.module.WallpaperChangedNotifier;
70import com.android.wallpaper.module.WallpaperPersister;
71import com.android.wallpaper.module.WallpaperPersister.Destination;
72import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080073import com.android.wallpaper.module.WallpaperSetter;
Jon Miranda16ea1b12017-12-12 14:52:48 -080074import com.android.wallpaper.picker.BaseActivity;
chihhangchuang3efb6832020-04-17 02:06:25 +080075import com.android.wallpaper.picker.BottomActionBarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080076import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080077import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Jon Miranda16ea1b12017-12-12 14:52:48 -080078import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080079import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080080import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
81import com.android.wallpaper.picker.StartRotationDialogFragment;
82import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080083import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080084import com.android.wallpaper.picker.WallpapersUiContainer;
85import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
86import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070087import com.android.wallpaper.util.SizeCalculator;
Chuck Liao8ec38e02020-02-26 20:59:32 +080088import com.android.wallpaper.widget.BottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +080089import com.android.wallpaper.widget.WallpaperInfoView;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070090
Jon Miranda16ea1b12017-12-12 14:52:48 -080091import com.bumptech.glide.Glide;
92import com.bumptech.glide.MemoryCategory;
93
94import java.util.ArrayList;
95import java.util.Date;
96import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +080097import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -080098import java.util.Random;
99
100/**
101 * Displays the Main UI for picking an individual wallpaper image.
102 */
chihhangchuang3efb6832020-04-17 02:06:25 +0800103public class IndividualPickerFragment extends BottomActionBarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800104 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
105 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chuck Liao69630f12020-03-05 19:01:25 +0800106 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800107 /**
108 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
109 * such as "my photos" or "daily rotation".
110 */
111 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
112 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
Ching-Sung Li073812b2020-04-07 21:19:21 +0800113 /**
114 * A temporary flag to hide the bottom action bar feature.
115 */
Chuck Liao1b7de472020-04-24 14:06:38 +0800116 static final boolean TEMP_BOTTOM_ACTION_BAR_FEATURE = true;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800117
Jon Miranda16ea1b12017-12-12 14:52:48 -0800118 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800119 private static final int UNUSED_REQUEST_CODE = 1;
120 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
121 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
122 private static final String PROGRESS_DIALOG_NO_TITLE = null;
123 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
124 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
125 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700126 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800127
Chuck Liao8ec38e02020-02-26 20:59:32 +0800128 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800129 * An interface for updating the thumbnail with the specific wallpaper.
130 */
131 public interface ThumbnailUpdater {
132 /**
133 * Updates the thumbnail with the specific wallpaper.
134 */
135 void updateThumbnail(WallpaperInfo wallpaperInfo);
136
137 /**
138 * Restores to the thumbnails of the wallpapers which were applied.
139 */
140 void restoreThumbnails();
141 }
142
“Chuck7ef99722020-03-22 04:34:03 +0800143 /**
144 * An interface for receiving the destination of the new applied wallpaper.
145 */
146 public interface WallpaperDestinationCallback {
147 /**
148 * Called when the destination of the wallpaper is set.
149 *
150 * @param destination the destination which a wallpaper may be set.
151 * See {@link Destination} for more details.
152 */
153 void onDestinationSet(@Destination int destination);
154 }
155
Chuck Liaob3829fb2020-04-01 00:47:50 +0800156 /**
157 * The listener which will be notified when the wallpaper is selected.
158 */
159 public interface WallpaperSelectedListener {
160 /**
161 * Called when the wallpaper is selected.
162 *
163 * @param position the position of the selected wallpaper
164 */
165 void onWallpaperSelected(int position);
166 }
167
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800168 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800169 WallpaperPreferences mWallpaperPreferences;
170 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800171 RecyclerView mImageGrid;
172 IndividualAdapter mAdapter;
173 WallpaperCategory mCategory;
174 WallpaperRotationInitializer mWallpaperRotationInitializer;
175 List<WallpaperInfo> mWallpapers;
176 Point mTileSizePx;
177 WallpapersUiContainer mWallpapersUiContainer;
178 @FormFactor
179 int mFormFactor;
180 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800181
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800182 Handler mHandler;
183 Random mRandom;
184
185 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
186 new WallpaperChangedNotifier.Listener() {
187 @Override
188 public void onWallpaperChanged() {
189 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
190 return;
191 }
192
193 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
194 mAdapter.mSelectedAdapterPosition);
195
196 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
197 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
198 if (selectedViewHolder instanceof SelectableHolder) {
199 ((SelectableHolder) selectedViewHolder).setSelectionState(
200 SelectableHolder.SELECTION_STATE_DESELECTED);
201 }
202 } else {
203 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
204 }
205 }
206 };
207 PackageStatusNotifier.Listener mAppStatusListener;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800208 BottomActionBar mBottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +0800209 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800210 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800211
chihhangchuangc687d912020-05-04 14:33:05 +0800212 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800213 private ProgressDialog mProgressDialog;
214 private boolean mTestingMode;
215 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800216 private SetIndividualHolder mPendingSetIndividualHolder;
217
218 /**
219 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
220 * committing fragment transactions.
221 */
222 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
223 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
224
Jon Miranda16ea1b12017-12-12 14:52:48 -0800225 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800226
227 /**
228 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
229 * invocation of the fragment.
230 */
231 private boolean mWasUpdateRunnableRun;
232
233 /**
234 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
235 * mode.
236 */
237 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
238 @Override
239 public void run() {
240 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
241 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
242 if (viewHolder instanceof DesktopRotationHolder) {
243 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
244 } else { // viewHolder is null
245 // If the rotation tile is unavailable (because user has scrolled down, causing the
246 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
247 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
248 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
249 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
250 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
251 }
252 }
253 };
254
Chuck Liao69630f12020-03-05 19:01:25 +0800255 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800256 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800257 private WallpaperManager mWallpaperManager;
258 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800259 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800260
Jon Miranda16ea1b12017-12-12 14:52:48 -0800261 public static IndividualPickerFragment newInstance(String collectionId) {
262 Bundle args = new Bundle();
263 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
264
265 IndividualPickerFragment fragment = new IndividualPickerFragment();
266 fragment.setArguments(args);
267 return fragment;
268 }
269
“Chuck7ef99722020-03-22 04:34:03 +0800270 /**
271 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
272 * would be set.
273 *
274 * @param wallpaperDestination the destination a wallpaper would be set.
275 * It will be either {@link Destination#DEST_HOME_SCREEN}
276 * or {@link Destination#DEST_LOCK_SCREEN}.
277 */
278 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
279 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800280 if (mWallpapers != null) {
281 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800282 }
283 }
284
Jon Miranda16ea1b12017-12-12 14:52:48 -0800285 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
286 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
287 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
288 getActivity());
289 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
290 @Override
291 public void onDrawableLoaded() {
292 if (getActivity() == null) {
293 return;
294 }
295
296 // Schedule the next update of the thumbnail.
297 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
298 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
299 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
300 }
301 });
302 }
303
304 @Override
305 public void onCreate(Bundle savedInstanceState) {
306 super.onCreate(savedInstanceState);
307
308 Injector injector = InjectorProvider.getInjector();
309 Context appContext = getContext().getApplicationContext();
310 mWallpaperPreferences = injector.getPreferences(appContext);
311
312 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
313 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
314
“Chuck7ef99722020-03-22 04:34:03 +0800315 mWallpaperManager = WallpaperManager.getInstance(appContext);
316
Jon Miranda16ea1b12017-12-12 14:52:48 -0800317 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
318
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700319 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
320
chihhangchuangc687d912020-05-04 14:33:05 +0800321 mUserEventLogger = injector.getUserEventLogger(appContext);
322
“Chuckffd832c2020-03-22 02:15:58 +0800323 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800324 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800325 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800326 injector.getPreferences(appContext),
327 injector.getUserEventLogger(appContext),
328 false);
329
Jon Miranda16ea1b12017-12-12 14:52:48 -0800330 mWallpapers = new ArrayList<>();
331 mRandom = new Random();
332 mHandler = new Handler();
333
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700334 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
335 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
336 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
337 Glide.get(getContext()).clearMemory();
338 }
339
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700340 CategoryProvider categoryProvider = injector.getCategoryProvider(appContext);
341 categoryProvider.fetchCategories(new CategoryReceiver() {
342 @Override
343 public void onCategoryReceived(Category category) {
344 // Do nothing.
345 }
346
347 @Override
348 public void doneFetchingCategories() {
349 mCategory = (WallpaperCategory) categoryProvider.getCategory(
350 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
351 if (mCategory == null) {
352 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
353
354 // The absence of this category in the CategoryProvider indicates a broken
355 // state, see b/38030129. Hence, finish the activity and return.
356 getActivity().finish();
357 return;
358 }
359 onCategoryLoaded();
360 }
361 }, false);
362 }
363
364 protected void onCategoryLoaded() {
365 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700366 fetchWallpapers(false);
367
368 if (mCategory.supportsThirdParty()) {
369 mAppStatusListener = (packageName, status) -> {
370 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
371 mCategory.containsThirdParty(packageName)) {
372 fetchWallpapers(true);
373 }
374 };
375 mPackageStatusNotifier.addListener(mAppStatusListener,
376 WallpaperService.SERVICE_INTERFACE);
377 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700378
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700379 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700380 }
381
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800382 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700383 mWallpapers.clear();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800384 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
385 @Override
386 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
387 for (WallpaperInfo wallpaper : wallpapers) {
388 mWallpapers.add(wallpaper);
389 }
390
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700391 // Wallpapers may load after the adapter is initialized, in which case we have
392 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800393 if (mAdapter != null) {
394 mAdapter.notifyDataSetChanged();
395 }
396
397 if (mWallpapersUiContainer != null) {
398 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700399 } else {
400 if (wallpapers.isEmpty()) {
401 // If there are no more wallpapers and we're on phone, just finish the
402 // Activity.
403 Activity activity = getActivity();
404 if (activity != null
405 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
406 activity.finish();
407 }
408 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800409 }
410 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700411 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800412 }
413
414 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700415 public void onSaveInstanceState(@NonNull Bundle outState) {
416 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700417 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700418 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
419 }
420
421 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800422 public View onCreateView(LayoutInflater inflater, ViewGroup container,
423 Bundle savedInstanceState) {
424 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
425
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700426 mTileSizePx = SizeCalculator.getIndividualTileSize(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800427
428 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
429 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
430 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
431 updateImageGridPadding(false /* addExtraBottomSpace */);
432 mImageGrid.setScrollBarSize(gridPaddingPx);
433 }
Chuck Liaoba401232020-03-13 20:11:04 +0800434 mImageGrid.addItemDecoration(new GridPaddingDecoration(
435 getResources().getDimensionPixelSize(R.dimen.grid_padding)));
Chuck Liaoca97c572020-05-08 17:13:25 +0800436 mImageGrid.setOnApplyWindowInsetsListener((v, windowInsets) -> {
437 v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(),
438 windowInsets.getSystemWindowInsetBottom());
439 return windowInsets;
440 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800441
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700442 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800443 setUpBottomSheet();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800444 return view;
445 }
446
447 @Override
Chuck Liao8ec38e02020-02-26 20:59:32 +0800448 public void onDestroyView() {
449 if (TEMP_BOTTOM_ACTION_BAR_FEATURE) {
Chuck Liao69630f12020-03-05 19:01:25 +0800450 updateThumbnail(null);
Chuck Liao8ec38e02020-02-26 20:59:32 +0800451 }
452 super.onDestroyView();
453 }
454
455 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800456 public void onClickTryAgain(@Destination int unused) {
457 if (mPendingSetIndividualHolder != null) {
458 mPendingSetIndividualHolder.setWallpaper();
459 }
460 }
461
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800462 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800463 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
464 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
465 R.dimen.current_wallpaper_bottom_sheet_layout_height);
466 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
467 // Only left and top may be set in order for the GridMarginDecoration to work properly.
468 mImageGrid.setPadding(
469 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
470 }
471
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700472 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700473 // Skip if mImageGrid been initialized yet
474 if (mImageGrid == null) {
475 return;
476 }
477 // Skip if category hasn't loaded yet
478 if (mCategory == null) {
479 return;
480 }
481 // Skip if the adapter was already created
482 if (mAdapter != null) {
483 return;
484 }
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700485 setUpImageGrid();
486 }
487
488 /**
489 * Create the adapter and assign it to mImageGrid.
490 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
491 */
492 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800493 mAdapter = new IndividualAdapter(mWallpapers);
494 mImageGrid.setAdapter(mAdapter);
495 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
496 }
497
Jon Miranda16ea1b12017-12-12 14:52:48 -0800498 /**
499 * Enables and populates the "Currently set" wallpaper BottomSheet.
500 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800501 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800502 mImageGrid.addOnScrollListener(new OnScrollListener() {
503 @Override
504 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
505 if (mCurrentWallpaperBottomSheetPresenter == null) {
506 return;
507 }
508
509 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
510 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
511 }
512 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
513 @Override
514 public void run() {
515 if (dy > 0) {
516 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
517 } else {
518 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
519 }
520 }
521 };
522 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
523 }
524 });
525 }
526
chihhangchuang3efb6832020-04-17 02:06:25 +0800527 @Override
528 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Ching-Sung Li073812b2020-04-07 21:19:21 +0800529 if (TEMP_BOTTOM_ACTION_BAR_FEATURE) {
chihhangchuang3efb6832020-04-17 02:06:25 +0800530 mBottomActionBar = bottomActionBar;
chihhangchuang803ea9a2020-04-21 13:03:10 +0800531 if (isRotationEnabled()) {
chihhangchuang08abb582020-04-27 17:20:31 +0800532 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800533 }
Ching-Sung Li073812b2020-04-07 21:19:21 +0800534 mBottomActionBar.setActionClickListener(ROTATION, unused -> {
535 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
536 startRotationDialogFragment.setTargetFragment(
537 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
538 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
539 });
540 mBottomActionBar.setActionClickListener(APPLY, unused -> {
541 mBottomActionBar.disableActions();
542 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
543 mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
544 });
chihhangchuang1a29e752020-04-28 18:22:53 +0800545
chihhangchuang7bef6562020-05-14 21:19:20 +0800546 mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
547 .inflate(R.layout.wallpaper_info_view, /* root= */ null);
548 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
Ching-Sung Li073812b2020-04-07 21:19:21 +0800549 mBottomActionBar.show();
Ching-Sung Li073812b2020-04-07 21:19:21 +0800550 }
551 }
552
Jon Miranda16ea1b12017-12-12 14:52:48 -0800553 @Override
554 public void onResume() {
555 super.onResume();
556
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700557 WallpaperPreferences preferences = InjectorProvider.getInjector()
558 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800559 preferences.setLastAppActiveTimestamp(new Date().getTime());
560
561 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
562 // PreviewFragment.
563 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
564
565 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
566 // shown earlier when this fragment's hosting activity didn't allow committing fragment
567 // transactions.
568 if (mStagedStartRotationErrorDialogFragment != null) {
569 mStagedStartRotationErrorDialogFragment.show(
570 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
571 mStagedStartRotationErrorDialogFragment = null;
572 }
573
574 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
575 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
576 // committing fragment transactions.
577 if (mStagedSetWallpaperErrorDialogFragment != null) {
578 mStagedSetWallpaperErrorDialogFragment.show(
579 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
580 mStagedSetWallpaperErrorDialogFragment = null;
581 }
582
Chuck Liao5a4243b2020-05-20 23:56:39 +0800583 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
584 // Must be resuming from a previously stopped state, so re-schedule the update of the
585 // daily wallpapers tile thumbnail.
586 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800587 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800588 }
589
590 @Override
591 public void onStop() {
592 super.onStop();
593 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
594 }
595
596 @Override
597 public void onDestroy() {
598 super.onDestroy();
599 if (mProgressDialog != null) {
600 mProgressDialog.dismiss();
601 }
602 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700603 if (mAppStatusListener != null) {
604 mPackageStatusNotifier.removeListener(mAppStatusListener);
605 }
Chuck Liao69630f12020-03-05 19:01:25 +0800606 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800607 }
608
609 @Override
610 public void retryStartRotation(@NetworkPreference int networkPreference) {
611 startRotation(networkPreference);
612 }
613
chihhangchuang7feb3752020-04-24 02:48:56 +0800614 @Override
615 public boolean onBackPressed() {
616 if (mSelectedWallpaperInfo != null) {
617 onWallpaperSelected(null, 0);
618 return true;
619 }
620 return false;
621 }
622
Jon Miranda16ea1b12017-12-12 14:52:48 -0800623 public void setCurrentWallpaperBottomSheetPresenter(
624 CurrentWallpaperBottomSheetPresenter presenter) {
625 mCurrentWallpaperBottomSheetPresenter = presenter;
626 }
627
628 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
629 mWallpapersUiContainer = uiContainer;
630 }
631
Chuck Liaob3829fb2020-04-01 00:47:50 +0800632 public void setOnWallpaperSelectedListener(
633 WallpaperSelectedListener wallpaperSelectedListener) {
634 mWallpaperSelectedListener = wallpaperSelectedListener;
635 }
636
637 /**
638 * Resizes the layout's height.
639 */
640 public void resizeLayout(int height) {
641 mImageGrid.getLayoutParams().height = height;
642 mImageGrid.requestLayout();
643 }
644
645 /**
646 * Scrolls to the specific item.
647 *
648 * @param position the position of the item
649 */
650 public void scrollToPosition(int position) {
651 ((GridLayoutManager) mImageGrid.getLayoutManager())
652 .scrollToPositionWithOffset(position, /* offset= */ 0);
653 }
654
Jon Miranda16ea1b12017-12-12 14:52:48 -0800655 /**
656 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
657 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
658 * constantly keeps the UI thread alive and blocks a test forever.
659 *
660 * @param testingMode
661 */
662 void setTestingMode(boolean testingMode) {
663 mTestingMode = testingMode;
664 }
665
Jon Miranda16ea1b12017-12-12 14:52:48 -0800666 @Override
667 public void startRotation(@NetworkPreference final int networkPreference) {
668 if (!isRotationEnabled()) {
669 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
670 return;
671 }
672
673 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
674 // causes Espresso to hang once the dialog is shown.
675 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
676 int themeResId;
677 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
678 themeResId = R.style.ProgressDialogThemePreL;
679 } else {
680 themeResId = R.style.LightDialogTheme;
681 }
682 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
683
684 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
685 mProgressDialog.setMessage(
686 getResources().getString(R.string.start_rotation_progress_message));
687 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
688 mProgressDialog.show();
689 }
690
691 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
692 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
693 }
694
695 final Context appContext = getActivity().getApplicationContext();
696
697 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
698 appContext,
699 networkPreference,
700 new Listener() {
701 @Override
702 public void onFirstWallpaperInRotationSet() {
703 if (mProgressDialog != null) {
704 mProgressDialog.dismiss();
705 }
706
707 // The fragment may be detached from its containing activity if the user exits the
708 // app before the first wallpaper image in rotation finishes downloading.
709 Activity activity = getActivity();
710
Jon Miranda16ea1b12017-12-12 14:52:48 -0800711
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700712 if (mWallpaperRotationInitializer.startRotation(appContext)) {
713 if (activity != null
714 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
715 try {
716 Toast.makeText(getActivity(),
717 R.string.wallpaper_set_successfully_message,
718 Toast.LENGTH_SHORT).show();
719 } catch (NotFoundException e) {
720 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800721 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800722
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700723 activity.setResult(Activity.RESULT_OK);
724 activity.finish();
725 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
726 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
727 }
728 } else { // Failed to start rotation.
729 showStartRotationErrorDialog(networkPreference);
730
731 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
732 DesktopRotationHolder rotationViewHolder =
733 (DesktopRotationHolder)
734 mImageGrid.findViewHolderForAdapterPosition(
735 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
736 rotationViewHolder.setSelectionState(
737 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800738 }
739 }
740 }
741
742 @Override
743 public void onError() {
744 if (mProgressDialog != null) {
745 mProgressDialog.dismiss();
746 }
747
748 showStartRotationErrorDialog(networkPreference);
749
750 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
751 DesktopRotationHolder rotationViewHolder =
752 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
753 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
754 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
755 }
756 }
757 });
758 }
759
760 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
761 BaseActivity activity = (BaseActivity) getActivity();
762 if (activity != null) {
763 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
764 StartRotationErrorDialogFragment.newInstance(networkPreference);
765 startRotationErrorDialogFragment.setTargetFragment(
766 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
767
768 if (activity.isSafeToCommitFragmentTransaction()) {
769 startRotationErrorDialogFragment.show(
770 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
771 } else {
772 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
773 }
774 }
775 }
776
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800777 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800778 Activity activity = getActivity();
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700779 return activity == null ? 0 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800780 }
781
782 /**
783 * Returns whether rotation is enabled for this category.
784 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800785 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700786 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800787 }
788
789 @Override
790 public void onCurrentWallpaperRefreshed() {
791 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
792 }
793
Chuck Liao69630f12020-03-05 19:01:25 +0800794 @Override
795 public void onSet(int destination) {
796 if (mSelectedWallpaperInfo == null) {
797 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
798 return;
799 }
800
“Chuckffd832c2020-03-22 02:15:58 +0800801 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800802 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
803 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
804 destination, 0, null, mSetWallpaperCallback);
805 } else {
806 mWallpaperSetter.setCurrentWallpaper(
807 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
808 }
“Chuck7ef99722020-03-22 04:34:03 +0800809 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800810 }
811
812 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
813 new WallpaperPersister.SetWallpaperCallback() {
814 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800815 public void onSuccess(WallpaperInfo wallpaperInfo) {
Chuck Liao6e52fff2020-04-23 16:11:23 +0800816 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
817 Toast.LENGTH_SHORT).show();
Chuck Liao69630f12020-03-05 19:01:25 +0800818 mBottomActionBar.enableActions();
Chuck Liao11f4a762020-04-08 13:24:43 +0800819 refreshAppliedWallpaper();
“Chuckffd832c2020-03-22 02:15:58 +0800820
821 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao69630f12020-03-05 19:01:25 +0800822 }
823
824 @Override
825 public void onError(@Nullable Throwable throwable) {
826 Log.e(TAG, "Can't apply the wallpaper.");
827 mBottomActionBar.enableActions();
828 }
829 };
830
831 @Override
832 public void onDialogDismissed(boolean withItemSelected) {
833 if (!withItemSelected) {
834 mBottomActionBar.enableActions();
835 }
836 }
837
Jon Miranda16ea1b12017-12-12 14:52:48 -0800838 /**
839 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
840 */
841 private void showSetWallpaperErrorDialog() {
842 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
843 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
844 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
845
846 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
847 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
848 } else {
849 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
850 }
851 }
852
Ching-Sung Li073812b2020-04-07 21:19:21 +0800853 void updateBottomActions(boolean hasWallpaperSelected) {
chihhangchuang803ea9a2020-04-21 13:03:10 +0800854 if (hasWallpaperSelected) {
chihhangchuang08abb582020-04-27 17:20:31 +0800855 mBottomActionBar.showActionsOnly(INFORMATION, APPLY);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800856 } else {
chihhangchuang08abb582020-04-27 17:20:31 +0800857 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800858 }
Chuck Liao69630f12020-03-05 19:01:25 +0800859 }
860
861 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800862 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800863 if (thumbnailUpdater == null) {
864 return;
865 }
866
867 if (selectedWallpaperInfo != null) {
868 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
869 } else {
870 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800871 }
872 }
873
“Chuck7ef99722020-03-22 04:34:03 +0800874 private void onWallpaperDestinationSet(int destination) {
875 WallpaperDestinationCallback wallpaperDestinationCallback =
876 (WallpaperDestinationCallback) getParentFragment();
877 if (wallpaperDestinationCallback == null) {
878 return;
879 }
880
881 wallpaperDestinationCallback.onDestinationSet(destination);
882 }
883
Ching-Sung Li073812b2020-04-07 21:19:21 +0800884 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800885 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800886 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800887 return;
888 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800889 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +0800890 updateActivatedStatus(mSelectedWallpaperInfo == null
891 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800892 // Update new selected wallpaper.
893 updateActivatedStatus(newSelectedWallpaperInfo == null
894 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
895
896 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
897 updateBottomActions(mSelectedWallpaperInfo != null);
898 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +0800899 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +0800900 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +0800901 WallpaperInfoHelper.loadExploreIntent(
902 getContext(),
903 mSelectedWallpaperInfo,
904 (actionLabel, exploreIntent) ->
905 mWallpaperInfoView.populateWallpaperInfo(
906 mSelectedWallpaperInfo, actionLabel, exploreIntent,
907 v -> onExploreClicked(exploreIntent)));
Chuck Liaob3829fb2020-04-01 00:47:50 +0800908 }
909
910 if (mWallpaperSelectedListener != null) {
911 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800912 }
Chuck Liaof40063f2020-03-03 18:35:24 +0800913 }
914
chihhangchuangc687d912020-05-04 14:33:05 +0800915 private void onExploreClicked(Intent exploreIntent) {
916 if (getContext() == null) {
917 return;
918 }
919 Context context = getContext();
920 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
921 mSelectedWallpaperInfo.getActionLabelRes(context));
922
923 startActivity(exploreIntent);
924 }
925
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800926 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +0800927 if (wallpaperInfo == null) {
928 return;
929 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800930 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800931 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800932 ? index + 1 : index;
933 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
934 if (holder != null) {
935 holder.itemView.setActivated(isActivated);
936 } else {
937 // Item is not visible, make sure the item is re-bound when it becomes visible.
938 mAdapter.notifyItemChanged(index);
939 }
940 }
941
“Chuckffd832c2020-03-22 02:15:58 +0800942 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
943 if (wallpaperInfo == null) {
944 return;
945 }
946 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800947 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +0800948 ? index + 1 : index;
949 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
950 if (holder != null) {
951 holder.itemView.findViewById(R.id.check_circle)
952 .setVisibility(isApplied ? View.VISIBLE : View.GONE);
953 } else {
954 // Item is not visible, make sure the item is re-bound when it becomes visible.
955 mAdapter.notifyItemChanged(index);
956 }
957 }
958
Chuck Liao11f4a762020-04-08 13:24:43 +0800959 private void refreshAppliedWallpaper() {
960 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
961 showCheckMarkAndBorderForAppliedWallpaper(false);
962
963 // Update to the new applied wallpaper.
964 String appliedWallpaperId = getAppliedWallpaperId();
965 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
966 .stream()
967 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
968 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
969 .findFirst();
970 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
971
972 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
973 showCheckMarkAndBorderForAppliedWallpaper(true);
974 }
975
976 private String getAppliedWallpaperId() {
977 WallpaperPreferences prefs =
978 InjectorProvider.getInjector().getPreferences(getContext());
979 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
980 boolean isDestinationBoth =
981 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
982
983 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
984 return wallpaperInfo != null
985 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
986 } else {
987 return prefs.getLockWallpaperRemoteId();
988 }
989 }
990
991 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
992 updateAppliedStatus(mAppliedWallpaperInfo, show);
993 if (mSelectedWallpaperInfo == null) {
994 updateActivatedStatus(mAppliedWallpaperInfo, show);
995 }
996 }
997
Chuck Liao5a4243b2020-05-20 23:56:39 +0800998 private boolean shouldShowRotationTile() {
999 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001000 }
1001
1002 /**
1003 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1004 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001005 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1006 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1007 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1008 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001009
1010 private final List<WallpaperInfo> mWallpapers;
1011
1012 private int mPendingSelectedAdapterPosition;
1013 private int mSelectedAdapterPosition;
1014
1015 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1016 mWallpapers = wallpapers;
1017 mPendingSelectedAdapterPosition = -1;
1018 mSelectedAdapterPosition = -1;
1019 }
1020
1021 @Override
1022 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1023 switch (viewType) {
1024 case ITEM_VIEW_TYPE_ROTATION:
1025 return createRotationHolder(parent);
1026 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1027 return createIndividualHolder(parent);
1028 case ITEM_VIEW_TYPE_MY_PHOTOS:
1029 return createMyPhotosHolder(parent);
1030 default:
1031 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1032 return null;
1033 }
1034 }
1035
1036 @Override
1037 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001038 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001039 return ITEM_VIEW_TYPE_ROTATION;
1040 }
1041
1042 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1043 if (mCategory.supportsCustomPhotos()
1044 && !isRotationEnabled()
1045 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1046 return ITEM_VIEW_TYPE_MY_PHOTOS;
1047 }
1048
1049 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1050 }
1051
1052 @Override
1053 public void onBindViewHolder(ViewHolder holder, int position) {
1054 int viewType = getItemViewType(position);
1055
1056 switch (viewType) {
1057 case ITEM_VIEW_TYPE_ROTATION:
1058 onBindRotationHolder(holder, position);
1059 break;
1060 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1061 onBindIndividualHolder(holder, position);
1062 break;
1063 case ITEM_VIEW_TYPE_MY_PHOTOS:
1064 ((MyPhotosViewHolder) holder).bind();
1065 break;
1066 default:
1067 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1068 }
1069 }
1070
1071 @Override
1072 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001073 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001074 ? mWallpapers.size() + 1
1075 : mWallpapers.size();
1076 }
1077
1078 private ViewHolder createRotationHolder(ViewGroup parent) {
1079 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001080 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
1081 SelectionAnimator selectionAnimator =
1082 new CheckmarkSelectionAnimator(getActivity(), view);
1083 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1084 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001085 }
1086
1087 private ViewHolder createIndividualHolder(ViewGroup parent) {
1088 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1089 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
Chuck Liao6a37a1c2020-03-07 03:39:43 +08001090 if (!TEMP_BOTTOM_ACTION_BAR_FEATURE) {
1091 view.findViewById(R.id.tile).setPadding(0, 0, 0, 0);
1092 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001093
1094 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1095 SelectionAnimator selectionAnimator =
1096 new CheckmarkSelectionAnimator(getActivity(), view);
1097 return new SetIndividualHolder(
1098 getActivity(), mTileSizePx.y, view,
1099 selectionAnimator,
1100 new OnSetListener() {
1101 @Override
1102 public void onPendingWallpaperSet(int adapterPosition) {
1103 // Deselect and hide loading indicator for any previously pending tile.
1104 if (mPendingSelectedAdapterPosition != -1) {
1105 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1106 mPendingSelectedAdapterPosition);
1107 if (oldViewHolder instanceof SelectableHolder) {
1108 ((SelectableHolder) oldViewHolder).setSelectionState(
1109 SelectableHolder.SELECTION_STATE_DESELECTED);
1110 }
1111 }
1112
1113 if (mSelectedAdapterPosition != -1) {
1114 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1115 mSelectedAdapterPosition);
1116 if (oldViewHolder instanceof SelectableHolder) {
1117 ((SelectableHolder) oldViewHolder).setSelectionState(
1118 SelectableHolder.SELECTION_STATE_DESELECTED);
1119 }
1120 }
1121
1122 mPendingSelectedAdapterPosition = adapterPosition;
1123 }
1124
1125 @Override
1126 public void onWallpaperSet(int adapterPosition) {
1127 // No-op -- UI handles a new wallpaper being set by reacting to the
1128 // WallpaperChangedNotifier.
1129 }
1130
1131 @Override
1132 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1133 showSetWallpaperErrorDialog();
1134 mPendingSetIndividualHolder = holder;
1135 }
1136 });
1137 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001138 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001139 }
1140 }
1141
1142 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1143 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1144 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1145
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001146 return new MyPhotosViewHolder(getActivity(),
1147 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1148 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001149 }
1150
1151 /**
1152 * Marks the tile at the given position as selected with a visual indication. Also updates the
1153 * "currently selected" BottomSheet to reflect the newly selected tile.
1154 */
1155 private void updateSelectedTile(int newlySelectedPosition) {
1156 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1157 // succession.
1158 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1159 return;
1160 }
1161
1162 if (mCurrentWallpaperBottomSheetPresenter != null) {
1163 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1164 IndividualPickerFragment.this);
1165
1166 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1167 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1168 }
1169 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1170 @Override
1171 public void run() {
1172 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1173 }
1174 };
1175 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1176 }
1177
1178 // User may have switched to another category, thus detaching this fragment, so check here.
1179 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1180 // still occurs in the UI after the user selects that other category.
1181 if (getActivity() == null) {
1182 return;
1183 }
1184
1185 // Update the newly selected wallpaper ViewHolder and the old one so that if
1186 // selection UI state applies (desktop UI), it is updated.
1187 if (mSelectedAdapterPosition >= 0) {
1188 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1189 mSelectedAdapterPosition);
1190 if (oldViewHolder instanceof SelectableHolder) {
1191 ((SelectableHolder) oldViewHolder).setSelectionState(
1192 SelectableHolder.SELECTION_STATE_DESELECTED);
1193 }
1194 }
1195
1196 // Animate selection of newly selected tile.
1197 ViewHolder newViewHolder = mImageGrid
1198 .findViewHolderForAdapterPosition(newlySelectedPosition);
1199 if (newViewHolder instanceof SelectableHolder) {
1200 ((SelectableHolder) newViewHolder).setSelectionState(
1201 SelectableHolder.SELECTION_STATE_SELECTED);
1202 }
1203
1204 mSelectedAdapterPosition = newlySelectedPosition;
1205
1206 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1207 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1208 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1209 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1210 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1211 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1212
1213 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1214 }
1215
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001216 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001217 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1218 String collectionId = mCategory.getCollectionId();
1219 ((DesktopRotationHolder) holder).bind(collectionId);
1220
1221 if (mWallpaperPreferences.getWallpaperPresentationMode()
1222 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1223 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1224 mSelectedAdapterPosition = position;
1225 }
1226
1227 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1228 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1229 mWasUpdateRunnableRun = true;
1230 }
1231 }
1232 }
1233
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001234 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001235 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001236 ? position - 1 : position;
1237 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1238 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001239 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001240 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1241 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1242 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001243
“Chuckffd832c2020-03-22 02:15:58 +08001244 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001245 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001246 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001247 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001248
1249 if (TEMP_BOTTOM_ACTION_BAR_FEATURE) {
“Chuckffd832c2020-03-22 02:15:58 +08001250 holder.itemView.setActivated(
1251 (isWallpaperApplied && !hasUserSelectedWallpaper) || isWallpaperSelected);
1252 holder.itemView.findViewById(R.id.check_circle).setVisibility(
1253 isWallpaperApplied ? View.VISIBLE : View.GONE);
Chuck Liao69630f12020-03-05 19:01:25 +08001254 holder.itemView.findViewById(R.id.tile).setOnClickListener(
Chuck Liaob3829fb2020-04-01 00:47:50 +08001255 view -> onWallpaperSelected(wallpaper, position));
Chuck Liaof40063f2020-03-03 18:35:24 +08001256 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001257 }
1258 }
Chuck Liaoba401232020-03-13 20:11:04 +08001259
1260 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1261
1262 private int mPadding;
1263
1264 GridPaddingDecoration(int padding) {
1265 mPadding = padding;
1266 }
1267
1268 @Override
1269 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1270 RecyclerView.State state) {
1271 int position = parent.getChildAdapterPosition(view);
1272 if (position >= 0) {
1273 outRect.left = mPadding;
1274 outRect.right = mPadding;
1275 }
1276 }
1277 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001278}