blob: f8ee2fc80e63783c37d4c70398db3af12275dcb3 [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 {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800110 /**
111 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
112 * such as "my photos" or "daily rotation".
113 */
114 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
115 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
116
Jon Miranda16ea1b12017-12-12 14:52:48 -0800117 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800118 private static final int UNUSED_REQUEST_CODE = 1;
119 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
120 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
121 private static final String PROGRESS_DIALOG_NO_TITLE = null;
122 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
123 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
124 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700125 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800126
Chuck Liao8ec38e02020-02-26 20:59:32 +0800127 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800128 * An interface for updating the thumbnail with the specific wallpaper.
129 */
130 public interface ThumbnailUpdater {
131 /**
132 * Updates the thumbnail with the specific wallpaper.
133 */
134 void updateThumbnail(WallpaperInfo wallpaperInfo);
135
136 /**
137 * Restores to the thumbnails of the wallpapers which were applied.
138 */
139 void restoreThumbnails();
140 }
141
“Chuck7ef99722020-03-22 04:34:03 +0800142 /**
143 * An interface for receiving the destination of the new applied wallpaper.
144 */
145 public interface WallpaperDestinationCallback {
146 /**
147 * Called when the destination of the wallpaper is set.
148 *
149 * @param destination the destination which a wallpaper may be set.
150 * See {@link Destination} for more details.
151 */
152 void onDestinationSet(@Destination int destination);
153 }
154
Chuck Liaob3829fb2020-04-01 00:47:50 +0800155 /**
156 * The listener which will be notified when the wallpaper is selected.
157 */
158 public interface WallpaperSelectedListener {
159 /**
160 * Called when the wallpaper is selected.
161 *
162 * @param position the position of the selected wallpaper
163 */
164 void onWallpaperSelected(int position);
165 }
166
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800167 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800168 WallpaperPreferences mWallpaperPreferences;
169 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800170 RecyclerView mImageGrid;
171 IndividualAdapter mAdapter;
172 WallpaperCategory mCategory;
173 WallpaperRotationInitializer mWallpaperRotationInitializer;
174 List<WallpaperInfo> mWallpapers;
175 Point mTileSizePx;
176 WallpapersUiContainer mWallpapersUiContainer;
177 @FormFactor
178 int mFormFactor;
179 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800180
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800181 Handler mHandler;
182 Random mRandom;
183
184 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
185 new WallpaperChangedNotifier.Listener() {
186 @Override
187 public void onWallpaperChanged() {
188 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
189 return;
190 }
191
192 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
193 mAdapter.mSelectedAdapterPosition);
194
195 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
196 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
197 if (selectedViewHolder instanceof SelectableHolder) {
198 ((SelectableHolder) selectedViewHolder).setSelectionState(
199 SelectableHolder.SELECTION_STATE_DESELECTED);
200 }
201 } else {
202 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
203 }
204 }
205 };
206 PackageStatusNotifier.Listener mAppStatusListener;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800207 BottomActionBar mBottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +0800208 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800209 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800210
chihhangchuangc687d912020-05-04 14:33:05 +0800211 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800212 private ProgressDialog mProgressDialog;
213 private boolean mTestingMode;
214 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800215 private SetIndividualHolder mPendingSetIndividualHolder;
216
217 /**
218 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
219 * committing fragment transactions.
220 */
221 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
222 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
223
Jon Miranda16ea1b12017-12-12 14:52:48 -0800224 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800225
226 /**
227 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
228 * invocation of the fragment.
229 */
230 private boolean mWasUpdateRunnableRun;
231
232 /**
233 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
234 * mode.
235 */
236 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
237 @Override
238 public void run() {
239 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
240 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
241 if (viewHolder instanceof DesktopRotationHolder) {
242 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
243 } else { // viewHolder is null
244 // If the rotation tile is unavailable (because user has scrolled down, causing the
245 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
246 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
247 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
248 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
249 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
250 }
251 }
252 };
253
Chuck Liao69630f12020-03-05 19:01:25 +0800254 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800255 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800256 private WallpaperManager mWallpaperManager;
257 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800258 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800259
Jon Miranda16ea1b12017-12-12 14:52:48 -0800260 public static IndividualPickerFragment newInstance(String collectionId) {
261 Bundle args = new Bundle();
262 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
263
264 IndividualPickerFragment fragment = new IndividualPickerFragment();
265 fragment.setArguments(args);
266 return fragment;
267 }
268
“Chuck7ef99722020-03-22 04:34:03 +0800269 /**
270 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
271 * would be set.
272 *
273 * @param wallpaperDestination the destination a wallpaper would be set.
274 * It will be either {@link Destination#DEST_HOME_SCREEN}
275 * or {@link Destination#DEST_LOCK_SCREEN}.
276 */
277 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
278 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800279 if (mWallpapers != null) {
280 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800281 }
282 }
283
Jon Miranda16ea1b12017-12-12 14:52:48 -0800284 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
285 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
286 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
287 getActivity());
288 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
289 @Override
290 public void onDrawableLoaded() {
291 if (getActivity() == null) {
292 return;
293 }
294
295 // Schedule the next update of the thumbnail.
296 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
297 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
298 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
299 }
300 });
301 }
302
303 @Override
304 public void onCreate(Bundle savedInstanceState) {
305 super.onCreate(savedInstanceState);
306
307 Injector injector = InjectorProvider.getInjector();
308 Context appContext = getContext().getApplicationContext();
309 mWallpaperPreferences = injector.getPreferences(appContext);
310
311 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
312 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
313
“Chuck7ef99722020-03-22 04:34:03 +0800314 mWallpaperManager = WallpaperManager.getInstance(appContext);
315
Jon Miranda16ea1b12017-12-12 14:52:48 -0800316 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
317
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700318 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
319
chihhangchuangc687d912020-05-04 14:33:05 +0800320 mUserEventLogger = injector.getUserEventLogger(appContext);
321
“Chuckffd832c2020-03-22 02:15:58 +0800322 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800323 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800324 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800325 injector.getPreferences(appContext),
326 injector.getUserEventLogger(appContext),
327 false);
328
Jon Miranda16ea1b12017-12-12 14:52:48 -0800329 mWallpapers = new ArrayList<>();
330 mRandom = new Random();
331 mHandler = new Handler();
332
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700333 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
334 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
335 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
336 Glide.get(getContext()).clearMemory();
337 }
338
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700339 CategoryProvider categoryProvider = injector.getCategoryProvider(appContext);
340 categoryProvider.fetchCategories(new CategoryReceiver() {
341 @Override
342 public void onCategoryReceived(Category category) {
343 // Do nothing.
344 }
345
346 @Override
347 public void doneFetchingCategories() {
348 mCategory = (WallpaperCategory) categoryProvider.getCategory(
349 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
350 if (mCategory == null) {
351 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
352
353 // The absence of this category in the CategoryProvider indicates a broken
354 // state, see b/38030129. Hence, finish the activity and return.
355 getActivity().finish();
356 return;
357 }
358 onCategoryLoaded();
359 }
360 }, false);
361 }
362
363 protected void onCategoryLoaded() {
364 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao58e4a1c2020-05-22 11:35:35 +0800365 // Avoids the "rotation" action is not shown correctly
366 // in a rare case : onCategoryLoaded() is called after onBottomActionBarReady().
367 if (isRotationEnabled() && mBottomActionBar != null
368 && !mBottomActionBar.areActionsShown(ROTATION)) {
369 mBottomActionBar.showActions(ROTATION);
370 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700371 fetchWallpapers(false);
372
373 if (mCategory.supportsThirdParty()) {
374 mAppStatusListener = (packageName, status) -> {
375 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
376 mCategory.containsThirdParty(packageName)) {
377 fetchWallpapers(true);
378 }
379 };
380 mPackageStatusNotifier.addListener(mAppStatusListener,
381 WallpaperService.SERVICE_INTERFACE);
382 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700383
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700384 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700385 }
386
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800387 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700388 mWallpapers.clear();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800389 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
390 @Override
391 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
392 for (WallpaperInfo wallpaper : wallpapers) {
393 mWallpapers.add(wallpaper);
394 }
395
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700396 // Wallpapers may load after the adapter is initialized, in which case we have
397 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800398 if (mAdapter != null) {
399 mAdapter.notifyDataSetChanged();
400 }
401
402 if (mWallpapersUiContainer != null) {
403 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700404 } else {
405 if (wallpapers.isEmpty()) {
406 // If there are no more wallpapers and we're on phone, just finish the
407 // Activity.
408 Activity activity = getActivity();
409 if (activity != null
410 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
411 activity.finish();
412 }
413 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800414 }
415 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700416 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800417 }
418
419 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700420 public void onSaveInstanceState(@NonNull Bundle outState) {
421 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700422 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700423 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
424 }
425
426 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800427 public View onCreateView(LayoutInflater inflater, ViewGroup container,
428 Bundle savedInstanceState) {
429 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
430
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700431 mTileSizePx = SizeCalculator.getIndividualTileSize(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800432
433 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
434 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
435 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
436 updateImageGridPadding(false /* addExtraBottomSpace */);
437 mImageGrid.setScrollBarSize(gridPaddingPx);
438 }
Chuck Liaoba401232020-03-13 20:11:04 +0800439 mImageGrid.addItemDecoration(new GridPaddingDecoration(
440 getResources().getDimensionPixelSize(R.dimen.grid_padding)));
Chuck Liaoca97c572020-05-08 17:13:25 +0800441 mImageGrid.setOnApplyWindowInsetsListener((v, windowInsets) -> {
442 v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(),
443 windowInsets.getSystemWindowInsetBottom());
444 return windowInsets;
445 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800446
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700447 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800448 setUpBottomSheet();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800449 return view;
450 }
451
452 @Override
Chuck Liao8ec38e02020-02-26 20:59:32 +0800453 public void onDestroyView() {
Chuck Liao342f4ab2020-05-26 18:28:09 +0800454 updateThumbnail(null);
Chuck Liao8ec38e02020-02-26 20:59:32 +0800455 super.onDestroyView();
456 }
457
458 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800459 public void onClickTryAgain(@Destination int unused) {
460 if (mPendingSetIndividualHolder != null) {
461 mPendingSetIndividualHolder.setWallpaper();
462 }
463 }
464
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800465 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800466 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
467 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
468 R.dimen.current_wallpaper_bottom_sheet_layout_height);
469 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
470 // Only left and top may be set in order for the GridMarginDecoration to work properly.
471 mImageGrid.setPadding(
472 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
473 }
474
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700475 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700476 // Skip if mImageGrid been initialized yet
477 if (mImageGrid == null) {
478 return;
479 }
480 // Skip if category hasn't loaded yet
481 if (mCategory == null) {
482 return;
483 }
484 // Skip if the adapter was already created
485 if (mAdapter != null) {
486 return;
487 }
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700488 setUpImageGrid();
489 }
490
491 /**
492 * Create the adapter and assign it to mImageGrid.
493 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
494 */
495 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800496 mAdapter = new IndividualAdapter(mWallpapers);
497 mImageGrid.setAdapter(mAdapter);
498 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
499 }
500
Jon Miranda16ea1b12017-12-12 14:52:48 -0800501 /**
502 * Enables and populates the "Currently set" wallpaper BottomSheet.
503 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800504 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800505 mImageGrid.addOnScrollListener(new OnScrollListener() {
506 @Override
507 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
508 if (mCurrentWallpaperBottomSheetPresenter == null) {
509 return;
510 }
511
512 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
513 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
514 }
515 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
516 @Override
517 public void run() {
518 if (dy > 0) {
519 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
520 } else {
521 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
522 }
523 }
524 };
525 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
526 }
527 });
528 }
529
chihhangchuang3efb6832020-04-17 02:06:25 +0800530 @Override
531 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Chuck Liao342f4ab2020-05-26 18:28:09 +0800532 mBottomActionBar = bottomActionBar;
533 if (isRotationEnabled()) {
534 mBottomActionBar.showActionsOnly(ROTATION);
Ching-Sung Li073812b2020-04-07 21:19:21 +0800535 }
Chuck Liao342f4ab2020-05-26 18:28:09 +0800536 mBottomActionBar.setActionClickListener(ROTATION, unused -> {
537 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
538 startRotationDialogFragment.setTargetFragment(
539 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
540 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
541 });
542 mBottomActionBar.setActionClickListener(APPLY, unused -> {
543 mBottomActionBar.disableActions();
544 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
545 mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
546 });
547
548 mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
549 .inflate(R.layout.wallpaper_info_view, /* root= */ null);
550 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
551 mBottomActionBar.setActionClickListener(EDIT, unused -> {
552 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
553 mSelectedWallpaperInfo.showPreview(getActivity(),
554 new PreviewActivity.PreviewActivityIntentFactory(),
555 PREVIEW_WALLPAPER_REQUEST_CODE);
556 });
557 mBottomActionBar.show();
Ching-Sung Li073812b2020-04-07 21:19:21 +0800558 }
559
Jon Miranda16ea1b12017-12-12 14:52:48 -0800560 @Override
561 public void onResume() {
562 super.onResume();
563
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700564 WallpaperPreferences preferences = InjectorProvider.getInjector()
565 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800566 preferences.setLastAppActiveTimestamp(new Date().getTime());
567
568 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
569 // PreviewFragment.
570 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
571
572 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
573 // shown earlier when this fragment's hosting activity didn't allow committing fragment
574 // transactions.
575 if (mStagedStartRotationErrorDialogFragment != null) {
576 mStagedStartRotationErrorDialogFragment.show(
577 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
578 mStagedStartRotationErrorDialogFragment = null;
579 }
580
581 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
582 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
583 // committing fragment transactions.
584 if (mStagedSetWallpaperErrorDialogFragment != null) {
585 mStagedSetWallpaperErrorDialogFragment.show(
586 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
587 mStagedSetWallpaperErrorDialogFragment = null;
588 }
589
Chuck Liao5a4243b2020-05-20 23:56:39 +0800590 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
591 // Must be resuming from a previously stopped state, so re-schedule the update of the
592 // daily wallpapers tile thumbnail.
593 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800594 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800595 }
596
597 @Override
598 public void onStop() {
599 super.onStop();
600 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
601 }
602
603 @Override
604 public void onDestroy() {
605 super.onDestroy();
606 if (mProgressDialog != null) {
607 mProgressDialog.dismiss();
608 }
609 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700610 if (mAppStatusListener != null) {
611 mPackageStatusNotifier.removeListener(mAppStatusListener);
612 }
Chuck Liao69630f12020-03-05 19:01:25 +0800613 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800614 }
615
616 @Override
617 public void retryStartRotation(@NetworkPreference int networkPreference) {
618 startRotation(networkPreference);
619 }
620
chihhangchuang7feb3752020-04-24 02:48:56 +0800621 @Override
622 public boolean onBackPressed() {
623 if (mSelectedWallpaperInfo != null) {
624 onWallpaperSelected(null, 0);
625 return true;
626 }
627 return false;
628 }
629
Jon Miranda16ea1b12017-12-12 14:52:48 -0800630 public void setCurrentWallpaperBottomSheetPresenter(
631 CurrentWallpaperBottomSheetPresenter presenter) {
632 mCurrentWallpaperBottomSheetPresenter = presenter;
633 }
634
635 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
636 mWallpapersUiContainer = uiContainer;
637 }
638
Chuck Liaob3829fb2020-04-01 00:47:50 +0800639 public void setOnWallpaperSelectedListener(
640 WallpaperSelectedListener wallpaperSelectedListener) {
641 mWallpaperSelectedListener = wallpaperSelectedListener;
642 }
643
644 /**
645 * Resizes the layout's height.
646 */
647 public void resizeLayout(int height) {
648 mImageGrid.getLayoutParams().height = height;
649 mImageGrid.requestLayout();
650 }
651
652 /**
653 * Scrolls to the specific item.
654 *
655 * @param position the position of the item
656 */
657 public void scrollToPosition(int position) {
658 ((GridLayoutManager) mImageGrid.getLayoutManager())
659 .scrollToPositionWithOffset(position, /* offset= */ 0);
660 }
661
Jon Miranda16ea1b12017-12-12 14:52:48 -0800662 /**
663 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
664 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
665 * constantly keeps the UI thread alive and blocks a test forever.
666 *
667 * @param testingMode
668 */
669 void setTestingMode(boolean testingMode) {
670 mTestingMode = testingMode;
671 }
672
Jon Miranda16ea1b12017-12-12 14:52:48 -0800673 @Override
674 public void startRotation(@NetworkPreference final int networkPreference) {
675 if (!isRotationEnabled()) {
676 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
677 return;
678 }
679
680 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
681 // causes Espresso to hang once the dialog is shown.
682 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
683 int themeResId;
684 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
685 themeResId = R.style.ProgressDialogThemePreL;
686 } else {
687 themeResId = R.style.LightDialogTheme;
688 }
689 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
690
691 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
692 mProgressDialog.setMessage(
693 getResources().getString(R.string.start_rotation_progress_message));
694 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
695 mProgressDialog.show();
696 }
697
698 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
699 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
700 }
701
702 final Context appContext = getActivity().getApplicationContext();
703
704 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
705 appContext,
706 networkPreference,
707 new Listener() {
708 @Override
709 public void onFirstWallpaperInRotationSet() {
710 if (mProgressDialog != null) {
711 mProgressDialog.dismiss();
712 }
713
714 // The fragment may be detached from its containing activity if the user exits the
715 // app before the first wallpaper image in rotation finishes downloading.
716 Activity activity = getActivity();
717
Jon Miranda16ea1b12017-12-12 14:52:48 -0800718
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700719 if (mWallpaperRotationInitializer.startRotation(appContext)) {
720 if (activity != null
721 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
722 try {
723 Toast.makeText(getActivity(),
724 R.string.wallpaper_set_successfully_message,
725 Toast.LENGTH_SHORT).show();
726 } catch (NotFoundException e) {
727 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800728 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800729
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700730 activity.setResult(Activity.RESULT_OK);
731 activity.finish();
732 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
733 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
734 }
735 } else { // Failed to start rotation.
736 showStartRotationErrorDialog(networkPreference);
737
738 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
739 DesktopRotationHolder rotationViewHolder =
740 (DesktopRotationHolder)
741 mImageGrid.findViewHolderForAdapterPosition(
742 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
743 rotationViewHolder.setSelectionState(
744 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800745 }
746 }
747 }
748
749 @Override
750 public void onError() {
751 if (mProgressDialog != null) {
752 mProgressDialog.dismiss();
753 }
754
755 showStartRotationErrorDialog(networkPreference);
756
757 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
758 DesktopRotationHolder rotationViewHolder =
759 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
760 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
761 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
762 }
763 }
764 });
765 }
766
767 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
768 BaseActivity activity = (BaseActivity) getActivity();
769 if (activity != null) {
770 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
771 StartRotationErrorDialogFragment.newInstance(networkPreference);
772 startRotationErrorDialogFragment.setTargetFragment(
773 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
774
775 if (activity.isSafeToCommitFragmentTransaction()) {
776 startRotationErrorDialogFragment.show(
777 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
778 } else {
779 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
780 }
781 }
782 }
783
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800784 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800785 Activity activity = getActivity();
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700786 return activity == null ? 0 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800787 }
788
789 /**
790 * Returns whether rotation is enabled for this category.
791 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800792 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700793 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800794 }
795
796 @Override
797 public void onCurrentWallpaperRefreshed() {
798 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
799 }
800
Chuck Liao69630f12020-03-05 19:01:25 +0800801 @Override
802 public void onSet(int destination) {
803 if (mSelectedWallpaperInfo == null) {
804 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
805 return;
806 }
807
“Chuckffd832c2020-03-22 02:15:58 +0800808 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800809 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
810 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
811 destination, 0, null, mSetWallpaperCallback);
812 } else {
813 mWallpaperSetter.setCurrentWallpaper(
814 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
815 }
“Chuck7ef99722020-03-22 04:34:03 +0800816 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800817 }
818
819 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
820 new WallpaperPersister.SetWallpaperCallback() {
821 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800822 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800823 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800824 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
825 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800826 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
827 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800828 }
829
830 @Override
831 public void onError(@Nullable Throwable throwable) {
832 Log.e(TAG, "Can't apply the wallpaper.");
833 mBottomActionBar.enableActions();
834 }
835 };
836
837 @Override
838 public void onDialogDismissed(boolean withItemSelected) {
839 if (!withItemSelected) {
840 mBottomActionBar.enableActions();
841 }
842 }
843
Jon Miranda16ea1b12017-12-12 14:52:48 -0800844 /**
845 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
846 */
847 private void showSetWallpaperErrorDialog() {
848 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
849 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
850 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
851
852 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
853 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
854 } else {
855 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
856 }
857 }
858
Ching-Sung Li073812b2020-04-07 21:19:21 +0800859 void updateBottomActions(boolean hasWallpaperSelected) {
chihhangchuang803ea9a2020-04-21 13:03:10 +0800860 if (hasWallpaperSelected) {
Chuck Liao7dc21572020-05-14 18:37:36 +0800861 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800862 } else {
chihhangchuang08abb582020-04-27 17:20:31 +0800863 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800864 }
Chuck Liao69630f12020-03-05 19:01:25 +0800865 }
866
867 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800868 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800869 if (thumbnailUpdater == null) {
870 return;
871 }
872
873 if (selectedWallpaperInfo != null) {
874 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
875 } else {
876 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800877 }
878 }
879
“Chuck7ef99722020-03-22 04:34:03 +0800880 private void onWallpaperDestinationSet(int destination) {
881 WallpaperDestinationCallback wallpaperDestinationCallback =
882 (WallpaperDestinationCallback) getParentFragment();
883 if (wallpaperDestinationCallback == null) {
884 return;
885 }
886
887 wallpaperDestinationCallback.onDestinationSet(destination);
888 }
889
Ching-Sung Li073812b2020-04-07 21:19:21 +0800890 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800891 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800892 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800893 return;
894 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800895 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +0800896 updateActivatedStatus(mSelectedWallpaperInfo == null
897 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800898 // Update new selected wallpaper.
899 updateActivatedStatus(newSelectedWallpaperInfo == null
900 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
901
902 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
903 updateBottomActions(mSelectedWallpaperInfo != null);
904 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +0800905 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +0800906 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +0800907 WallpaperInfoHelper.loadExploreIntent(
908 getContext(),
909 mSelectedWallpaperInfo,
910 (actionLabel, exploreIntent) ->
911 mWallpaperInfoView.populateWallpaperInfo(
912 mSelectedWallpaperInfo, actionLabel, exploreIntent,
913 v -> onExploreClicked(exploreIntent)));
Chuck Liaob3829fb2020-04-01 00:47:50 +0800914 }
915
916 if (mWallpaperSelectedListener != null) {
917 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800918 }
Chuck Liaof40063f2020-03-03 18:35:24 +0800919 }
920
chihhangchuangc687d912020-05-04 14:33:05 +0800921 private void onExploreClicked(Intent exploreIntent) {
922 if (getContext() == null) {
923 return;
924 }
925 Context context = getContext();
926 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
927 mSelectedWallpaperInfo.getActionLabelRes(context));
928
929 startActivity(exploreIntent);
930 }
931
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800932 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +0800933 if (wallpaperInfo == null) {
934 return;
935 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800936 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800937 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800938 ? index + 1 : index;
939 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
940 if (holder != null) {
941 holder.itemView.setActivated(isActivated);
942 } else {
943 // Item is not visible, make sure the item is re-bound when it becomes visible.
944 mAdapter.notifyItemChanged(index);
945 }
946 }
947
“Chuckffd832c2020-03-22 02:15:58 +0800948 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
949 if (wallpaperInfo == null) {
950 return;
951 }
952 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800953 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +0800954 ? index + 1 : index;
955 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
956 if (holder != null) {
957 holder.itemView.findViewById(R.id.check_circle)
958 .setVisibility(isApplied ? View.VISIBLE : View.GONE);
959 } else {
960 // Item is not visible, make sure the item is re-bound when it becomes visible.
961 mAdapter.notifyItemChanged(index);
962 }
963 }
964
Chuck Liao11f4a762020-04-08 13:24:43 +0800965 private void refreshAppliedWallpaper() {
966 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
967 showCheckMarkAndBorderForAppliedWallpaper(false);
968
969 // Update to the new applied wallpaper.
970 String appliedWallpaperId = getAppliedWallpaperId();
971 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
972 .stream()
973 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
974 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
975 .findFirst();
976 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
977
978 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
979 showCheckMarkAndBorderForAppliedWallpaper(true);
980 }
981
982 private String getAppliedWallpaperId() {
983 WallpaperPreferences prefs =
984 InjectorProvider.getInjector().getPreferences(getContext());
985 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
986 boolean isDestinationBoth =
987 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
988
989 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
990 return wallpaperInfo != null
991 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
992 } else {
993 return prefs.getLockWallpaperRemoteId();
994 }
995 }
996
997 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
998 updateAppliedStatus(mAppliedWallpaperInfo, show);
999 if (mSelectedWallpaperInfo == null) {
1000 updateActivatedStatus(mAppliedWallpaperInfo, show);
1001 }
1002 }
1003
Chuck Liao5a4243b2020-05-20 23:56:39 +08001004 private boolean shouldShowRotationTile() {
1005 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001006 }
1007
1008 /**
1009 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1010 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001011 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1012 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1013 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1014 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001015
1016 private final List<WallpaperInfo> mWallpapers;
1017
1018 private int mPendingSelectedAdapterPosition;
1019 private int mSelectedAdapterPosition;
1020
1021 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1022 mWallpapers = wallpapers;
1023 mPendingSelectedAdapterPosition = -1;
1024 mSelectedAdapterPosition = -1;
1025 }
1026
1027 @Override
1028 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1029 switch (viewType) {
1030 case ITEM_VIEW_TYPE_ROTATION:
1031 return createRotationHolder(parent);
1032 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1033 return createIndividualHolder(parent);
1034 case ITEM_VIEW_TYPE_MY_PHOTOS:
1035 return createMyPhotosHolder(parent);
1036 default:
1037 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1038 return null;
1039 }
1040 }
1041
1042 @Override
1043 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001044 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001045 return ITEM_VIEW_TYPE_ROTATION;
1046 }
1047
1048 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1049 if (mCategory.supportsCustomPhotos()
1050 && !isRotationEnabled()
1051 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1052 return ITEM_VIEW_TYPE_MY_PHOTOS;
1053 }
1054
1055 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1056 }
1057
1058 @Override
1059 public void onBindViewHolder(ViewHolder holder, int position) {
1060 int viewType = getItemViewType(position);
1061
1062 switch (viewType) {
1063 case ITEM_VIEW_TYPE_ROTATION:
1064 onBindRotationHolder(holder, position);
1065 break;
1066 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1067 onBindIndividualHolder(holder, position);
1068 break;
1069 case ITEM_VIEW_TYPE_MY_PHOTOS:
1070 ((MyPhotosViewHolder) holder).bind();
1071 break;
1072 default:
1073 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1074 }
1075 }
1076
1077 @Override
1078 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001079 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001080 ? mWallpapers.size() + 1
1081 : mWallpapers.size();
1082 }
1083
1084 private ViewHolder createRotationHolder(ViewGroup parent) {
1085 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001086 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
1087 SelectionAnimator selectionAnimator =
1088 new CheckmarkSelectionAnimator(getActivity(), view);
1089 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1090 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001091 }
1092
1093 private ViewHolder createIndividualHolder(ViewGroup parent) {
1094 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1095 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1096
1097 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1098 SelectionAnimator selectionAnimator =
1099 new CheckmarkSelectionAnimator(getActivity(), view);
1100 return new SetIndividualHolder(
1101 getActivity(), mTileSizePx.y, view,
1102 selectionAnimator,
1103 new OnSetListener() {
1104 @Override
1105 public void onPendingWallpaperSet(int adapterPosition) {
1106 // Deselect and hide loading indicator for any previously pending tile.
1107 if (mPendingSelectedAdapterPosition != -1) {
1108 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1109 mPendingSelectedAdapterPosition);
1110 if (oldViewHolder instanceof SelectableHolder) {
1111 ((SelectableHolder) oldViewHolder).setSelectionState(
1112 SelectableHolder.SELECTION_STATE_DESELECTED);
1113 }
1114 }
1115
1116 if (mSelectedAdapterPosition != -1) {
1117 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1118 mSelectedAdapterPosition);
1119 if (oldViewHolder instanceof SelectableHolder) {
1120 ((SelectableHolder) oldViewHolder).setSelectionState(
1121 SelectableHolder.SELECTION_STATE_DESELECTED);
1122 }
1123 }
1124
1125 mPendingSelectedAdapterPosition = adapterPosition;
1126 }
1127
1128 @Override
1129 public void onWallpaperSet(int adapterPosition) {
1130 // No-op -- UI handles a new wallpaper being set by reacting to the
1131 // WallpaperChangedNotifier.
1132 }
1133
1134 @Override
1135 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1136 showSetWallpaperErrorDialog();
1137 mPendingSetIndividualHolder = holder;
1138 }
1139 });
1140 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001141 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001142 }
1143 }
1144
1145 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1146 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1147 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1148
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001149 return new MyPhotosViewHolder(getActivity(),
1150 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1151 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001152 }
1153
1154 /**
1155 * Marks the tile at the given position as selected with a visual indication. Also updates the
1156 * "currently selected" BottomSheet to reflect the newly selected tile.
1157 */
1158 private void updateSelectedTile(int newlySelectedPosition) {
1159 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1160 // succession.
1161 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1162 return;
1163 }
1164
1165 if (mCurrentWallpaperBottomSheetPresenter != null) {
1166 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1167 IndividualPickerFragment.this);
1168
1169 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1170 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1171 }
1172 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1173 @Override
1174 public void run() {
1175 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1176 }
1177 };
1178 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1179 }
1180
1181 // User may have switched to another category, thus detaching this fragment, so check here.
1182 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1183 // still occurs in the UI after the user selects that other category.
1184 if (getActivity() == null) {
1185 return;
1186 }
1187
1188 // Update the newly selected wallpaper ViewHolder and the old one so that if
1189 // selection UI state applies (desktop UI), it is updated.
1190 if (mSelectedAdapterPosition >= 0) {
1191 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1192 mSelectedAdapterPosition);
1193 if (oldViewHolder instanceof SelectableHolder) {
1194 ((SelectableHolder) oldViewHolder).setSelectionState(
1195 SelectableHolder.SELECTION_STATE_DESELECTED);
1196 }
1197 }
1198
1199 // Animate selection of newly selected tile.
1200 ViewHolder newViewHolder = mImageGrid
1201 .findViewHolderForAdapterPosition(newlySelectedPosition);
1202 if (newViewHolder instanceof SelectableHolder) {
1203 ((SelectableHolder) newViewHolder).setSelectionState(
1204 SelectableHolder.SELECTION_STATE_SELECTED);
1205 }
1206
1207 mSelectedAdapterPosition = newlySelectedPosition;
1208
1209 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1210 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1211 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1212 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1213 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1214 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1215
1216 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1217 }
1218
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001219 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001220 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1221 String collectionId = mCategory.getCollectionId();
1222 ((DesktopRotationHolder) holder).bind(collectionId);
1223
1224 if (mWallpaperPreferences.getWallpaperPresentationMode()
1225 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1226 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1227 mSelectedAdapterPosition = position;
1228 }
1229
1230 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1231 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1232 mWasUpdateRunnableRun = true;
1233 }
1234 }
1235 }
1236
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001237 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001238 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001239 ? position - 1 : position;
1240 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1241 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001242 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001243 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1244 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1245 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001246
“Chuckffd832c2020-03-22 02:15:58 +08001247 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001248 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001249 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001250 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001251
Chuck Liao342f4ab2020-05-26 18:28:09 +08001252 holder.itemView.setActivated(
1253 (isWallpaperApplied && !hasUserSelectedWallpaper) || isWallpaperSelected);
1254 holder.itemView.findViewById(R.id.check_circle).setVisibility(
1255 isWallpaperApplied ? View.VISIBLE : View.GONE);
1256 holder.itemView.findViewById(R.id.tile).setOnClickListener(
1257 view -> onWallpaperSelected(wallpaper, position));
Jon Miranda16ea1b12017-12-12 14:52:48 -08001258 }
1259 }
Chuck Liaoba401232020-03-13 20:11:04 +08001260
1261 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1262
1263 private int mPadding;
1264
1265 GridPaddingDecoration(int padding) {
1266 mPadding = padding;
1267 }
1268
1269 @Override
1270 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1271 RecyclerView.State state) {
1272 int position = parent.getChildAdapterPosition(view);
1273 if (position >= 0) {
1274 outRect.left = mPadding;
1275 outRect.right = mPadding;
1276 }
1277 }
1278 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001279}