blob: 98d5f2a8f2b6bdf851b204f743ed3c4c3064258c [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.wallpaper.picker.individual;
17
Chuck Liao7dc21572020-05-14 18:37:36 +080018import static com.android.wallpaper.picker.WallpaperPickerDelegate.PREVIEW_WALLPAPER_REQUEST_CODE;
Chuck Liao69630f12020-03-05 19:01:25 +080019import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
Chuck Liao7dc21572020-05-14 18:37:36 +080020import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
chihhangchuang22aa0cc2020-03-25 19:12:42 +080021import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
Chuck Liao8ec38e02020-02-26 20:59:32 +080022import static com.android.wallpaper.widget.BottomActionBar.BottomAction.ROTATION;
23
Jon Miranda16ea1b12017-12-12 14:52:48 -080024import android.app.Activity;
25import android.app.ProgressDialog;
“Chuck7ef99722020-03-22 04:34:03 +080026import android.app.WallpaperManager;
Jon Miranda16ea1b12017-12-12 14:52:48 -080027import android.content.Context;
chihhangchuangc687d912020-05-04 14:33:05 +080028import android.content.Intent;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070029import android.content.res.Configuration;
Jon Miranda16ea1b12017-12-12 14:52:48 -080030import android.content.res.Resources.NotFoundException;
31import android.graphics.Point;
Chuck Liaoba401232020-03-13 20:11:04 +080032import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080033import android.os.Build.VERSION;
34import android.os.Build.VERSION_CODES;
35import android.os.Bundle;
36import android.os.Handler;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070037import android.service.wallpaper.WallpaperService;
Jon Miranda16ea1b12017-12-12 14:52:48 -080038import android.util.Log;
39import android.view.LayoutInflater;
40import android.view.View;
41import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080042import android.widget.Toast;
43
Santiago Etchebehereb1854472019-06-06 17:44:54 -070044import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080045import androidx.annotation.Nullable;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080046import androidx.fragment.app.DialogFragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080047import androidx.recyclerview.widget.GridLayoutManager;
48import androidx.recyclerview.widget.RecyclerView;
49import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
50import androidx.recyclerview.widget.RecyclerView.ViewHolder;
51
Jon Miranda16ea1b12017-12-12 14:52:48 -080052import com.android.wallpaper.R;
53import com.android.wallpaper.asset.Asset;
54import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070055import com.android.wallpaper.model.Category;
56import com.android.wallpaper.model.CategoryProvider;
57import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080058import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080059import com.android.wallpaper.model.WallpaperCategory;
60import com.android.wallpaper.model.WallpaperInfo;
61import com.android.wallpaper.model.WallpaperReceiver;
62import com.android.wallpaper.model.WallpaperRotationInitializer;
63import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
64import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080065import com.android.wallpaper.module.FormFactorChecker;
66import com.android.wallpaper.module.FormFactorChecker.FormFactor;
67import com.android.wallpaper.module.Injector;
68import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070069import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080070import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080071import com.android.wallpaper.module.WallpaperChangedNotifier;
72import com.android.wallpaper.module.WallpaperPersister;
73import com.android.wallpaper.module.WallpaperPersister.Destination;
74import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080075import com.android.wallpaper.module.WallpaperSetter;
Jon Miranda16ea1b12017-12-12 14:52:48 -080076import com.android.wallpaper.picker.BaseActivity;
chihhangchuang3efb6832020-04-17 02:06:25 +080077import com.android.wallpaper.picker.BottomActionBarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080078import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080079import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Chuck Liao7dc21572020-05-14 18:37:36 +080080import com.android.wallpaper.picker.PreviewActivity;
Jon Miranda16ea1b12017-12-12 14:52:48 -080081import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080082import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080083import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
84import com.android.wallpaper.picker.StartRotationDialogFragment;
85import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080086import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080087import com.android.wallpaper.picker.WallpapersUiContainer;
88import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
89import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070090import com.android.wallpaper.util.SizeCalculator;
Chuck Liao8ec38e02020-02-26 20:59:32 +080091import com.android.wallpaper.widget.BottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +080092import com.android.wallpaper.widget.WallpaperInfoView;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070093
Jon Miranda16ea1b12017-12-12 14:52:48 -080094import com.bumptech.glide.Glide;
95import com.bumptech.glide.MemoryCategory;
96
97import java.util.ArrayList;
98import java.util.Date;
99import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800100import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800101import java.util.Random;
102
103/**
104 * Displays the Main UI for picking an individual wallpaper image.
105 */
chihhangchuang3efb6832020-04-17 02:06:25 +0800106public class IndividualPickerFragment extends BottomActionBarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800107 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
108 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chuck Liao69630f12020-03-05 19:01:25 +0800109 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800110
111 public static final boolean NEW_SCROLL_INTERACTION = true;
112
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800113 /**
114 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
115 * such as "my photos" or "daily rotation".
116 */
117 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
118 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
119
Jon Miranda16ea1b12017-12-12 14:52:48 -0800120 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800121 private static final int UNUSED_REQUEST_CODE = 1;
122 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
123 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
124 private static final String PROGRESS_DIALOG_NO_TITLE = null;
125 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
126 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
127 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700128 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800129
Chuck Liao8ec38e02020-02-26 20:59:32 +0800130 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800131 * An interface for updating the thumbnail with the specific wallpaper.
132 */
133 public interface ThumbnailUpdater {
134 /**
135 * Updates the thumbnail with the specific wallpaper.
136 */
137 void updateThumbnail(WallpaperInfo wallpaperInfo);
138
139 /**
140 * Restores to the thumbnails of the wallpapers which were applied.
141 */
142 void restoreThumbnails();
143 }
144
“Chuck7ef99722020-03-22 04:34:03 +0800145 /**
146 * An interface for receiving the destination of the new applied wallpaper.
147 */
148 public interface WallpaperDestinationCallback {
149 /**
150 * Called when the destination of the wallpaper is set.
151 *
152 * @param destination the destination which a wallpaper may be set.
153 * See {@link Destination} for more details.
154 */
155 void onDestinationSet(@Destination int destination);
156 }
157
Chuck Liaob3829fb2020-04-01 00:47:50 +0800158 /**
159 * The listener which will be notified when the wallpaper is selected.
160 */
161 public interface WallpaperSelectedListener {
162 /**
163 * Called when the wallpaper is selected.
164 *
165 * @param position the position of the selected wallpaper
166 */
167 void onWallpaperSelected(int position);
168 }
169
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800170 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800171 WallpaperPreferences mWallpaperPreferences;
172 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800173 RecyclerView mImageGrid;
174 IndividualAdapter mAdapter;
175 WallpaperCategory mCategory;
176 WallpaperRotationInitializer mWallpaperRotationInitializer;
177 List<WallpaperInfo> mWallpapers;
178 Point mTileSizePx;
179 WallpapersUiContainer mWallpapersUiContainer;
180 @FormFactor
181 int mFormFactor;
182 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800183
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800184 Handler mHandler;
185 Random mRandom;
186
187 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
188 new WallpaperChangedNotifier.Listener() {
189 @Override
190 public void onWallpaperChanged() {
191 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
192 return;
193 }
194
195 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
196 mAdapter.mSelectedAdapterPosition);
197
198 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
199 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
200 if (selectedViewHolder instanceof SelectableHolder) {
201 ((SelectableHolder) selectedViewHolder).setSelectionState(
202 SelectableHolder.SELECTION_STATE_DESELECTED);
203 }
204 } else {
205 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
206 }
207 }
208 };
209 PackageStatusNotifier.Listener mAppStatusListener;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800210 BottomActionBar mBottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +0800211 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800212 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800213
chihhangchuangc687d912020-05-04 14:33:05 +0800214 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800215 private ProgressDialog mProgressDialog;
216 private boolean mTestingMode;
217 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800218 private SetIndividualHolder mPendingSetIndividualHolder;
219
220 /**
221 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
222 * committing fragment transactions.
223 */
224 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
225 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
226
Jon Miranda16ea1b12017-12-12 14:52:48 -0800227 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800228
229 /**
230 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
231 * invocation of the fragment.
232 */
233 private boolean mWasUpdateRunnableRun;
234
235 /**
236 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
237 * mode.
238 */
239 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
240 @Override
241 public void run() {
242 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
243 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
244 if (viewHolder instanceof DesktopRotationHolder) {
245 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
246 } else { // viewHolder is null
247 // If the rotation tile is unavailable (because user has scrolled down, causing the
248 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
249 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
250 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
251 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
252 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
253 }
254 }
255 };
256
Chuck Liao69630f12020-03-05 19:01:25 +0800257 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800258 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800259 private WallpaperManager mWallpaperManager;
260 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800261 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800262
Jon Miranda16ea1b12017-12-12 14:52:48 -0800263 public static IndividualPickerFragment newInstance(String collectionId) {
264 Bundle args = new Bundle();
265 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
266
267 IndividualPickerFragment fragment = new IndividualPickerFragment();
268 fragment.setArguments(args);
269 return fragment;
270 }
271
“Chuck7ef99722020-03-22 04:34:03 +0800272 /**
273 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
274 * would be set.
275 *
276 * @param wallpaperDestination the destination a wallpaper would be set.
277 * It will be either {@link Destination#DEST_HOME_SCREEN}
278 * or {@link Destination#DEST_LOCK_SCREEN}.
279 */
280 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
281 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800282 if (mWallpapers != null) {
283 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800284 }
285 }
286
Jon Miranda16ea1b12017-12-12 14:52:48 -0800287 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
288 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
289 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
290 getActivity());
291 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
292 @Override
293 public void onDrawableLoaded() {
294 if (getActivity() == null) {
295 return;
296 }
297
298 // Schedule the next update of the thumbnail.
299 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
300 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
301 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
302 }
303 });
304 }
305
306 @Override
307 public void onCreate(Bundle savedInstanceState) {
308 super.onCreate(savedInstanceState);
309
310 Injector injector = InjectorProvider.getInjector();
311 Context appContext = getContext().getApplicationContext();
312 mWallpaperPreferences = injector.getPreferences(appContext);
313
314 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
315 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
316
“Chuck7ef99722020-03-22 04:34:03 +0800317 mWallpaperManager = WallpaperManager.getInstance(appContext);
318
Jon Miranda16ea1b12017-12-12 14:52:48 -0800319 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
320
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700321 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
322
chihhangchuangc687d912020-05-04 14:33:05 +0800323 mUserEventLogger = injector.getUserEventLogger(appContext);
324
“Chuckffd832c2020-03-22 02:15:58 +0800325 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800326 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800327 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800328 injector.getPreferences(appContext),
329 injector.getUserEventLogger(appContext),
330 false);
331
Jon Miranda16ea1b12017-12-12 14:52:48 -0800332 mWallpapers = new ArrayList<>();
333 mRandom = new Random();
334 mHandler = new Handler();
335
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700336 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
337 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
338 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
339 Glide.get(getContext()).clearMemory();
340 }
341
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700342 CategoryProvider categoryProvider = injector.getCategoryProvider(appContext);
343 categoryProvider.fetchCategories(new CategoryReceiver() {
344 @Override
345 public void onCategoryReceived(Category category) {
346 // Do nothing.
347 }
348
349 @Override
350 public void doneFetchingCategories() {
351 mCategory = (WallpaperCategory) categoryProvider.getCategory(
352 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
353 if (mCategory == null) {
354 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
355
356 // The absence of this category in the CategoryProvider indicates a broken
357 // state, see b/38030129. Hence, finish the activity and return.
358 getActivity().finish();
359 return;
360 }
361 onCategoryLoaded();
362 }
363 }, false);
364 }
365
366 protected void onCategoryLoaded() {
367 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao58e4a1c2020-05-22 11:35:35 +0800368 // Avoids the "rotation" action is not shown correctly
369 // in a rare case : onCategoryLoaded() is called after onBottomActionBarReady().
370 if (isRotationEnabled() && mBottomActionBar != null
371 && !mBottomActionBar.areActionsShown(ROTATION)) {
372 mBottomActionBar.showActions(ROTATION);
373 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700374 fetchWallpapers(false);
375
376 if (mCategory.supportsThirdParty()) {
377 mAppStatusListener = (packageName, status) -> {
378 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
379 mCategory.containsThirdParty(packageName)) {
380 fetchWallpapers(true);
381 }
382 };
383 mPackageStatusNotifier.addListener(mAppStatusListener,
384 WallpaperService.SERVICE_INTERFACE);
385 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700386
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700387 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700388 }
389
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800390 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700391 mWallpapers.clear();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800392 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
393 @Override
394 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
395 for (WallpaperInfo wallpaper : wallpapers) {
396 mWallpapers.add(wallpaper);
397 }
398
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700399 // Wallpapers may load after the adapter is initialized, in which case we have
400 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800401 if (mAdapter != null) {
402 mAdapter.notifyDataSetChanged();
403 }
404
405 if (mWallpapersUiContainer != null) {
406 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700407 } else {
408 if (wallpapers.isEmpty()) {
409 // If there are no more wallpapers and we're on phone, just finish the
410 // Activity.
411 Activity activity = getActivity();
412 if (activity != null
413 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
414 activity.finish();
415 }
416 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800417 }
418 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700419 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800420 }
421
422 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700423 public void onSaveInstanceState(@NonNull Bundle outState) {
424 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700425 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700426 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
427 }
428
429 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800430 public View onCreateView(LayoutInflater inflater, ViewGroup container,
431 Bundle savedInstanceState) {
432 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
433
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700434 mTileSizePx = SizeCalculator.getIndividualTileSize(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800435
436 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
437 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
438 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
439 updateImageGridPadding(false /* addExtraBottomSpace */);
440 mImageGrid.setScrollBarSize(gridPaddingPx);
441 }
Chuck Liaoba401232020-03-13 20:11:04 +0800442 mImageGrid.addItemDecoration(new GridPaddingDecoration(
443 getResources().getDimensionPixelSize(R.dimen.grid_padding)));
Chuck Liaoca97c572020-05-08 17:13:25 +0800444 mImageGrid.setOnApplyWindowInsetsListener((v, windowInsets) -> {
445 v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(),
446 windowInsets.getSystemWindowInsetBottom());
447 return windowInsets;
448 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800449
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700450 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800451 setUpBottomSheet();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800452 return view;
453 }
454
455 @Override
Chuck Liao8ec38e02020-02-26 20:59:32 +0800456 public void onDestroyView() {
Chuck Liao46644b92020-06-08 14:20:50 +0800457 if (!NEW_SCROLL_INTERACTION) {
458 updateThumbnail(null);
459 }
Chuck Liao8ec38e02020-02-26 20:59:32 +0800460 super.onDestroyView();
461 }
462
463 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800464 public void onClickTryAgain(@Destination int unused) {
465 if (mPendingSetIndividualHolder != null) {
466 mPendingSetIndividualHolder.setWallpaper();
467 }
468 }
469
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800470 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800471 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
472 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
473 R.dimen.current_wallpaper_bottom_sheet_layout_height);
474 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
475 // Only left and top may be set in order for the GridMarginDecoration to work properly.
476 mImageGrid.setPadding(
477 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
478 }
479
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700480 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700481 // Skip if mImageGrid been initialized yet
482 if (mImageGrid == null) {
483 return;
484 }
485 // Skip if category hasn't loaded yet
486 if (mCategory == null) {
487 return;
488 }
489 // Skip if the adapter was already created
490 if (mAdapter != null) {
491 return;
492 }
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700493 setUpImageGrid();
494 }
495
496 /**
497 * Create the adapter and assign it to mImageGrid.
498 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
499 */
500 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800501 mAdapter = new IndividualAdapter(mWallpapers);
502 mImageGrid.setAdapter(mAdapter);
503 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
504 }
505
Jon Miranda16ea1b12017-12-12 14:52:48 -0800506 /**
507 * Enables and populates the "Currently set" wallpaper BottomSheet.
508 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800509 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800510 mImageGrid.addOnScrollListener(new OnScrollListener() {
511 @Override
512 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
513 if (mCurrentWallpaperBottomSheetPresenter == null) {
514 return;
515 }
516
517 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
518 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
519 }
520 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
521 @Override
522 public void run() {
523 if (dy > 0) {
524 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
525 } else {
526 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
527 }
528 }
529 };
530 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
531 }
532 });
533 }
534
chihhangchuang3efb6832020-04-17 02:06:25 +0800535 @Override
536 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Chuck Liao342f4ab2020-05-26 18:28:09 +0800537 mBottomActionBar = bottomActionBar;
538 if (isRotationEnabled()) {
539 mBottomActionBar.showActionsOnly(ROTATION);
Ching-Sung Li073812b2020-04-07 21:19:21 +0800540 }
Chuck Liao342f4ab2020-05-26 18:28:09 +0800541 mBottomActionBar.setActionClickListener(ROTATION, unused -> {
542 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
543 startRotationDialogFragment.setTargetFragment(
544 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
545 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
546 });
547 mBottomActionBar.setActionClickListener(APPLY, unused -> {
548 mBottomActionBar.disableActions();
549 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
550 mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
551 });
552
553 mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
554 .inflate(R.layout.wallpaper_info_view, /* root= */ null);
555 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
556 mBottomActionBar.setActionClickListener(EDIT, unused -> {
557 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
558 mSelectedWallpaperInfo.showPreview(getActivity(),
559 new PreviewActivity.PreviewActivityIntentFactory(),
560 PREVIEW_WALLPAPER_REQUEST_CODE);
561 });
562 mBottomActionBar.show();
Ching-Sung Li073812b2020-04-07 21:19:21 +0800563 }
564
Jon Miranda16ea1b12017-12-12 14:52:48 -0800565 @Override
566 public void onResume() {
567 super.onResume();
568
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700569 WallpaperPreferences preferences = InjectorProvider.getInjector()
570 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800571 preferences.setLastAppActiveTimestamp(new Date().getTime());
572
573 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
574 // PreviewFragment.
575 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
576
577 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
578 // shown earlier when this fragment's hosting activity didn't allow committing fragment
579 // transactions.
580 if (mStagedStartRotationErrorDialogFragment != null) {
581 mStagedStartRotationErrorDialogFragment.show(
582 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
583 mStagedStartRotationErrorDialogFragment = null;
584 }
585
586 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
587 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
588 // committing fragment transactions.
589 if (mStagedSetWallpaperErrorDialogFragment != null) {
590 mStagedSetWallpaperErrorDialogFragment.show(
591 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
592 mStagedSetWallpaperErrorDialogFragment = null;
593 }
594
Chuck Liao5a4243b2020-05-20 23:56:39 +0800595 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
596 // Must be resuming from a previously stopped state, so re-schedule the update of the
597 // daily wallpapers tile thumbnail.
598 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800599 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800600 }
601
602 @Override
603 public void onStop() {
604 super.onStop();
605 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
606 }
607
608 @Override
609 public void onDestroy() {
610 super.onDestroy();
611 if (mProgressDialog != null) {
612 mProgressDialog.dismiss();
613 }
614 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700615 if (mAppStatusListener != null) {
616 mPackageStatusNotifier.removeListener(mAppStatusListener);
617 }
Chuck Liao69630f12020-03-05 19:01:25 +0800618 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800619 }
620
621 @Override
622 public void retryStartRotation(@NetworkPreference int networkPreference) {
623 startRotation(networkPreference);
624 }
625
chihhangchuang7feb3752020-04-24 02:48:56 +0800626 @Override
627 public boolean onBackPressed() {
628 if (mSelectedWallpaperInfo != null) {
629 onWallpaperSelected(null, 0);
630 return true;
631 }
632 return false;
633 }
634
Jon Miranda16ea1b12017-12-12 14:52:48 -0800635 public void setCurrentWallpaperBottomSheetPresenter(
636 CurrentWallpaperBottomSheetPresenter presenter) {
637 mCurrentWallpaperBottomSheetPresenter = presenter;
638 }
639
640 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
641 mWallpapersUiContainer = uiContainer;
642 }
643
Chuck Liaob3829fb2020-04-01 00:47:50 +0800644 public void setOnWallpaperSelectedListener(
645 WallpaperSelectedListener wallpaperSelectedListener) {
646 mWallpaperSelectedListener = wallpaperSelectedListener;
647 }
648
649 /**
650 * Resizes the layout's height.
651 */
652 public void resizeLayout(int height) {
653 mImageGrid.getLayoutParams().height = height;
654 mImageGrid.requestLayout();
655 }
656
657 /**
658 * Scrolls to the specific item.
659 *
660 * @param position the position of the item
661 */
662 public void scrollToPosition(int position) {
663 ((GridLayoutManager) mImageGrid.getLayoutManager())
664 .scrollToPositionWithOffset(position, /* offset= */ 0);
665 }
666
Jon Miranda16ea1b12017-12-12 14:52:48 -0800667 /**
668 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
669 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
670 * constantly keeps the UI thread alive and blocks a test forever.
671 *
672 * @param testingMode
673 */
674 void setTestingMode(boolean testingMode) {
675 mTestingMode = testingMode;
676 }
677
Jon Miranda16ea1b12017-12-12 14:52:48 -0800678 @Override
679 public void startRotation(@NetworkPreference final int networkPreference) {
680 if (!isRotationEnabled()) {
681 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
682 return;
683 }
684
685 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
686 // causes Espresso to hang once the dialog is shown.
687 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
688 int themeResId;
689 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
690 themeResId = R.style.ProgressDialogThemePreL;
691 } else {
692 themeResId = R.style.LightDialogTheme;
693 }
694 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
695
696 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
697 mProgressDialog.setMessage(
698 getResources().getString(R.string.start_rotation_progress_message));
699 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
700 mProgressDialog.show();
701 }
702
703 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
704 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
705 }
706
707 final Context appContext = getActivity().getApplicationContext();
708
709 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
710 appContext,
711 networkPreference,
712 new Listener() {
713 @Override
714 public void onFirstWallpaperInRotationSet() {
715 if (mProgressDialog != null) {
716 mProgressDialog.dismiss();
717 }
718
719 // The fragment may be detached from its containing activity if the user exits the
720 // app before the first wallpaper image in rotation finishes downloading.
721 Activity activity = getActivity();
722
Jon Miranda16ea1b12017-12-12 14:52:48 -0800723
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700724 if (mWallpaperRotationInitializer.startRotation(appContext)) {
725 if (activity != null
726 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
727 try {
728 Toast.makeText(getActivity(),
729 R.string.wallpaper_set_successfully_message,
730 Toast.LENGTH_SHORT).show();
731 } catch (NotFoundException e) {
732 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800733 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800734
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700735 activity.setResult(Activity.RESULT_OK);
736 activity.finish();
737 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
738 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
739 }
740 } else { // Failed to start rotation.
741 showStartRotationErrorDialog(networkPreference);
742
743 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
744 DesktopRotationHolder rotationViewHolder =
745 (DesktopRotationHolder)
746 mImageGrid.findViewHolderForAdapterPosition(
747 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
748 rotationViewHolder.setSelectionState(
749 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800750 }
751 }
752 }
753
754 @Override
755 public void onError() {
756 if (mProgressDialog != null) {
757 mProgressDialog.dismiss();
758 }
759
760 showStartRotationErrorDialog(networkPreference);
761
762 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
763 DesktopRotationHolder rotationViewHolder =
764 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
765 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
766 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
767 }
768 }
769 });
770 }
771
772 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
773 BaseActivity activity = (BaseActivity) getActivity();
774 if (activity != null) {
775 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
776 StartRotationErrorDialogFragment.newInstance(networkPreference);
777 startRotationErrorDialogFragment.setTargetFragment(
778 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
779
780 if (activity.isSafeToCommitFragmentTransaction()) {
781 startRotationErrorDialogFragment.show(
782 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
783 } else {
784 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
785 }
786 }
787 }
788
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800789 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800790 Activity activity = getActivity();
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700791 return activity == null ? 0 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800792 }
793
794 /**
795 * Returns whether rotation is enabled for this category.
796 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800797 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700798 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800799 }
800
801 @Override
802 public void onCurrentWallpaperRefreshed() {
803 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
804 }
805
Chuck Liao69630f12020-03-05 19:01:25 +0800806 @Override
807 public void onSet(int destination) {
808 if (mSelectedWallpaperInfo == null) {
809 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
810 return;
811 }
812
“Chuckffd832c2020-03-22 02:15:58 +0800813 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800814 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
815 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
816 destination, 0, null, mSetWallpaperCallback);
817 } else {
818 mWallpaperSetter.setCurrentWallpaper(
819 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
820 }
“Chuck7ef99722020-03-22 04:34:03 +0800821 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800822 }
823
824 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
825 new WallpaperPersister.SetWallpaperCallback() {
826 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800827 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800828 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800829 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
830 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800831 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
832 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800833 }
834
835 @Override
836 public void onError(@Nullable Throwable throwable) {
837 Log.e(TAG, "Can't apply the wallpaper.");
838 mBottomActionBar.enableActions();
839 }
840 };
841
842 @Override
843 public void onDialogDismissed(boolean withItemSelected) {
844 if (!withItemSelected) {
845 mBottomActionBar.enableActions();
846 }
847 }
848
Jon Miranda16ea1b12017-12-12 14:52:48 -0800849 /**
850 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
851 */
852 private void showSetWallpaperErrorDialog() {
853 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
854 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
855 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
856
857 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
858 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
859 } else {
860 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
861 }
862 }
863
Ching-Sung Li073812b2020-04-07 21:19:21 +0800864 void updateBottomActions(boolean hasWallpaperSelected) {
chihhangchuang803ea9a2020-04-21 13:03:10 +0800865 if (hasWallpaperSelected) {
Chuck Liao7dc21572020-05-14 18:37:36 +0800866 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800867 } else {
chihhangchuang08abb582020-04-27 17:20:31 +0800868 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800869 }
Chuck Liao69630f12020-03-05 19:01:25 +0800870 }
871
872 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800873 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800874 if (thumbnailUpdater == null) {
875 return;
876 }
877
878 if (selectedWallpaperInfo != null) {
879 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
880 } else {
881 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800882 }
883 }
884
“Chuck7ef99722020-03-22 04:34:03 +0800885 private void onWallpaperDestinationSet(int destination) {
886 WallpaperDestinationCallback wallpaperDestinationCallback =
887 (WallpaperDestinationCallback) getParentFragment();
888 if (wallpaperDestinationCallback == null) {
889 return;
890 }
891
892 wallpaperDestinationCallback.onDestinationSet(destination);
893 }
894
Ching-Sung Li073812b2020-04-07 21:19:21 +0800895 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800896 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800897 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800898 return;
899 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800900 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +0800901 updateActivatedStatus(mSelectedWallpaperInfo == null
902 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800903 // Update new selected wallpaper.
904 updateActivatedStatus(newSelectedWallpaperInfo == null
905 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
906
907 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
908 updateBottomActions(mSelectedWallpaperInfo != null);
909 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +0800910 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +0800911 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +0800912 WallpaperInfoHelper.loadExploreIntent(
913 getContext(),
914 mSelectedWallpaperInfo,
915 (actionLabel, exploreIntent) ->
916 mWallpaperInfoView.populateWallpaperInfo(
917 mSelectedWallpaperInfo, actionLabel, exploreIntent,
918 v -> onExploreClicked(exploreIntent)));
Chuck Liaob3829fb2020-04-01 00:47:50 +0800919 }
920
921 if (mWallpaperSelectedListener != null) {
922 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800923 }
Chuck Liaof40063f2020-03-03 18:35:24 +0800924 }
925
chihhangchuangc687d912020-05-04 14:33:05 +0800926 private void onExploreClicked(Intent exploreIntent) {
927 if (getContext() == null) {
928 return;
929 }
930 Context context = getContext();
931 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
932 mSelectedWallpaperInfo.getActionLabelRes(context));
933
934 startActivity(exploreIntent);
935 }
936
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800937 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +0800938 if (wallpaperInfo == null) {
939 return;
940 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800941 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800942 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800943 ? index + 1 : index;
944 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
945 if (holder != null) {
946 holder.itemView.setActivated(isActivated);
947 } else {
948 // Item is not visible, make sure the item is re-bound when it becomes visible.
949 mAdapter.notifyItemChanged(index);
950 }
951 }
952
“Chuckffd832c2020-03-22 02:15:58 +0800953 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
954 if (wallpaperInfo == null) {
955 return;
956 }
957 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800958 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +0800959 ? index + 1 : index;
960 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
961 if (holder != null) {
962 holder.itemView.findViewById(R.id.check_circle)
963 .setVisibility(isApplied ? View.VISIBLE : View.GONE);
964 } else {
965 // Item is not visible, make sure the item is re-bound when it becomes visible.
966 mAdapter.notifyItemChanged(index);
967 }
968 }
969
Chuck Liao11f4a762020-04-08 13:24:43 +0800970 private void refreshAppliedWallpaper() {
971 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
972 showCheckMarkAndBorderForAppliedWallpaper(false);
973
974 // Update to the new applied wallpaper.
975 String appliedWallpaperId = getAppliedWallpaperId();
976 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
977 .stream()
978 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
979 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
980 .findFirst();
981 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
982
983 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
984 showCheckMarkAndBorderForAppliedWallpaper(true);
985 }
986
987 private String getAppliedWallpaperId() {
988 WallpaperPreferences prefs =
989 InjectorProvider.getInjector().getPreferences(getContext());
990 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
991 boolean isDestinationBoth =
992 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
993
994 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
995 return wallpaperInfo != null
996 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
997 } else {
998 return prefs.getLockWallpaperRemoteId();
999 }
1000 }
1001
1002 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1003 updateAppliedStatus(mAppliedWallpaperInfo, show);
1004 if (mSelectedWallpaperInfo == null) {
1005 updateActivatedStatus(mAppliedWallpaperInfo, show);
1006 }
1007 }
1008
Chuck Liao5a4243b2020-05-20 23:56:39 +08001009 private boolean shouldShowRotationTile() {
1010 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001011 }
1012
1013 /**
1014 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1015 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001016 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1017 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1018 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1019 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001020
1021 private final List<WallpaperInfo> mWallpapers;
1022
1023 private int mPendingSelectedAdapterPosition;
1024 private int mSelectedAdapterPosition;
1025
1026 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1027 mWallpapers = wallpapers;
1028 mPendingSelectedAdapterPosition = -1;
1029 mSelectedAdapterPosition = -1;
1030 }
1031
1032 @Override
1033 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1034 switch (viewType) {
1035 case ITEM_VIEW_TYPE_ROTATION:
1036 return createRotationHolder(parent);
1037 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1038 return createIndividualHolder(parent);
1039 case ITEM_VIEW_TYPE_MY_PHOTOS:
1040 return createMyPhotosHolder(parent);
1041 default:
1042 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1043 return null;
1044 }
1045 }
1046
1047 @Override
1048 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001049 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001050 return ITEM_VIEW_TYPE_ROTATION;
1051 }
1052
1053 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1054 if (mCategory.supportsCustomPhotos()
1055 && !isRotationEnabled()
1056 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1057 return ITEM_VIEW_TYPE_MY_PHOTOS;
1058 }
1059
1060 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1061 }
1062
1063 @Override
1064 public void onBindViewHolder(ViewHolder holder, int position) {
1065 int viewType = getItemViewType(position);
1066
1067 switch (viewType) {
1068 case ITEM_VIEW_TYPE_ROTATION:
1069 onBindRotationHolder(holder, position);
1070 break;
1071 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1072 onBindIndividualHolder(holder, position);
1073 break;
1074 case ITEM_VIEW_TYPE_MY_PHOTOS:
1075 ((MyPhotosViewHolder) holder).bind();
1076 break;
1077 default:
1078 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1079 }
1080 }
1081
1082 @Override
1083 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001084 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001085 ? mWallpapers.size() + 1
1086 : mWallpapers.size();
1087 }
1088
1089 private ViewHolder createRotationHolder(ViewGroup parent) {
1090 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001091 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
1092 SelectionAnimator selectionAnimator =
1093 new CheckmarkSelectionAnimator(getActivity(), view);
1094 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1095 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001096 }
1097
1098 private ViewHolder createIndividualHolder(ViewGroup parent) {
1099 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1100 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1101
1102 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1103 SelectionAnimator selectionAnimator =
1104 new CheckmarkSelectionAnimator(getActivity(), view);
1105 return new SetIndividualHolder(
1106 getActivity(), mTileSizePx.y, view,
1107 selectionAnimator,
1108 new OnSetListener() {
1109 @Override
1110 public void onPendingWallpaperSet(int adapterPosition) {
1111 // Deselect and hide loading indicator for any previously pending tile.
1112 if (mPendingSelectedAdapterPosition != -1) {
1113 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1114 mPendingSelectedAdapterPosition);
1115 if (oldViewHolder instanceof SelectableHolder) {
1116 ((SelectableHolder) oldViewHolder).setSelectionState(
1117 SelectableHolder.SELECTION_STATE_DESELECTED);
1118 }
1119 }
1120
1121 if (mSelectedAdapterPosition != -1) {
1122 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1123 mSelectedAdapterPosition);
1124 if (oldViewHolder instanceof SelectableHolder) {
1125 ((SelectableHolder) oldViewHolder).setSelectionState(
1126 SelectableHolder.SELECTION_STATE_DESELECTED);
1127 }
1128 }
1129
1130 mPendingSelectedAdapterPosition = adapterPosition;
1131 }
1132
1133 @Override
1134 public void onWallpaperSet(int adapterPosition) {
1135 // No-op -- UI handles a new wallpaper being set by reacting to the
1136 // WallpaperChangedNotifier.
1137 }
1138
1139 @Override
1140 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1141 showSetWallpaperErrorDialog();
1142 mPendingSetIndividualHolder = holder;
1143 }
1144 });
1145 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001146 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001147 }
1148 }
1149
1150 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1151 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1152 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1153
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001154 return new MyPhotosViewHolder(getActivity(),
1155 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1156 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001157 }
1158
1159 /**
1160 * Marks the tile at the given position as selected with a visual indication. Also updates the
1161 * "currently selected" BottomSheet to reflect the newly selected tile.
1162 */
1163 private void updateSelectedTile(int newlySelectedPosition) {
1164 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1165 // succession.
1166 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1167 return;
1168 }
1169
1170 if (mCurrentWallpaperBottomSheetPresenter != null) {
1171 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1172 IndividualPickerFragment.this);
1173
1174 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1175 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1176 }
1177 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1178 @Override
1179 public void run() {
1180 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1181 }
1182 };
1183 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1184 }
1185
1186 // User may have switched to another category, thus detaching this fragment, so check here.
1187 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1188 // still occurs in the UI after the user selects that other category.
1189 if (getActivity() == null) {
1190 return;
1191 }
1192
1193 // Update the newly selected wallpaper ViewHolder and the old one so that if
1194 // selection UI state applies (desktop UI), it is updated.
1195 if (mSelectedAdapterPosition >= 0) {
1196 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1197 mSelectedAdapterPosition);
1198 if (oldViewHolder instanceof SelectableHolder) {
1199 ((SelectableHolder) oldViewHolder).setSelectionState(
1200 SelectableHolder.SELECTION_STATE_DESELECTED);
1201 }
1202 }
1203
1204 // Animate selection of newly selected tile.
1205 ViewHolder newViewHolder = mImageGrid
1206 .findViewHolderForAdapterPosition(newlySelectedPosition);
1207 if (newViewHolder instanceof SelectableHolder) {
1208 ((SelectableHolder) newViewHolder).setSelectionState(
1209 SelectableHolder.SELECTION_STATE_SELECTED);
1210 }
1211
1212 mSelectedAdapterPosition = newlySelectedPosition;
1213
1214 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1215 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1216 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1217 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1218 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1219 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1220
1221 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1222 }
1223
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001224 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001225 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1226 String collectionId = mCategory.getCollectionId();
1227 ((DesktopRotationHolder) holder).bind(collectionId);
1228
1229 if (mWallpaperPreferences.getWallpaperPresentationMode()
1230 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1231 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1232 mSelectedAdapterPosition = position;
1233 }
1234
1235 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1236 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1237 mWasUpdateRunnableRun = true;
1238 }
1239 }
1240 }
1241
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001242 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001243 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001244 ? position - 1 : position;
1245 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1246 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001247 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001248 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1249 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1250 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001251
“Chuckffd832c2020-03-22 02:15:58 +08001252 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001253 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001254 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001255 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001256
Chuck Liao342f4ab2020-05-26 18:28:09 +08001257 holder.itemView.setActivated(
1258 (isWallpaperApplied && !hasUserSelectedWallpaper) || isWallpaperSelected);
1259 holder.itemView.findViewById(R.id.check_circle).setVisibility(
1260 isWallpaperApplied ? View.VISIBLE : View.GONE);
Chuck Liao46644b92020-06-08 14:20:50 +08001261 if (!NEW_SCROLL_INTERACTION) {
1262 holder.itemView.findViewById(R.id.tile).setOnClickListener(
1263 view -> onWallpaperSelected(wallpaper, position));
1264 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001265 }
1266 }
Chuck Liaoba401232020-03-13 20:11:04 +08001267
1268 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1269
1270 private int mPadding;
1271
1272 GridPaddingDecoration(int padding) {
1273 mPadding = padding;
1274 }
1275
1276 @Override
1277 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1278 RecyclerView.State state) {
1279 int position = parent.getChildAdapterPosition(view);
1280 if (position >= 0) {
1281 outRect.left = mPadding;
1282 outRect.right = mPadding;
1283 }
1284 }
1285 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001286}