blob: cbc01f3d0a011cd1f974b232eda8b13a62379365 [file] [log] [blame]
Jon Miranda16ea1b12017-12-12 14:52:48 -08001/*
2 * Copyright (C) 2017 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16package com.android.wallpaper.picker.individual;
17
Chuck Liao7dc21572020-05-14 18:37:36 +080018import static com.android.wallpaper.picker.WallpaperPickerDelegate.PREVIEW_WALLPAPER_REQUEST_CODE;
Chuck Liao69630f12020-03-05 19:01:25 +080019import static com.android.wallpaper.widget.BottomActionBar.BottomAction.APPLY;
Chuck Liao7dc21572020-05-14 18:37:36 +080020import static com.android.wallpaper.widget.BottomActionBar.BottomAction.EDIT;
chihhangchuang22aa0cc2020-03-25 19:12:42 +080021import static com.android.wallpaper.widget.BottomActionBar.BottomAction.INFORMATION;
Chuck Liao8ec38e02020-02-26 20:59:32 +080022import static com.android.wallpaper.widget.BottomActionBar.BottomAction.ROTATION;
23
Jon Miranda16ea1b12017-12-12 14:52:48 -080024import android.app.Activity;
25import android.app.ProgressDialog;
“Chuck7ef99722020-03-22 04:34:03 +080026import android.app.WallpaperManager;
Jon Miranda16ea1b12017-12-12 14:52:48 -080027import android.content.Context;
Chihhang Chuang85f099a2020-06-16 18:04:39 +080028import android.content.DialogInterface;
chihhangchuangc687d912020-05-04 14:33:05 +080029import android.content.Intent;
Santiago Etchebehereb1854472019-06-06 17:44:54 -070030import android.content.res.Configuration;
Jon Miranda16ea1b12017-12-12 14:52:48 -080031import android.content.res.Resources.NotFoundException;
32import android.graphics.Point;
Chuck Liaoba401232020-03-13 20:11:04 +080033import android.graphics.Rect;
Jon Miranda16ea1b12017-12-12 14:52:48 -080034import android.os.Build.VERSION;
35import android.os.Build.VERSION_CODES;
36import android.os.Bundle;
37import android.os.Handler;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070038import android.service.wallpaper.WallpaperService;
Jon Miranda16ea1b12017-12-12 14:52:48 -080039import android.util.Log;
40import android.view.LayoutInflater;
41import android.view.View;
42import android.view.ViewGroup;
Jon Miranda16ea1b12017-12-12 14:52:48 -080043import android.widget.Toast;
44
Santiago Etchebehereb1854472019-06-06 17:44:54 -070045import androidx.annotation.NonNull;
Chuck Liao69630f12020-03-05 19:01:25 +080046import androidx.annotation.Nullable;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080047import androidx.fragment.app.DialogFragment;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080048import androidx.recyclerview.widget.GridLayoutManager;
49import androidx.recyclerview.widget.RecyclerView;
50import androidx.recyclerview.widget.RecyclerView.OnScrollListener;
51import androidx.recyclerview.widget.RecyclerView.ViewHolder;
52
Jon Miranda16ea1b12017-12-12 14:52:48 -080053import com.android.wallpaper.R;
54import com.android.wallpaper.asset.Asset;
55import com.android.wallpaper.asset.Asset.DrawableLoadedListener;
Santiago Etchebehere8648bb82019-08-06 17:09:02 -070056import com.android.wallpaper.model.Category;
57import com.android.wallpaper.model.CategoryProvider;
58import com.android.wallpaper.model.CategoryReceiver;
Chuck Liao69630f12020-03-05 19:01:25 +080059import com.android.wallpaper.model.LiveWallpaperInfo;
Jon Miranda16ea1b12017-12-12 14:52:48 -080060import com.android.wallpaper.model.WallpaperCategory;
61import com.android.wallpaper.model.WallpaperInfo;
62import com.android.wallpaper.model.WallpaperReceiver;
63import com.android.wallpaper.model.WallpaperRotationInitializer;
64import com.android.wallpaper.model.WallpaperRotationInitializer.Listener;
65import com.android.wallpaper.model.WallpaperRotationInitializer.NetworkPreference;
Jon Miranda16ea1b12017-12-12 14:52:48 -080066import com.android.wallpaper.module.FormFactorChecker;
67import com.android.wallpaper.module.FormFactorChecker.FormFactor;
68import com.android.wallpaper.module.Injector;
69import com.android.wallpaper.module.InjectorProvider;
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -070070import com.android.wallpaper.module.PackageStatusNotifier;
chihhangchuangc687d912020-05-04 14:33:05 +080071import com.android.wallpaper.module.UserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -080072import com.android.wallpaper.module.WallpaperChangedNotifier;
73import com.android.wallpaper.module.WallpaperPersister;
74import com.android.wallpaper.module.WallpaperPersister.Destination;
75import com.android.wallpaper.module.WallpaperPreferences;
Chuck Liao69630f12020-03-05 19:01:25 +080076import com.android.wallpaper.module.WallpaperSetter;
Jon Miranda16ea1b12017-12-12 14:52:48 -080077import com.android.wallpaper.picker.BaseActivity;
chihhangchuang3efb6832020-04-17 02:06:25 +080078import com.android.wallpaper.picker.BottomActionBarFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080079import com.android.wallpaper.picker.CurrentWallpaperBottomSheetPresenter;
Santiago Etchebeherefab49612019-01-15 12:22:42 -080080import com.android.wallpaper.picker.MyPhotosStarter.MyPhotosStarterProvider;
Chuck Liao7dc21572020-05-14 18:37:36 +080081import com.android.wallpaper.picker.PreviewActivity;
Jon Miranda16ea1b12017-12-12 14:52:48 -080082import com.android.wallpaper.picker.RotationStarter;
Chuck Liao69630f12020-03-05 19:01:25 +080083import com.android.wallpaper.picker.SetWallpaperDialogFragment;
Jon Miranda16ea1b12017-12-12 14:52:48 -080084import com.android.wallpaper.picker.SetWallpaperErrorDialogFragment;
85import com.android.wallpaper.picker.StartRotationDialogFragment;
86import com.android.wallpaper.picker.StartRotationErrorDialogFragment;
chihhangchuangc687d912020-05-04 14:33:05 +080087import com.android.wallpaper.picker.WallpaperInfoHelper;
Jon Miranda16ea1b12017-12-12 14:52:48 -080088import com.android.wallpaper.picker.WallpapersUiContainer;
89import com.android.wallpaper.picker.individual.SetIndividualHolder.OnSetListener;
90import com.android.wallpaper.util.DiskBasedLogger;
Santiago Etchebehere53c63432020-05-07 18:55:35 -070091import com.android.wallpaper.util.SizeCalculator;
Chuck Liao8ec38e02020-02-26 20:59:32 +080092import com.android.wallpaper.widget.BottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +080093import com.android.wallpaper.widget.WallpaperInfoView;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070094
Jon Miranda16ea1b12017-12-12 14:52:48 -080095import com.bumptech.glide.Glide;
96import com.bumptech.glide.MemoryCategory;
97
98import java.util.ArrayList;
99import java.util.Date;
100import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800101import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800102import java.util.Random;
103
104/**
105 * Displays the Main UI for picking an individual wallpaper image.
106 */
chihhangchuang3efb6832020-04-17 02:06:25 +0800107public class IndividualPickerFragment extends BottomActionBarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800108 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
109 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800110 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.Listener,
111 StartRotationDialogFragment.Listener {
Chuck Liao46644b92020-06-08 14:20:50 +0800112
113 public static final boolean NEW_SCROLL_INTERACTION = true;
114
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800115 /**
116 * Position of a special tile that doesn't belong to an individual wallpaper of the category,
117 * such as "my photos" or "daily rotation".
118 */
119 static final int SPECIAL_FIXED_TILE_ADAPTER_POSITION = 0;
120 static final String ARG_CATEGORY_COLLECTION_ID = "category_collection_id";
121
Jon Miranda16ea1b12017-12-12 14:52:48 -0800122 private static final String TAG = "IndividualPickerFrgmnt";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800123 private static final int UNUSED_REQUEST_CODE = 1;
124 private static final String TAG_START_ROTATION_DIALOG = "start_rotation_dialog";
125 private static final String TAG_START_ROTATION_ERROR_DIALOG = "start_rotation_error_dialog";
126 private static final String PROGRESS_DIALOG_NO_TITLE = null;
127 private static final boolean PROGRESS_DIALOG_INDETERMINATE = true;
128 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
129 "individual_set_wallpaper_error_dialog";
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700130 private static final String KEY_NIGHT_MODE = "IndividualPickerFragment.NIGHT_MODE";
Jon Miranda16ea1b12017-12-12 14:52:48 -0800131
Chuck Liao8ec38e02020-02-26 20:59:32 +0800132 /**
Chuck Liaof40063f2020-03-03 18:35:24 +0800133 * An interface for updating the thumbnail with the specific wallpaper.
134 */
135 public interface ThumbnailUpdater {
136 /**
137 * Updates the thumbnail with the specific wallpaper.
138 */
139 void updateThumbnail(WallpaperInfo wallpaperInfo);
140
141 /**
142 * Restores to the thumbnails of the wallpapers which were applied.
143 */
144 void restoreThumbnails();
145 }
146
“Chuck7ef99722020-03-22 04:34:03 +0800147 /**
148 * An interface for receiving the destination of the new applied wallpaper.
149 */
150 public interface WallpaperDestinationCallback {
151 /**
152 * Called when the destination of the wallpaper is set.
153 *
154 * @param destination the destination which a wallpaper may be set.
155 * See {@link Destination} for more details.
156 */
157 void onDestinationSet(@Destination int destination);
158 }
159
Chuck Liaob3829fb2020-04-01 00:47:50 +0800160 /**
161 * The listener which will be notified when the wallpaper is selected.
162 */
163 public interface WallpaperSelectedListener {
164 /**
165 * Called when the wallpaper is selected.
166 *
167 * @param position the position of the selected wallpaper
168 */
169 void onWallpaperSelected(int position);
170 }
171
Ching-Sung Lied6560f2020-05-04 19:25:09 +0800172 WallpaperPersister mWallpaperPersister;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800173 WallpaperPreferences mWallpaperPreferences;
174 WallpaperChangedNotifier mWallpaperChangedNotifier;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800175 RecyclerView mImageGrid;
176 IndividualAdapter mAdapter;
177 WallpaperCategory mCategory;
178 WallpaperRotationInitializer mWallpaperRotationInitializer;
179 List<WallpaperInfo> mWallpapers;
180 Point mTileSizePx;
181 WallpapersUiContainer mWallpapersUiContainer;
182 @FormFactor
183 int mFormFactor;
184 PackageStatusNotifier mPackageStatusNotifier;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800185
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800186 Handler mHandler;
187 Random mRandom;
188
189 WallpaperChangedNotifier.Listener mWallpaperChangedListener =
190 new WallpaperChangedNotifier.Listener() {
191 @Override
192 public void onWallpaperChanged() {
193 if (mFormFactor != FormFactorChecker.FORM_FACTOR_DESKTOP) {
194 return;
195 }
196
197 ViewHolder selectedViewHolder = mImageGrid.findViewHolderForAdapterPosition(
198 mAdapter.mSelectedAdapterPosition);
199
200 // Null remote ID => My Photos wallpaper, so deselect whatever was previously selected.
201 if (mWallpaperPreferences.getHomeWallpaperRemoteId() == null) {
202 if (selectedViewHolder instanceof SelectableHolder) {
203 ((SelectableHolder) selectedViewHolder).setSelectionState(
204 SelectableHolder.SELECTION_STATE_DESELECTED);
205 }
206 } else {
207 mAdapter.updateSelectedTile(mAdapter.mPendingSelectedAdapterPosition);
208 }
209 }
210 };
211 PackageStatusNotifier.Listener mAppStatusListener;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800212 BottomActionBar mBottomActionBar;
chihhangchuang1a29e752020-04-28 18:22:53 +0800213 WallpaperInfoView mWallpaperInfoView;
Ching-Sung Li073812b2020-04-07 21:19:21 +0800214 @Nullable WallpaperInfo mSelectedWallpaperInfo;
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800215
chihhangchuangc687d912020-05-04 14:33:05 +0800216 private UserEventLogger mUserEventLogger;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800217 private ProgressDialog mProgressDialog;
218 private boolean mTestingMode;
219 private CurrentWallpaperBottomSheetPresenter mCurrentWallpaperBottomSheetPresenter;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800220 private SetIndividualHolder mPendingSetIndividualHolder;
221
222 /**
223 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
224 * committing fragment transactions.
225 */
226 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
227 private StartRotationErrorDialogFragment mStagedStartRotationErrorDialogFragment;
228
Jon Miranda16ea1b12017-12-12 14:52:48 -0800229 private Runnable mCurrentWallpaperBottomSheetExpandedRunnable;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800230
231 /**
232 * Whether {@code mUpdateDailyWallpaperThumbRunnable} has been run at least once in this
233 * invocation of the fragment.
234 */
235 private boolean mWasUpdateRunnableRun;
236
237 /**
238 * A Runnable which regularly updates the thumbnail for the "Daily wallpapers" tile in desktop
239 * mode.
240 */
241 private Runnable mUpdateDailyWallpaperThumbRunnable = new Runnable() {
242 @Override
243 public void run() {
244 ViewHolder viewHolder = mImageGrid.findViewHolderForAdapterPosition(
245 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
246 if (viewHolder instanceof DesktopRotationHolder) {
247 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) viewHolder);
248 } else { // viewHolder is null
249 // If the rotation tile is unavailable (because user has scrolled down, causing the
250 // ViewHolder to be recycled), schedule the update for some time later. Once user scrolls up
251 // again, the ViewHolder will be re-bound and its thumbnail will be updated.
252 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable,
253 DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
254 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS);
255 }
256 }
257 };
258
Chuck Liao69630f12020-03-05 19:01:25 +0800259 private WallpaperSetter mWallpaperSetter;
“Chuckffd832c2020-03-22 02:15:58 +0800260 private WallpaperInfo mAppliedWallpaperInfo;
“Chuck7ef99722020-03-22 04:34:03 +0800261 private WallpaperManager mWallpaperManager;
262 private int mWallpaperDestination;
Chuck Liaob3829fb2020-04-01 00:47:50 +0800263 private WallpaperSelectedListener mWallpaperSelectedListener;
Chuck Liao8ec38e02020-02-26 20:59:32 +0800264
Jon Miranda16ea1b12017-12-12 14:52:48 -0800265 public static IndividualPickerFragment newInstance(String collectionId) {
266 Bundle args = new Bundle();
267 args.putString(ARG_CATEGORY_COLLECTION_ID, collectionId);
268
269 IndividualPickerFragment fragment = new IndividualPickerFragment();
270 fragment.setArguments(args);
271 return fragment;
272 }
273
“Chuck7ef99722020-03-22 04:34:03 +0800274 /**
275 * Highlights the applied wallpaper (if it exists) according to the destination a wallpaper
276 * would be set.
277 *
278 * @param wallpaperDestination the destination a wallpaper would be set.
279 * It will be either {@link Destination#DEST_HOME_SCREEN}
280 * or {@link Destination#DEST_LOCK_SCREEN}.
281 */
282 public void highlightAppliedWallpaper(@Destination int wallpaperDestination) {
283 mWallpaperDestination = wallpaperDestination;
Chuck Liao11f4a762020-04-08 13:24:43 +0800284 if (mWallpapers != null) {
285 refreshAppliedWallpaper();
“Chuck7ef99722020-03-22 04:34:03 +0800286 }
287 }
288
Jon Miranda16ea1b12017-12-12 14:52:48 -0800289 private void updateDesktopDailyRotationThumbnail(DesktopRotationHolder holder) {
290 int wallpapersIndex = mRandom.nextInt(mWallpapers.size());
291 Asset newThumbnailAsset = mWallpapers.get(wallpapersIndex).getThumbAsset(
292 getActivity());
293 holder.updateThumbnail(newThumbnailAsset, new DrawableLoadedListener() {
294 @Override
295 public void onDrawableLoaded() {
296 if (getActivity() == null) {
297 return;
298 }
299
300 // Schedule the next update of the thumbnail.
301 int delayMillis = DesktopRotationHolder.CROSSFADE_DURATION_MILLIS
302 + DesktopRotationHolder.CROSSFADE_DURATION_PAUSE_MILLIS;
303 mHandler.postDelayed(mUpdateDailyWallpaperThumbRunnable, delayMillis);
304 }
305 });
306 }
307
308 @Override
309 public void onCreate(Bundle savedInstanceState) {
310 super.onCreate(savedInstanceState);
311
312 Injector injector = InjectorProvider.getInjector();
313 Context appContext = getContext().getApplicationContext();
314 mWallpaperPreferences = injector.getPreferences(appContext);
315
316 mWallpaperChangedNotifier = WallpaperChangedNotifier.getInstance();
317 mWallpaperChangedNotifier.registerListener(mWallpaperChangedListener);
318
“Chuck7ef99722020-03-22 04:34:03 +0800319 mWallpaperManager = WallpaperManager.getInstance(appContext);
320
Jon Miranda16ea1b12017-12-12 14:52:48 -0800321 mFormFactor = injector.getFormFactorChecker(appContext).getFormFactor();
322
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700323 mPackageStatusNotifier = injector.getPackageStatusNotifier(appContext);
324
chihhangchuangc687d912020-05-04 14:33:05 +0800325 mUserEventLogger = injector.getUserEventLogger(appContext);
326
“Chuckffd832c2020-03-22 02:15:58 +0800327 mWallpaperPersister = injector.getWallpaperPersister(appContext);
Chuck Liao69630f12020-03-05 19:01:25 +0800328 mWallpaperSetter = new WallpaperSetter(
“Chuckffd832c2020-03-22 02:15:58 +0800329 mWallpaperPersister,
Chuck Liao69630f12020-03-05 19:01:25 +0800330 injector.getPreferences(appContext),
331 injector.getUserEventLogger(appContext),
332 false);
333
Jon Miranda16ea1b12017-12-12 14:52:48 -0800334 mWallpapers = new ArrayList<>();
335 mRandom = new Random();
336 mHandler = new Handler();
337
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700338 // Clear Glide's cache if night-mode changed to ensure thumbnails are reloaded
339 if (savedInstanceState != null && (savedInstanceState.getInt(KEY_NIGHT_MODE)
340 != (getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK))) {
341 Glide.get(getContext()).clearMemory();
342 }
343
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700344 CategoryProvider categoryProvider = injector.getCategoryProvider(appContext);
345 categoryProvider.fetchCategories(new CategoryReceiver() {
346 @Override
347 public void onCategoryReceived(Category category) {
348 // Do nothing.
349 }
350
351 @Override
352 public void doneFetchingCategories() {
353 mCategory = (WallpaperCategory) categoryProvider.getCategory(
354 getArguments().getString(ARG_CATEGORY_COLLECTION_ID));
355 if (mCategory == null) {
356 DiskBasedLogger.e(TAG, "Failed to find the category.", getContext());
357
358 // The absence of this category in the CategoryProvider indicates a broken
359 // state, see b/38030129. Hence, finish the activity and return.
360 getActivity().finish();
361 return;
362 }
363 onCategoryLoaded();
364 }
365 }, false);
366 }
367
368 protected void onCategoryLoaded() {
369 mWallpaperRotationInitializer = mCategory.getWallpaperRotationInitializer();
Chuck Liao58e4a1c2020-05-22 11:35:35 +0800370 // Avoids the "rotation" action is not shown correctly
371 // in a rare case : onCategoryLoaded() is called after onBottomActionBarReady().
372 if (isRotationEnabled() && mBottomActionBar != null
373 && !mBottomActionBar.areActionsShown(ROTATION)) {
374 mBottomActionBar.showActions(ROTATION);
375 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700376 fetchWallpapers(false);
377
378 if (mCategory.supportsThirdParty()) {
379 mAppStatusListener = (packageName, status) -> {
380 if (status != PackageStatusNotifier.PackageStatus.REMOVED ||
381 mCategory.containsThirdParty(packageName)) {
382 fetchWallpapers(true);
383 }
384 };
385 mPackageStatusNotifier.addListener(mAppStatusListener,
386 WallpaperService.SERVICE_INTERFACE);
387 }
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700388
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700389 maybeSetUpImageGrid();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700390 }
391
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800392 void fetchWallpapers(boolean forceReload) {
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700393 mWallpapers.clear();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800394 mCategory.fetchWallpapers(getActivity().getApplicationContext(), new WallpaperReceiver() {
395 @Override
396 public void onWallpapersReceived(List<WallpaperInfo> wallpapers) {
397 for (WallpaperInfo wallpaper : wallpapers) {
398 mWallpapers.add(wallpaper);
399 }
400
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700401 // Wallpapers may load after the adapter is initialized, in which case we have
402 // to explicitly notify that the data set has changed.
Jon Miranda16ea1b12017-12-12 14:52:48 -0800403 if (mAdapter != null) {
404 mAdapter.notifyDataSetChanged();
405 }
406
407 if (mWallpapersUiContainer != null) {
408 mWallpapersUiContainer.onWallpapersReady();
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700409 } else {
410 if (wallpapers.isEmpty()) {
411 // If there are no more wallpapers and we're on phone, just finish the
412 // Activity.
413 Activity activity = getActivity();
414 if (activity != null
415 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
416 activity.finish();
417 }
418 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800419 }
420 }
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700421 }, forceReload);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800422 }
423
424 @Override
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700425 public void onSaveInstanceState(@NonNull Bundle outState) {
426 super.onSaveInstanceState(outState);
Santiago Etchebehere0ec065c2019-06-13 11:30:21 -0700427 outState.putInt(KEY_NIGHT_MODE,
Santiago Etchebehereb1854472019-06-06 17:44:54 -0700428 getResources().getConfiguration().uiMode & Configuration.UI_MODE_NIGHT_MASK);
429 }
430
431 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800432 public View onCreateView(LayoutInflater inflater, ViewGroup container,
433 Bundle savedInstanceState) {
434 View view = inflater.inflate(R.layout.fragment_individual_picker, container, false);
435
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700436 mTileSizePx = SizeCalculator.getIndividualTileSize(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800437
438 mImageGrid = (RecyclerView) view.findViewById(R.id.wallpaper_grid);
439 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
440 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
441 updateImageGridPadding(false /* addExtraBottomSpace */);
442 mImageGrid.setScrollBarSize(gridPaddingPx);
443 }
Chuck Liaoba401232020-03-13 20:11:04 +0800444 mImageGrid.addItemDecoration(new GridPaddingDecoration(
445 getResources().getDimensionPixelSize(R.dimen.grid_padding)));
Chuck Liaoca97c572020-05-08 17:13:25 +0800446 mImageGrid.setOnApplyWindowInsetsListener((v, windowInsets) -> {
447 v.setPadding(v.getPaddingLeft(), v.getPaddingTop(), v.getPaddingRight(),
448 windowInsets.getSystemWindowInsetBottom());
449 return windowInsets;
450 });
Jon Miranda16ea1b12017-12-12 14:52:48 -0800451
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700452 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800453 setUpBottomSheet();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800454 return view;
455 }
456
457 @Override
Chuck Liao8ec38e02020-02-26 20:59:32 +0800458 public void onDestroyView() {
Chuck Liao46644b92020-06-08 14:20:50 +0800459 if (!NEW_SCROLL_INTERACTION) {
460 updateThumbnail(null);
461 }
Chuck Liao8ec38e02020-02-26 20:59:32 +0800462 super.onDestroyView();
463 }
464
465 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800466 public void onClickTryAgain(@Destination int unused) {
467 if (mPendingSetIndividualHolder != null) {
468 mPendingSetIndividualHolder.setWallpaper();
469 }
470 }
471
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800472 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800473 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
474 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
475 R.dimen.current_wallpaper_bottom_sheet_layout_height);
476 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
477 // Only left and top may be set in order for the GridMarginDecoration to work properly.
478 mImageGrid.setPadding(
479 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
480 }
481
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700482 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700483 // Skip if mImageGrid been initialized yet
484 if (mImageGrid == null) {
485 return;
486 }
487 // Skip if category hasn't loaded yet
488 if (mCategory == null) {
489 return;
490 }
491 // Skip if the adapter was already created
492 if (mAdapter != null) {
493 return;
494 }
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700495 setUpImageGrid();
496 }
497
498 /**
499 * Create the adapter and assign it to mImageGrid.
500 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
501 */
502 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800503 mAdapter = new IndividualAdapter(mWallpapers);
504 mImageGrid.setAdapter(mAdapter);
505 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
506 }
507
Jon Miranda16ea1b12017-12-12 14:52:48 -0800508 /**
509 * Enables and populates the "Currently set" wallpaper BottomSheet.
510 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800511 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800512 mImageGrid.addOnScrollListener(new OnScrollListener() {
513 @Override
514 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
515 if (mCurrentWallpaperBottomSheetPresenter == null) {
516 return;
517 }
518
519 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
520 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
521 }
522 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
523 @Override
524 public void run() {
525 if (dy > 0) {
526 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
527 } else {
528 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
529 }
530 }
531 };
532 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
533 }
534 });
535 }
536
chihhangchuang3efb6832020-04-17 02:06:25 +0800537 @Override
538 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Chuck Liao342f4ab2020-05-26 18:28:09 +0800539 mBottomActionBar = bottomActionBar;
540 if (isRotationEnabled()) {
541 mBottomActionBar.showActionsOnly(ROTATION);
Ching-Sung Li073812b2020-04-07 21:19:21 +0800542 }
Chuck Liao342f4ab2020-05-26 18:28:09 +0800543 mBottomActionBar.setActionClickListener(ROTATION, unused -> {
544 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
545 startRotationDialogFragment.setTargetFragment(
546 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
547 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
548 });
549 mBottomActionBar.setActionClickListener(APPLY, unused -> {
550 mBottomActionBar.disableActions();
551 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
552 mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
553 });
554
555 mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
556 .inflate(R.layout.wallpaper_info_view, /* root= */ null);
557 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
558 mBottomActionBar.setActionClickListener(EDIT, unused -> {
559 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
560 mSelectedWallpaperInfo.showPreview(getActivity(),
561 new PreviewActivity.PreviewActivityIntentFactory(),
562 PREVIEW_WALLPAPER_REQUEST_CODE);
563 });
564 mBottomActionBar.show();
Ching-Sung Li073812b2020-04-07 21:19:21 +0800565 }
566
Jon Miranda16ea1b12017-12-12 14:52:48 -0800567 @Override
568 public void onResume() {
569 super.onResume();
570
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700571 WallpaperPreferences preferences = InjectorProvider.getInjector()
572 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800573 preferences.setLastAppActiveTimestamp(new Date().getTime());
574
575 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
576 // PreviewFragment.
577 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
578
579 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
580 // shown earlier when this fragment's hosting activity didn't allow committing fragment
581 // transactions.
582 if (mStagedStartRotationErrorDialogFragment != null) {
583 mStagedStartRotationErrorDialogFragment.show(
584 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
585 mStagedStartRotationErrorDialogFragment = null;
586 }
587
588 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
589 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
590 // committing fragment transactions.
591 if (mStagedSetWallpaperErrorDialogFragment != null) {
592 mStagedSetWallpaperErrorDialogFragment.show(
593 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
594 mStagedSetWallpaperErrorDialogFragment = null;
595 }
596
Chuck Liao5a4243b2020-05-20 23:56:39 +0800597 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
598 // Must be resuming from a previously stopped state, so re-schedule the update of the
599 // daily wallpapers tile thumbnail.
600 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800601 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800602 }
603
604 @Override
605 public void onStop() {
606 super.onStop();
607 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
608 }
609
610 @Override
611 public void onDestroy() {
612 super.onDestroy();
613 if (mProgressDialog != null) {
614 mProgressDialog.dismiss();
615 }
616 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700617 if (mAppStatusListener != null) {
618 mPackageStatusNotifier.removeListener(mAppStatusListener);
619 }
Chuck Liao69630f12020-03-05 19:01:25 +0800620 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800621 }
622
623 @Override
Chihhang Chuang85f099a2020-06-16 18:04:39 +0800624 public void onStartRotationDialogDismiss(@NonNull DialogInterface dialog) {
625 mBottomActionBar.setActionSelected(ROTATION, false /* selected */);
626 }
627
628 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800629 public void retryStartRotation(@NetworkPreference int networkPreference) {
630 startRotation(networkPreference);
631 }
632
chihhangchuang7feb3752020-04-24 02:48:56 +0800633 @Override
634 public boolean onBackPressed() {
635 if (mSelectedWallpaperInfo != null) {
636 onWallpaperSelected(null, 0);
637 return true;
638 }
639 return false;
640 }
641
Jon Miranda16ea1b12017-12-12 14:52:48 -0800642 public void setCurrentWallpaperBottomSheetPresenter(
643 CurrentWallpaperBottomSheetPresenter presenter) {
644 mCurrentWallpaperBottomSheetPresenter = presenter;
645 }
646
647 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
648 mWallpapersUiContainer = uiContainer;
649 }
650
Chuck Liaob3829fb2020-04-01 00:47:50 +0800651 public void setOnWallpaperSelectedListener(
652 WallpaperSelectedListener wallpaperSelectedListener) {
653 mWallpaperSelectedListener = wallpaperSelectedListener;
654 }
655
656 /**
657 * Resizes the layout's height.
658 */
659 public void resizeLayout(int height) {
660 mImageGrid.getLayoutParams().height = height;
661 mImageGrid.requestLayout();
662 }
663
664 /**
665 * Scrolls to the specific item.
666 *
667 * @param position the position of the item
668 */
669 public void scrollToPosition(int position) {
670 ((GridLayoutManager) mImageGrid.getLayoutManager())
671 .scrollToPositionWithOffset(position, /* offset= */ 0);
672 }
673
Jon Miranda16ea1b12017-12-12 14:52:48 -0800674 /**
675 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
676 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
677 * constantly keeps the UI thread alive and blocks a test forever.
678 *
679 * @param testingMode
680 */
681 void setTestingMode(boolean testingMode) {
682 mTestingMode = testingMode;
683 }
684
Jon Miranda16ea1b12017-12-12 14:52:48 -0800685 @Override
686 public void startRotation(@NetworkPreference final int networkPreference) {
687 if (!isRotationEnabled()) {
688 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
689 return;
690 }
691
692 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
693 // causes Espresso to hang once the dialog is shown.
694 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
695 int themeResId;
696 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
697 themeResId = R.style.ProgressDialogThemePreL;
698 } else {
699 themeResId = R.style.LightDialogTheme;
700 }
701 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
702
703 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
704 mProgressDialog.setMessage(
705 getResources().getString(R.string.start_rotation_progress_message));
706 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
707 mProgressDialog.show();
708 }
709
710 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
711 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
712 }
713
714 final Context appContext = getActivity().getApplicationContext();
715
716 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
717 appContext,
718 networkPreference,
719 new Listener() {
720 @Override
721 public void onFirstWallpaperInRotationSet() {
722 if (mProgressDialog != null) {
723 mProgressDialog.dismiss();
724 }
725
726 // The fragment may be detached from its containing activity if the user exits the
727 // app before the first wallpaper image in rotation finishes downloading.
728 Activity activity = getActivity();
729
Jon Miranda16ea1b12017-12-12 14:52:48 -0800730
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700731 if (mWallpaperRotationInitializer.startRotation(appContext)) {
732 if (activity != null
733 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
734 try {
735 Toast.makeText(getActivity(),
736 R.string.wallpaper_set_successfully_message,
737 Toast.LENGTH_SHORT).show();
738 } catch (NotFoundException e) {
739 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800740 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800741
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700742 activity.setResult(Activity.RESULT_OK);
743 activity.finish();
744 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
745 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
746 }
747 } else { // Failed to start rotation.
748 showStartRotationErrorDialog(networkPreference);
749
750 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
751 DesktopRotationHolder rotationViewHolder =
752 (DesktopRotationHolder)
753 mImageGrid.findViewHolderForAdapterPosition(
754 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
755 rotationViewHolder.setSelectionState(
756 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800757 }
758 }
759 }
760
761 @Override
762 public void onError() {
763 if (mProgressDialog != null) {
764 mProgressDialog.dismiss();
765 }
766
767 showStartRotationErrorDialog(networkPreference);
768
769 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
770 DesktopRotationHolder rotationViewHolder =
771 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
772 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
773 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
774 }
775 }
776 });
777 }
778
779 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
780 BaseActivity activity = (BaseActivity) getActivity();
781 if (activity != null) {
782 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
783 StartRotationErrorDialogFragment.newInstance(networkPreference);
784 startRotationErrorDialogFragment.setTargetFragment(
785 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
786
787 if (activity.isSafeToCommitFragmentTransaction()) {
788 startRotationErrorDialogFragment.show(
789 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
790 } else {
791 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
792 }
793 }
794 }
795
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800796 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800797 Activity activity = getActivity();
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700798 return activity == null ? 0 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800799 }
800
801 /**
802 * Returns whether rotation is enabled for this category.
803 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800804 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700805 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800806 }
807
808 @Override
809 public void onCurrentWallpaperRefreshed() {
810 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
811 }
812
Chuck Liao69630f12020-03-05 19:01:25 +0800813 @Override
814 public void onSet(int destination) {
815 if (mSelectedWallpaperInfo == null) {
816 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
817 return;
818 }
819
“Chuckffd832c2020-03-22 02:15:58 +0800820 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800821 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
822 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
823 destination, 0, null, mSetWallpaperCallback);
824 } else {
825 mWallpaperSetter.setCurrentWallpaper(
826 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
827 }
“Chuck7ef99722020-03-22 04:34:03 +0800828 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800829 }
830
831 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
832 new WallpaperPersister.SetWallpaperCallback() {
833 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800834 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800835 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800836 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
837 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800838 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
839 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800840 }
841
842 @Override
843 public void onError(@Nullable Throwable throwable) {
844 Log.e(TAG, "Can't apply the wallpaper.");
845 mBottomActionBar.enableActions();
846 }
847 };
848
849 @Override
850 public void onDialogDismissed(boolean withItemSelected) {
851 if (!withItemSelected) {
852 mBottomActionBar.enableActions();
853 }
854 }
855
Jon Miranda16ea1b12017-12-12 14:52:48 -0800856 /**
857 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
858 */
859 private void showSetWallpaperErrorDialog() {
860 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
861 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
862 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
863
864 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
865 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
866 } else {
867 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
868 }
869 }
870
Ching-Sung Li073812b2020-04-07 21:19:21 +0800871 void updateBottomActions(boolean hasWallpaperSelected) {
chihhangchuang803ea9a2020-04-21 13:03:10 +0800872 if (hasWallpaperSelected) {
Chuck Liao7dc21572020-05-14 18:37:36 +0800873 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800874 } else {
chihhangchuang08abb582020-04-27 17:20:31 +0800875 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800876 }
Chuck Liao69630f12020-03-05 19:01:25 +0800877 }
878
879 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800880 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800881 if (thumbnailUpdater == null) {
882 return;
883 }
884
885 if (selectedWallpaperInfo != null) {
886 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
887 } else {
888 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800889 }
890 }
891
“Chuck7ef99722020-03-22 04:34:03 +0800892 private void onWallpaperDestinationSet(int destination) {
893 WallpaperDestinationCallback wallpaperDestinationCallback =
894 (WallpaperDestinationCallback) getParentFragment();
895 if (wallpaperDestinationCallback == null) {
896 return;
897 }
898
899 wallpaperDestinationCallback.onDestinationSet(destination);
900 }
901
Ching-Sung Li073812b2020-04-07 21:19:21 +0800902 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800903 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800904 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800905 return;
906 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800907 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +0800908 updateActivatedStatus(mSelectedWallpaperInfo == null
909 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800910 // Update new selected wallpaper.
911 updateActivatedStatus(newSelectedWallpaperInfo == null
912 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
913
914 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
915 updateBottomActions(mSelectedWallpaperInfo != null);
916 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +0800917 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +0800918 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +0800919 WallpaperInfoHelper.loadExploreIntent(
920 getContext(),
921 mSelectedWallpaperInfo,
922 (actionLabel, exploreIntent) ->
923 mWallpaperInfoView.populateWallpaperInfo(
924 mSelectedWallpaperInfo, actionLabel, exploreIntent,
925 v -> onExploreClicked(exploreIntent)));
Chuck Liaob3829fb2020-04-01 00:47:50 +0800926 }
927
928 if (mWallpaperSelectedListener != null) {
929 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800930 }
Chuck Liaof40063f2020-03-03 18:35:24 +0800931 }
932
chihhangchuangc687d912020-05-04 14:33:05 +0800933 private void onExploreClicked(Intent exploreIntent) {
934 if (getContext() == null) {
935 return;
936 }
937 Context context = getContext();
938 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
939 mSelectedWallpaperInfo.getActionLabelRes(context));
940
941 startActivity(exploreIntent);
942 }
943
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800944 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +0800945 if (wallpaperInfo == null) {
946 return;
947 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800948 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800949 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800950 ? index + 1 : index;
951 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
952 if (holder != null) {
953 holder.itemView.setActivated(isActivated);
954 } else {
955 // Item is not visible, make sure the item is re-bound when it becomes visible.
956 mAdapter.notifyItemChanged(index);
957 }
958 }
959
“Chuckffd832c2020-03-22 02:15:58 +0800960 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
961 if (wallpaperInfo == null) {
962 return;
963 }
964 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800965 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +0800966 ? index + 1 : index;
967 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
968 if (holder != null) {
969 holder.itemView.findViewById(R.id.check_circle)
970 .setVisibility(isApplied ? View.VISIBLE : View.GONE);
971 } else {
972 // Item is not visible, make sure the item is re-bound when it becomes visible.
973 mAdapter.notifyItemChanged(index);
974 }
975 }
976
Chuck Liao11f4a762020-04-08 13:24:43 +0800977 private void refreshAppliedWallpaper() {
978 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
979 showCheckMarkAndBorderForAppliedWallpaper(false);
980
981 // Update to the new applied wallpaper.
982 String appliedWallpaperId = getAppliedWallpaperId();
983 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
984 .stream()
985 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
986 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
987 .findFirst();
988 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
989
990 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
991 showCheckMarkAndBorderForAppliedWallpaper(true);
992 }
993
994 private String getAppliedWallpaperId() {
995 WallpaperPreferences prefs =
996 InjectorProvider.getInjector().getPreferences(getContext());
997 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
998 boolean isDestinationBoth =
999 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
1000
1001 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1002 return wallpaperInfo != null
1003 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1004 } else {
1005 return prefs.getLockWallpaperRemoteId();
1006 }
1007 }
1008
1009 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1010 updateAppliedStatus(mAppliedWallpaperInfo, show);
1011 if (mSelectedWallpaperInfo == null) {
1012 updateActivatedStatus(mAppliedWallpaperInfo, show);
1013 }
1014 }
1015
Chuck Liao5a4243b2020-05-20 23:56:39 +08001016 private boolean shouldShowRotationTile() {
1017 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001018 }
1019
1020 /**
1021 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1022 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001023 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1024 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1025 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1026 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001027
1028 private final List<WallpaperInfo> mWallpapers;
1029
1030 private int mPendingSelectedAdapterPosition;
1031 private int mSelectedAdapterPosition;
1032
1033 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1034 mWallpapers = wallpapers;
1035 mPendingSelectedAdapterPosition = -1;
1036 mSelectedAdapterPosition = -1;
1037 }
1038
1039 @Override
1040 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1041 switch (viewType) {
1042 case ITEM_VIEW_TYPE_ROTATION:
1043 return createRotationHolder(parent);
1044 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1045 return createIndividualHolder(parent);
1046 case ITEM_VIEW_TYPE_MY_PHOTOS:
1047 return createMyPhotosHolder(parent);
1048 default:
1049 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1050 return null;
1051 }
1052 }
1053
1054 @Override
1055 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001056 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001057 return ITEM_VIEW_TYPE_ROTATION;
1058 }
1059
1060 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1061 if (mCategory.supportsCustomPhotos()
1062 && !isRotationEnabled()
1063 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1064 return ITEM_VIEW_TYPE_MY_PHOTOS;
1065 }
1066
1067 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1068 }
1069
1070 @Override
1071 public void onBindViewHolder(ViewHolder holder, int position) {
1072 int viewType = getItemViewType(position);
1073
1074 switch (viewType) {
1075 case ITEM_VIEW_TYPE_ROTATION:
1076 onBindRotationHolder(holder, position);
1077 break;
1078 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1079 onBindIndividualHolder(holder, position);
1080 break;
1081 case ITEM_VIEW_TYPE_MY_PHOTOS:
1082 ((MyPhotosViewHolder) holder).bind();
1083 break;
1084 default:
1085 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1086 }
1087 }
1088
1089 @Override
1090 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001091 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001092 ? mWallpapers.size() + 1
1093 : mWallpapers.size();
1094 }
1095
1096 private ViewHolder createRotationHolder(ViewGroup parent) {
1097 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001098 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
1099 SelectionAnimator selectionAnimator =
1100 new CheckmarkSelectionAnimator(getActivity(), view);
1101 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1102 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001103 }
1104
1105 private ViewHolder createIndividualHolder(ViewGroup parent) {
1106 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1107 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1108
1109 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1110 SelectionAnimator selectionAnimator =
1111 new CheckmarkSelectionAnimator(getActivity(), view);
1112 return new SetIndividualHolder(
1113 getActivity(), mTileSizePx.y, view,
1114 selectionAnimator,
1115 new OnSetListener() {
1116 @Override
1117 public void onPendingWallpaperSet(int adapterPosition) {
1118 // Deselect and hide loading indicator for any previously pending tile.
1119 if (mPendingSelectedAdapterPosition != -1) {
1120 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1121 mPendingSelectedAdapterPosition);
1122 if (oldViewHolder instanceof SelectableHolder) {
1123 ((SelectableHolder) oldViewHolder).setSelectionState(
1124 SelectableHolder.SELECTION_STATE_DESELECTED);
1125 }
1126 }
1127
1128 if (mSelectedAdapterPosition != -1) {
1129 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1130 mSelectedAdapterPosition);
1131 if (oldViewHolder instanceof SelectableHolder) {
1132 ((SelectableHolder) oldViewHolder).setSelectionState(
1133 SelectableHolder.SELECTION_STATE_DESELECTED);
1134 }
1135 }
1136
1137 mPendingSelectedAdapterPosition = adapterPosition;
1138 }
1139
1140 @Override
1141 public void onWallpaperSet(int adapterPosition) {
1142 // No-op -- UI handles a new wallpaper being set by reacting to the
1143 // WallpaperChangedNotifier.
1144 }
1145
1146 @Override
1147 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1148 showSetWallpaperErrorDialog();
1149 mPendingSetIndividualHolder = holder;
1150 }
1151 });
1152 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001153 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001154 }
1155 }
1156
1157 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1158 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1159 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1160
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001161 return new MyPhotosViewHolder(getActivity(),
1162 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1163 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001164 }
1165
1166 /**
1167 * Marks the tile at the given position as selected with a visual indication. Also updates the
1168 * "currently selected" BottomSheet to reflect the newly selected tile.
1169 */
1170 private void updateSelectedTile(int newlySelectedPosition) {
1171 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1172 // succession.
1173 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1174 return;
1175 }
1176
1177 if (mCurrentWallpaperBottomSheetPresenter != null) {
1178 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1179 IndividualPickerFragment.this);
1180
1181 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1182 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1183 }
1184 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1185 @Override
1186 public void run() {
1187 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1188 }
1189 };
1190 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1191 }
1192
1193 // User may have switched to another category, thus detaching this fragment, so check here.
1194 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1195 // still occurs in the UI after the user selects that other category.
1196 if (getActivity() == null) {
1197 return;
1198 }
1199
1200 // Update the newly selected wallpaper ViewHolder and the old one so that if
1201 // selection UI state applies (desktop UI), it is updated.
1202 if (mSelectedAdapterPosition >= 0) {
1203 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1204 mSelectedAdapterPosition);
1205 if (oldViewHolder instanceof SelectableHolder) {
1206 ((SelectableHolder) oldViewHolder).setSelectionState(
1207 SelectableHolder.SELECTION_STATE_DESELECTED);
1208 }
1209 }
1210
1211 // Animate selection of newly selected tile.
1212 ViewHolder newViewHolder = mImageGrid
1213 .findViewHolderForAdapterPosition(newlySelectedPosition);
1214 if (newViewHolder instanceof SelectableHolder) {
1215 ((SelectableHolder) newViewHolder).setSelectionState(
1216 SelectableHolder.SELECTION_STATE_SELECTED);
1217 }
1218
1219 mSelectedAdapterPosition = newlySelectedPosition;
1220
1221 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1222 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1223 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1224 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1225 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1226 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1227
1228 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1229 }
1230
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001231 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001232 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1233 String collectionId = mCategory.getCollectionId();
1234 ((DesktopRotationHolder) holder).bind(collectionId);
1235
1236 if (mWallpaperPreferences.getWallpaperPresentationMode()
1237 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1238 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1239 mSelectedAdapterPosition = position;
1240 }
1241
1242 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1243 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1244 mWasUpdateRunnableRun = true;
1245 }
1246 }
1247 }
1248
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001249 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001250 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001251 ? position - 1 : position;
1252 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1253 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001254 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001255 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1256 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1257 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001258
“Chuckffd832c2020-03-22 02:15:58 +08001259 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001260 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001261 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001262 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001263
Chuck Liao342f4ab2020-05-26 18:28:09 +08001264 holder.itemView.setActivated(
1265 (isWallpaperApplied && !hasUserSelectedWallpaper) || isWallpaperSelected);
1266 holder.itemView.findViewById(R.id.check_circle).setVisibility(
1267 isWallpaperApplied ? View.VISIBLE : View.GONE);
Chuck Liao46644b92020-06-08 14:20:50 +08001268 if (!NEW_SCROLL_INTERACTION) {
1269 holder.itemView.findViewById(R.id.tile).setOnClickListener(
1270 view -> onWallpaperSelected(wallpaper, position));
1271 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001272 }
1273 }
Chuck Liaoba401232020-03-13 20:11:04 +08001274
1275 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1276
1277 private int mPadding;
1278
1279 GridPaddingDecoration(int padding) {
1280 mPadding = padding;
1281 }
1282
1283 @Override
1284 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1285 RecyclerView.State state) {
1286 int position = parent.getChildAdapterPosition(view);
1287 if (position >= 0) {
1288 outRect.left = mPadding;
1289 outRect.right = mPadding;
1290 }
1291 }
1292 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001293}