blob: 3a4851632cf94d496a62fa00600eaef6c1026b47 [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;
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +080093import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate;
94import com.android.wallpaper.widget.WallpaperPickerRecyclerViewAccessibilityDelegate.BottomSheetHost;
Sunny Goyal8600a3f2018-08-15 12:48:01 -070095
Jon Miranda16ea1b12017-12-12 14:52:48 -080096import com.bumptech.glide.Glide;
97import com.bumptech.glide.MemoryCategory;
98
99import java.util.ArrayList;
100import java.util.Date;
101import java.util.List;
Chuck Liao11f4a762020-04-08 13:24:43 +0800102import java.util.Optional;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800103import java.util.Random;
104
105/**
106 * Displays the Main UI for picking an individual wallpaper image.
107 */
chihhangchuang3efb6832020-04-17 02:06:25 +0800108public class IndividualPickerFragment extends BottomActionBarFragment
Jon Miranda16ea1b12017-12-12 14:52:48 -0800109 implements RotationStarter, StartRotationErrorDialogFragment.Listener,
110 CurrentWallpaperBottomSheetPresenter.RefreshListener,
Chuck Liao69630f12020-03-05 19:01:25 +0800111 SetWallpaperErrorDialogFragment.Listener, SetWallpaperDialogFragment.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
Wesley.CW Wangdc68fde2020-06-15 19:12:33 +0800452 mImageGrid.setAccessibilityDelegateCompat(
453 new WallpaperPickerRecyclerViewAccessibilityDelegate(
454 mImageGrid, (BottomSheetHost) getParentFragment(), getNumColumns()));
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700455 maybeSetUpImageGrid();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800456 setUpBottomSheet();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800457 return view;
458 }
459
460 @Override
Chuck Liao8ec38e02020-02-26 20:59:32 +0800461 public void onDestroyView() {
Chuck Liao46644b92020-06-08 14:20:50 +0800462 if (!NEW_SCROLL_INTERACTION) {
463 updateThumbnail(null);
464 }
Chuck Liao8ec38e02020-02-26 20:59:32 +0800465 super.onDestroyView();
466 }
467
468 @Override
Jon Miranda16ea1b12017-12-12 14:52:48 -0800469 public void onClickTryAgain(@Destination int unused) {
470 if (mPendingSetIndividualHolder != null) {
471 mPendingSetIndividualHolder.setWallpaper();
472 }
473 }
474
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800475 void updateImageGridPadding(boolean addExtraBottomSpace) {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800476 int gridPaddingPx = getResources().getDimensionPixelSize(R.dimen.grid_padding_desktop);
477 int bottomSheetHeightPx = getResources().getDimensionPixelSize(
478 R.dimen.current_wallpaper_bottom_sheet_layout_height);
479 int paddingBottomPx = addExtraBottomSpace ? bottomSheetHeightPx : 0;
480 // Only left and top may be set in order for the GridMarginDecoration to work properly.
481 mImageGrid.setPadding(
482 gridPaddingPx, gridPaddingPx, 0, paddingBottomPx);
483 }
484
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700485 private void maybeSetUpImageGrid() {
Santiago Etchebehere8648bb82019-08-06 17:09:02 -0700486 // Skip if mImageGrid been initialized yet
487 if (mImageGrid == null) {
488 return;
489 }
490 // Skip if category hasn't loaded yet
491 if (mCategory == null) {
492 return;
493 }
494 // Skip if the adapter was already created
495 if (mAdapter != null) {
496 return;
497 }
Santiago Etchebehere460fbcb2019-08-20 11:47:30 -0700498 setUpImageGrid();
499 }
500
501 /**
502 * Create the adapter and assign it to mImageGrid.
503 * Both mImageGrid and mCategory are guaranteed to not be null when this method is called.
504 */
505 void setUpImageGrid() {
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800506 mAdapter = new IndividualAdapter(mWallpapers);
507 mImageGrid.setAdapter(mAdapter);
508 mImageGrid.setLayoutManager(new GridLayoutManager(getActivity(), getNumColumns()));
509 }
510
Jon Miranda16ea1b12017-12-12 14:52:48 -0800511 /**
512 * Enables and populates the "Currently set" wallpaper BottomSheet.
513 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800514 void setUpBottomSheet() {
Jon Miranda16ea1b12017-12-12 14:52:48 -0800515 mImageGrid.addOnScrollListener(new OnScrollListener() {
516 @Override
517 public void onScrolled(RecyclerView recyclerView, int dx, final int dy) {
518 if (mCurrentWallpaperBottomSheetPresenter == null) {
519 return;
520 }
521
522 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
523 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
524 }
525 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
526 @Override
527 public void run() {
528 if (dy > 0) {
529 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(false);
530 } else {
531 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
532 }
533 }
534 };
535 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
536 }
537 });
538 }
539
chihhangchuang3efb6832020-04-17 02:06:25 +0800540 @Override
541 protected void onBottomActionBarReady(BottomActionBar bottomActionBar) {
Chuck Liao342f4ab2020-05-26 18:28:09 +0800542 mBottomActionBar = bottomActionBar;
543 if (isRotationEnabled()) {
544 mBottomActionBar.showActionsOnly(ROTATION);
Ching-Sung Li073812b2020-04-07 21:19:21 +0800545 }
Chuck Liao342f4ab2020-05-26 18:28:09 +0800546 mBottomActionBar.setActionClickListener(ROTATION, unused -> {
547 DialogFragment startRotationDialogFragment = new StartRotationDialogFragment();
548 startRotationDialogFragment.setTargetFragment(
549 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
550 startRotationDialogFragment.show(getFragmentManager(), TAG_START_ROTATION_DIALOG);
551 });
552 mBottomActionBar.setActionClickListener(APPLY, unused -> {
553 mBottomActionBar.disableActions();
554 mWallpaperSetter.requestDestination(getActivity(), getFragmentManager(), this,
555 mSelectedWallpaperInfo instanceof LiveWallpaperInfo);
556 });
557
558 mWallpaperInfoView = (WallpaperInfoView) LayoutInflater.from(getContext())
559 .inflate(R.layout.wallpaper_info_view, /* root= */ null);
560 mBottomActionBar.attachViewToBottomSheetAndBindAction(mWallpaperInfoView, INFORMATION);
561 mBottomActionBar.setActionClickListener(EDIT, unused -> {
562 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
563 mSelectedWallpaperInfo.showPreview(getActivity(),
564 new PreviewActivity.PreviewActivityIntentFactory(),
565 PREVIEW_WALLPAPER_REQUEST_CODE);
566 });
567 mBottomActionBar.show();
Ching-Sung Li073812b2020-04-07 21:19:21 +0800568 }
569
Jon Miranda16ea1b12017-12-12 14:52:48 -0800570 @Override
571 public void onResume() {
572 super.onResume();
573
Santiago Etchebehere8cad0dd2019-10-17 10:52:39 -0700574 WallpaperPreferences preferences = InjectorProvider.getInjector()
575 .getPreferences(getActivity());
Jon Miranda16ea1b12017-12-12 14:52:48 -0800576 preferences.setLastAppActiveTimestamp(new Date().getTime());
577
578 // Reset Glide memory settings to a "normal" level of usage since it may have been lowered in
579 // PreviewFragment.
580 Glide.get(getActivity()).setMemoryCategory(MemoryCategory.NORMAL);
581
582 // Show the staged 'start rotation' error dialog fragment if there is one that was unable to be
583 // shown earlier when this fragment's hosting activity didn't allow committing fragment
584 // transactions.
585 if (mStagedStartRotationErrorDialogFragment != null) {
586 mStagedStartRotationErrorDialogFragment.show(
587 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
588 mStagedStartRotationErrorDialogFragment = null;
589 }
590
591 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
592 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
593 // committing fragment transactions.
594 if (mStagedSetWallpaperErrorDialogFragment != null) {
595 mStagedSetWallpaperErrorDialogFragment.show(
596 getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
597 mStagedSetWallpaperErrorDialogFragment = null;
598 }
599
Chuck Liao5a4243b2020-05-20 23:56:39 +0800600 if (shouldShowRotationTile() && mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
601 // Must be resuming from a previously stopped state, so re-schedule the update of the
602 // daily wallpapers tile thumbnail.
603 mUpdateDailyWallpaperThumbRunnable.run();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800604 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800605 }
606
607 @Override
608 public void onStop() {
609 super.onStop();
610 mHandler.removeCallbacks(mUpdateDailyWallpaperThumbRunnable);
611 }
612
613 @Override
614 public void onDestroy() {
615 super.onDestroy();
616 if (mProgressDialog != null) {
617 mProgressDialog.dismiss();
618 }
619 mWallpaperChangedNotifier.unregisterListener(mWallpaperChangedListener);
Santiago Etchebehere1ee76a22018-05-15 15:02:24 -0700620 if (mAppStatusListener != null) {
621 mPackageStatusNotifier.removeListener(mAppStatusListener);
622 }
Chuck Liao69630f12020-03-05 19:01:25 +0800623 mWallpaperSetter.cleanUp();
Jon Miranda16ea1b12017-12-12 14:52:48 -0800624 }
625
626 @Override
627 public void retryStartRotation(@NetworkPreference int networkPreference) {
628 startRotation(networkPreference);
629 }
630
chihhangchuang7feb3752020-04-24 02:48:56 +0800631 @Override
632 public boolean onBackPressed() {
633 if (mSelectedWallpaperInfo != null) {
634 onWallpaperSelected(null, 0);
635 return true;
636 }
637 return false;
638 }
639
Jon Miranda16ea1b12017-12-12 14:52:48 -0800640 public void setCurrentWallpaperBottomSheetPresenter(
641 CurrentWallpaperBottomSheetPresenter presenter) {
642 mCurrentWallpaperBottomSheetPresenter = presenter;
643 }
644
645 public void setWallpapersUiContainer(WallpapersUiContainer uiContainer) {
646 mWallpapersUiContainer = uiContainer;
647 }
648
Chuck Liaob3829fb2020-04-01 00:47:50 +0800649 public void setOnWallpaperSelectedListener(
650 WallpaperSelectedListener wallpaperSelectedListener) {
651 mWallpaperSelectedListener = wallpaperSelectedListener;
652 }
653
654 /**
655 * Resizes the layout's height.
656 */
657 public void resizeLayout(int height) {
658 mImageGrid.getLayoutParams().height = height;
659 mImageGrid.requestLayout();
660 }
661
662 /**
663 * Scrolls to the specific item.
664 *
665 * @param position the position of the item
666 */
667 public void scrollToPosition(int position) {
668 ((GridLayoutManager) mImageGrid.getLayoutManager())
669 .scrollToPositionWithOffset(position, /* offset= */ 0);
670 }
671
Jon Miranda16ea1b12017-12-12 14:52:48 -0800672 /**
673 * Enable a test mode of operation -- in which certain UI features are disabled to allow for
674 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
675 * constantly keeps the UI thread alive and blocks a test forever.
676 *
677 * @param testingMode
678 */
679 void setTestingMode(boolean testingMode) {
680 mTestingMode = testingMode;
681 }
682
Jon Miranda16ea1b12017-12-12 14:52:48 -0800683 @Override
684 public void startRotation(@NetworkPreference final int networkPreference) {
685 if (!isRotationEnabled()) {
686 Log.e(TAG, "Rotation is not enabled for this category " + mCategory.getTitle());
687 return;
688 }
689
690 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
691 // causes Espresso to hang once the dialog is shown.
692 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE && !mTestingMode) {
693 int themeResId;
694 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
695 themeResId = R.style.ProgressDialogThemePreL;
696 } else {
697 themeResId = R.style.LightDialogTheme;
698 }
699 mProgressDialog = new ProgressDialog(getActivity(), themeResId);
700
701 mProgressDialog.setTitle(PROGRESS_DIALOG_NO_TITLE);
702 mProgressDialog.setMessage(
703 getResources().getString(R.string.start_rotation_progress_message));
704 mProgressDialog.setIndeterminate(PROGRESS_DIALOG_INDETERMINATE);
705 mProgressDialog.show();
706 }
707
708 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
709 mAdapter.mPendingSelectedAdapterPosition = SPECIAL_FIXED_TILE_ADAPTER_POSITION;
710 }
711
712 final Context appContext = getActivity().getApplicationContext();
713
714 mWallpaperRotationInitializer.setFirstWallpaperInRotation(
715 appContext,
716 networkPreference,
717 new Listener() {
718 @Override
719 public void onFirstWallpaperInRotationSet() {
720 if (mProgressDialog != null) {
721 mProgressDialog.dismiss();
722 }
723
724 // The fragment may be detached from its containing activity if the user exits the
725 // app before the first wallpaper image in rotation finishes downloading.
726 Activity activity = getActivity();
727
Jon Miranda16ea1b12017-12-12 14:52:48 -0800728
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700729 if (mWallpaperRotationInitializer.startRotation(appContext)) {
730 if (activity != null
731 && mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
732 try {
733 Toast.makeText(getActivity(),
734 R.string.wallpaper_set_successfully_message,
735 Toast.LENGTH_SHORT).show();
736 } catch (NotFoundException e) {
737 Log.e(TAG, "Could not show toast " + e);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800738 }
Jon Miranda16ea1b12017-12-12 14:52:48 -0800739
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700740 activity.setResult(Activity.RESULT_OK);
741 activity.finish();
742 } else if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
743 mAdapter.updateSelectedTile(SPECIAL_FIXED_TILE_ADAPTER_POSITION);
744 }
745 } else { // Failed to start rotation.
746 showStartRotationErrorDialog(networkPreference);
747
748 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
749 DesktopRotationHolder rotationViewHolder =
750 (DesktopRotationHolder)
751 mImageGrid.findViewHolderForAdapterPosition(
752 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
753 rotationViewHolder.setSelectionState(
754 SelectableHolder.SELECTION_STATE_DESELECTED);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800755 }
756 }
757 }
758
759 @Override
760 public void onError() {
761 if (mProgressDialog != null) {
762 mProgressDialog.dismiss();
763 }
764
765 showStartRotationErrorDialog(networkPreference);
766
767 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
768 DesktopRotationHolder rotationViewHolder =
769 (DesktopRotationHolder) mImageGrid.findViewHolderForAdapterPosition(
770 SPECIAL_FIXED_TILE_ADAPTER_POSITION);
771 rotationViewHolder.setSelectionState(SelectableHolder.SELECTION_STATE_DESELECTED);
772 }
773 }
774 });
775 }
776
777 private void showStartRotationErrorDialog(@NetworkPreference int networkPreference) {
778 BaseActivity activity = (BaseActivity) getActivity();
779 if (activity != null) {
780 StartRotationErrorDialogFragment startRotationErrorDialogFragment =
781 StartRotationErrorDialogFragment.newInstance(networkPreference);
782 startRotationErrorDialogFragment.setTargetFragment(
783 IndividualPickerFragment.this, UNUSED_REQUEST_CODE);
784
785 if (activity.isSafeToCommitFragmentTransaction()) {
786 startRotationErrorDialogFragment.show(
787 getFragmentManager(), TAG_START_ROTATION_ERROR_DIALOG);
788 } else {
789 mStagedStartRotationErrorDialogFragment = startRotationErrorDialogFragment;
790 }
791 }
792 }
793
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800794 int getNumColumns() {
Ching-Sung Li9df77a32019-07-10 11:45:30 +0800795 Activity activity = getActivity();
Santiago Etchebehere53c63432020-05-07 18:55:35 -0700796 return activity == null ? 0 : SizeCalculator.getNumIndividualColumns(activity);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800797 }
798
799 /**
800 * Returns whether rotation is enabled for this category.
801 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +0800802 boolean isRotationEnabled() {
Samuel Fufa03bc6ac2019-09-19 12:01:50 -0700803 return mWallpaperRotationInitializer != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -0800804 }
805
806 @Override
807 public void onCurrentWallpaperRefreshed() {
808 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
809 }
810
Chuck Liao69630f12020-03-05 19:01:25 +0800811 @Override
812 public void onSet(int destination) {
813 if (mSelectedWallpaperInfo == null) {
814 Log.e(TAG, "Unable to set wallpaper since the selected wallpaper info is null");
815 return;
816 }
817
“Chuckffd832c2020-03-22 02:15:58 +0800818 mWallpaperPersister.setWallpaperInfoInPreview(mSelectedWallpaperInfo);
Chuck Liao69630f12020-03-05 19:01:25 +0800819 if (mSelectedWallpaperInfo instanceof LiveWallpaperInfo) {
820 mWallpaperSetter.setCurrentWallpaper(getActivity(), mSelectedWallpaperInfo, null,
821 destination, 0, null, mSetWallpaperCallback);
822 } else {
823 mWallpaperSetter.setCurrentWallpaper(
824 getActivity(), mSelectedWallpaperInfo, destination, mSetWallpaperCallback);
825 }
“Chuck7ef99722020-03-22 04:34:03 +0800826 onWallpaperDestinationSet(destination);
Chuck Liao69630f12020-03-05 19:01:25 +0800827 }
828
829 private WallpaperPersister.SetWallpaperCallback mSetWallpaperCallback =
830 new WallpaperPersister.SetWallpaperCallback() {
831 @Override
“Chuckffd832c2020-03-22 02:15:58 +0800832 public void onSuccess(WallpaperInfo wallpaperInfo) {
chihhangchuang210c9602020-06-02 12:38:14 +0800833 mWallpaperPersister.onLiveWallpaperSet();
Chuck Liao6e52fff2020-04-23 16:11:23 +0800834 Toast.makeText(getActivity(), R.string.wallpaper_set_successfully_message,
835 Toast.LENGTH_SHORT).show();
chihhangchuang210c9602020-06-02 12:38:14 +0800836 getActivity().overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
837 getActivity().finish();
Chuck Liao69630f12020-03-05 19:01:25 +0800838 }
839
840 @Override
841 public void onError(@Nullable Throwable throwable) {
842 Log.e(TAG, "Can't apply the wallpaper.");
843 mBottomActionBar.enableActions();
844 }
845 };
846
847 @Override
848 public void onDialogDismissed(boolean withItemSelected) {
849 if (!withItemSelected) {
850 mBottomActionBar.enableActions();
851 }
852 }
853
Jon Miranda16ea1b12017-12-12 14:52:48 -0800854 /**
855 * Shows a "set wallpaper" error dialog with a failure message and button to try again.
856 */
857 private void showSetWallpaperErrorDialog() {
858 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
859 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
860 dialogFragment.setTargetFragment(this, UNUSED_REQUEST_CODE);
861
862 if (((BaseActivity) getActivity()).isSafeToCommitFragmentTransaction()) {
863 dialogFragment.show(getFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
864 } else {
865 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
866 }
867 }
868
Ching-Sung Li073812b2020-04-07 21:19:21 +0800869 void updateBottomActions(boolean hasWallpaperSelected) {
chihhangchuang803ea9a2020-04-21 13:03:10 +0800870 if (hasWallpaperSelected) {
Chuck Liao7dc21572020-05-14 18:37:36 +0800871 mBottomActionBar.showActionsOnly(INFORMATION, EDIT, APPLY);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800872 } else {
chihhangchuang08abb582020-04-27 17:20:31 +0800873 mBottomActionBar.showActionsOnly(ROTATION);
chihhangchuang803ea9a2020-04-21 13:03:10 +0800874 }
Chuck Liao69630f12020-03-05 19:01:25 +0800875 }
876
877 private void updateThumbnail(WallpaperInfo selectedWallpaperInfo) {
Chuck Liaof40063f2020-03-03 18:35:24 +0800878 ThumbnailUpdater thumbnailUpdater = (ThumbnailUpdater) getParentFragment();
Chuck Liao69630f12020-03-05 19:01:25 +0800879 if (thumbnailUpdater == null) {
880 return;
881 }
882
883 if (selectedWallpaperInfo != null) {
884 thumbnailUpdater.updateThumbnail(selectedWallpaperInfo);
885 } else {
886 thumbnailUpdater.restoreThumbnails();
Chuck Liaof40063f2020-03-03 18:35:24 +0800887 }
888 }
889
“Chuck7ef99722020-03-22 04:34:03 +0800890 private void onWallpaperDestinationSet(int destination) {
891 WallpaperDestinationCallback wallpaperDestinationCallback =
892 (WallpaperDestinationCallback) getParentFragment();
893 if (wallpaperDestinationCallback == null) {
894 return;
895 }
896
897 wallpaperDestinationCallback.onDestinationSet(destination);
898 }
899
Ching-Sung Li073812b2020-04-07 21:19:21 +0800900 void onWallpaperSelected(@Nullable WallpaperInfo newSelectedWallpaperInfo,
Chuck Liaob3829fb2020-04-01 00:47:50 +0800901 int position) {
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800902 if (mSelectedWallpaperInfo == newSelectedWallpaperInfo) {
“Chuckffd832c2020-03-22 02:15:58 +0800903 return;
904 }
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800905 // Update current wallpaper.
“Chuckffd832c2020-03-22 02:15:58 +0800906 updateActivatedStatus(mSelectedWallpaperInfo == null
907 ? mAppliedWallpaperInfo : mSelectedWallpaperInfo, false);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800908 // Update new selected wallpaper.
909 updateActivatedStatus(newSelectedWallpaperInfo == null
910 ? mAppliedWallpaperInfo : newSelectedWallpaperInfo, true);
911
912 mSelectedWallpaperInfo = newSelectedWallpaperInfo;
913 updateBottomActions(mSelectedWallpaperInfo != null);
914 updateThumbnail(mSelectedWallpaperInfo);
chihhangchuangde9e8722020-05-02 17:22:13 +0800915 // Populate wallpaper info into view.
chihhangchuang1a29e752020-04-28 18:22:53 +0800916 if (mSelectedWallpaperInfo != null && mWallpaperInfoView != null) {
chihhangchuangc687d912020-05-04 14:33:05 +0800917 WallpaperInfoHelper.loadExploreIntent(
918 getContext(),
919 mSelectedWallpaperInfo,
920 (actionLabel, exploreIntent) ->
921 mWallpaperInfoView.populateWallpaperInfo(
922 mSelectedWallpaperInfo, actionLabel, exploreIntent,
923 v -> onExploreClicked(exploreIntent)));
Chuck Liaob3829fb2020-04-01 00:47:50 +0800924 }
925
926 if (mWallpaperSelectedListener != null) {
927 mWallpaperSelectedListener.onWallpaperSelected(position);
chihhangchuang22aa0cc2020-03-25 19:12:42 +0800928 }
Chuck Liaof40063f2020-03-03 18:35:24 +0800929 }
930
chihhangchuangc687d912020-05-04 14:33:05 +0800931 private void onExploreClicked(Intent exploreIntent) {
932 if (getContext() == null) {
933 return;
934 }
935 Context context = getContext();
936 mUserEventLogger.logActionClicked(mSelectedWallpaperInfo.getCollectionId(context),
937 mSelectedWallpaperInfo.getActionLabelRes(context));
938
939 startActivity(exploreIntent);
940 }
941
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800942 private void updateActivatedStatus(WallpaperInfo wallpaperInfo, boolean isActivated) {
“Chuckffd832c2020-03-22 02:15:58 +0800943 if (wallpaperInfo == null) {
944 return;
945 }
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800946 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800947 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Chuck Liao6a37a1c2020-03-07 03:39:43 +0800948 ? index + 1 : index;
949 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
950 if (holder != null) {
951 holder.itemView.setActivated(isActivated);
952 } else {
953 // Item is not visible, make sure the item is re-bound when it becomes visible.
954 mAdapter.notifyItemChanged(index);
955 }
956 }
957
“Chuckffd832c2020-03-22 02:15:58 +0800958 private void updateAppliedStatus(WallpaperInfo wallpaperInfo, boolean isApplied) {
959 if (wallpaperInfo == null) {
960 return;
961 }
962 int index = mWallpapers.indexOf(wallpaperInfo);
Chuck Liao5a4243b2020-05-20 23:56:39 +0800963 index = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
“Chuckffd832c2020-03-22 02:15:58 +0800964 ? index + 1 : index;
965 ViewHolder holder = mImageGrid.findViewHolderForAdapterPosition(index);
966 if (holder != null) {
967 holder.itemView.findViewById(R.id.check_circle)
968 .setVisibility(isApplied ? View.VISIBLE : View.GONE);
969 } else {
970 // Item is not visible, make sure the item is re-bound when it becomes visible.
971 mAdapter.notifyItemChanged(index);
972 }
973 }
974
Chuck Liao11f4a762020-04-08 13:24:43 +0800975 private void refreshAppliedWallpaper() {
976 // Clear the check mark and blue border(if it shows) of the old applied wallpaper.
977 showCheckMarkAndBorderForAppliedWallpaper(false);
978
979 // Update to the new applied wallpaper.
980 String appliedWallpaperId = getAppliedWallpaperId();
981 Optional<WallpaperInfo> wallpaperInfoOptional = mWallpapers
982 .stream()
983 .filter(wallpaper -> wallpaper.getWallpaperId() != null)
984 .filter(wallpaper -> wallpaper.getWallpaperId().equals(appliedWallpaperId))
985 .findFirst();
986 mAppliedWallpaperInfo = wallpaperInfoOptional.orElse(null);
987
988 // Set the check mark and blue border(if user doesn't select) of the new applied wallpaper.
989 showCheckMarkAndBorderForAppliedWallpaper(true);
990 }
991
992 private String getAppliedWallpaperId() {
993 WallpaperPreferences prefs =
994 InjectorProvider.getInjector().getPreferences(getContext());
995 android.app.WallpaperInfo wallpaperInfo = mWallpaperManager.getWallpaperInfo();
996 boolean isDestinationBoth =
997 mWallpaperManager.getWallpaperId(WallpaperManager.FLAG_LOCK) < 0;
998
999 if (isDestinationBoth || mWallpaperDestination == WallpaperPersister.DEST_HOME_SCREEN) {
1000 return wallpaperInfo != null
1001 ? wallpaperInfo.getServiceName() : prefs.getHomeWallpaperRemoteId();
1002 } else {
1003 return prefs.getLockWallpaperRemoteId();
1004 }
1005 }
1006
1007 private void showCheckMarkAndBorderForAppliedWallpaper(boolean show) {
1008 updateAppliedStatus(mAppliedWallpaperInfo, show);
1009 if (mSelectedWallpaperInfo == null) {
1010 updateActivatedStatus(mAppliedWallpaperInfo, show);
1011 }
1012 }
1013
Chuck Liao5a4243b2020-05-20 23:56:39 +08001014 private boolean shouldShowRotationTile() {
1015 return mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && isRotationEnabled();
Jon Miranda16ea1b12017-12-12 14:52:48 -08001016 }
1017
1018 /**
1019 * RecyclerView Adapter subclass for the wallpaper tiles in the RecyclerView.
1020 */
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001021 class IndividualAdapter extends RecyclerView.Adapter<ViewHolder> {
1022 static final int ITEM_VIEW_TYPE_ROTATION = 1;
1023 static final int ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER = 2;
1024 static final int ITEM_VIEW_TYPE_MY_PHOTOS = 3;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001025
1026 private final List<WallpaperInfo> mWallpapers;
1027
1028 private int mPendingSelectedAdapterPosition;
1029 private int mSelectedAdapterPosition;
1030
1031 IndividualAdapter(List<WallpaperInfo> wallpapers) {
1032 mWallpapers = wallpapers;
1033 mPendingSelectedAdapterPosition = -1;
1034 mSelectedAdapterPosition = -1;
1035 }
1036
1037 @Override
1038 public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
1039 switch (viewType) {
1040 case ITEM_VIEW_TYPE_ROTATION:
1041 return createRotationHolder(parent);
1042 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1043 return createIndividualHolder(parent);
1044 case ITEM_VIEW_TYPE_MY_PHOTOS:
1045 return createMyPhotosHolder(parent);
1046 default:
1047 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1048 return null;
1049 }
1050 }
1051
1052 @Override
1053 public int getItemViewType(int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001054 if (shouldShowRotationTile() && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001055 return ITEM_VIEW_TYPE_ROTATION;
1056 }
1057
1058 // A category cannot have both a "start rotation" tile and a "my photos" tile.
1059 if (mCategory.supportsCustomPhotos()
1060 && !isRotationEnabled()
1061 && position == SPECIAL_FIXED_TILE_ADAPTER_POSITION) {
1062 return ITEM_VIEW_TYPE_MY_PHOTOS;
1063 }
1064
1065 return ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER;
1066 }
1067
1068 @Override
1069 public void onBindViewHolder(ViewHolder holder, int position) {
1070 int viewType = getItemViewType(position);
1071
1072 switch (viewType) {
1073 case ITEM_VIEW_TYPE_ROTATION:
1074 onBindRotationHolder(holder, position);
1075 break;
1076 case ITEM_VIEW_TYPE_INDIVIDUAL_WALLPAPER:
1077 onBindIndividualHolder(holder, position);
1078 break;
1079 case ITEM_VIEW_TYPE_MY_PHOTOS:
1080 ((MyPhotosViewHolder) holder).bind();
1081 break;
1082 default:
1083 Log.e(TAG, "Unsupported viewType " + viewType + " in IndividualAdapter");
1084 }
1085 }
1086
1087 @Override
1088 public int getItemCount() {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001089 return (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001090 ? mWallpapers.size() + 1
1091 : mWallpapers.size();
1092 }
1093
1094 private ViewHolder createRotationHolder(ViewGroup parent) {
1095 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
Chuck Liao5a4243b2020-05-20 23:56:39 +08001096 View view = layoutInflater.inflate(R.layout.grid_item_rotation_desktop, parent, false);
1097 SelectionAnimator selectionAnimator =
1098 new CheckmarkSelectionAnimator(getActivity(), view);
1099 return new DesktopRotationHolder(getActivity(), mTileSizePx.y, view, selectionAnimator,
1100 IndividualPickerFragment.this);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001101 }
1102
1103 private ViewHolder createIndividualHolder(ViewGroup parent) {
1104 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1105 View view = layoutInflater.inflate(R.layout.grid_item_image, parent, false);
1106
1107 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1108 SelectionAnimator selectionAnimator =
1109 new CheckmarkSelectionAnimator(getActivity(), view);
1110 return new SetIndividualHolder(
1111 getActivity(), mTileSizePx.y, view,
1112 selectionAnimator,
1113 new OnSetListener() {
1114 @Override
1115 public void onPendingWallpaperSet(int adapterPosition) {
1116 // Deselect and hide loading indicator for any previously pending tile.
1117 if (mPendingSelectedAdapterPosition != -1) {
1118 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1119 mPendingSelectedAdapterPosition);
1120 if (oldViewHolder instanceof SelectableHolder) {
1121 ((SelectableHolder) oldViewHolder).setSelectionState(
1122 SelectableHolder.SELECTION_STATE_DESELECTED);
1123 }
1124 }
1125
1126 if (mSelectedAdapterPosition != -1) {
1127 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1128 mSelectedAdapterPosition);
1129 if (oldViewHolder instanceof SelectableHolder) {
1130 ((SelectableHolder) oldViewHolder).setSelectionState(
1131 SelectableHolder.SELECTION_STATE_DESELECTED);
1132 }
1133 }
1134
1135 mPendingSelectedAdapterPosition = adapterPosition;
1136 }
1137
1138 @Override
1139 public void onWallpaperSet(int adapterPosition) {
1140 // No-op -- UI handles a new wallpaper being set by reacting to the
1141 // WallpaperChangedNotifier.
1142 }
1143
1144 @Override
1145 public void onWallpaperSetFailed(SetIndividualHolder holder) {
1146 showSetWallpaperErrorDialog();
1147 mPendingSetIndividualHolder = holder;
1148 }
1149 });
1150 } else { // MOBILE
Chuck Liao3d1a51c2020-02-17 18:29:34 +08001151 return new PreviewIndividualHolder(getActivity(), mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001152 }
1153 }
1154
1155 private ViewHolder createMyPhotosHolder(ViewGroup parent) {
1156 LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
1157 View view = layoutInflater.inflate(R.layout.grid_item_my_photos, parent, false);
1158
Santiago Etchebeherefab49612019-01-15 12:22:42 -08001159 return new MyPhotosViewHolder(getActivity(),
1160 ((MyPhotosStarterProvider) getActivity()).getMyPhotosStarter(),
1161 mTileSizePx.y, view);
Jon Miranda16ea1b12017-12-12 14:52:48 -08001162 }
1163
1164 /**
1165 * Marks the tile at the given position as selected with a visual indication. Also updates the
1166 * "currently selected" BottomSheet to reflect the newly selected tile.
1167 */
1168 private void updateSelectedTile(int newlySelectedPosition) {
1169 // Prevent multiple spinners from appearing with a user tapping several tiles in rapid
1170 // succession.
1171 if (mPendingSelectedAdapterPosition == mSelectedAdapterPosition) {
1172 return;
1173 }
1174
1175 if (mCurrentWallpaperBottomSheetPresenter != null) {
1176 mCurrentWallpaperBottomSheetPresenter.refreshCurrentWallpapers(
1177 IndividualPickerFragment.this);
1178
1179 if (mCurrentWallpaperBottomSheetExpandedRunnable != null) {
1180 mHandler.removeCallbacks(mCurrentWallpaperBottomSheetExpandedRunnable);
1181 }
1182 mCurrentWallpaperBottomSheetExpandedRunnable = new Runnable() {
1183 @Override
1184 public void run() {
1185 mCurrentWallpaperBottomSheetPresenter.setCurrentWallpapersExpanded(true);
1186 }
1187 };
1188 mHandler.postDelayed(mCurrentWallpaperBottomSheetExpandedRunnable, 100);
1189 }
1190
1191 // User may have switched to another category, thus detaching this fragment, so check here.
1192 // NOTE: We do this check after updating the current wallpaper BottomSheet so that the update
1193 // still occurs in the UI after the user selects that other category.
1194 if (getActivity() == null) {
1195 return;
1196 }
1197
1198 // Update the newly selected wallpaper ViewHolder and the old one so that if
1199 // selection UI state applies (desktop UI), it is updated.
1200 if (mSelectedAdapterPosition >= 0) {
1201 ViewHolder oldViewHolder = mImageGrid.findViewHolderForAdapterPosition(
1202 mSelectedAdapterPosition);
1203 if (oldViewHolder instanceof SelectableHolder) {
1204 ((SelectableHolder) oldViewHolder).setSelectionState(
1205 SelectableHolder.SELECTION_STATE_DESELECTED);
1206 }
1207 }
1208
1209 // Animate selection of newly selected tile.
1210 ViewHolder newViewHolder = mImageGrid
1211 .findViewHolderForAdapterPosition(newlySelectedPosition);
1212 if (newViewHolder instanceof SelectableHolder) {
1213 ((SelectableHolder) newViewHolder).setSelectionState(
1214 SelectableHolder.SELECTION_STATE_SELECTED);
1215 }
1216
1217 mSelectedAdapterPosition = newlySelectedPosition;
1218
1219 // If the tile was in the last row of the grid, add space below it so the user can scroll down
1220 // and up to see the BottomSheet without it fully overlapping the newly selected tile.
1221 int spanCount = ((GridLayoutManager) mImageGrid.getLayoutManager()).getSpanCount();
1222 int numRows = (int) Math.ceil((float) getItemCount() / spanCount);
1223 int rowOfNewlySelectedTile = newlySelectedPosition / spanCount;
1224 boolean isInLastRow = rowOfNewlySelectedTile == numRows - 1;
1225
1226 updateImageGridPadding(isInLastRow /* addExtraBottomSpace */);
1227 }
1228
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001229 void onBindRotationHolder(ViewHolder holder, int position) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001230 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
1231 String collectionId = mCategory.getCollectionId();
1232 ((DesktopRotationHolder) holder).bind(collectionId);
1233
1234 if (mWallpaperPreferences.getWallpaperPresentationMode()
1235 == WallpaperPreferences.PRESENTATION_MODE_ROTATING
1236 && collectionId.equals(mWallpaperPreferences.getHomeWallpaperCollectionId())) {
1237 mSelectedAdapterPosition = position;
1238 }
1239
1240 if (!mWasUpdateRunnableRun && !mWallpapers.isEmpty()) {
1241 updateDesktopDailyRotationThumbnail((DesktopRotationHolder) holder);
1242 mWasUpdateRunnableRun = true;
1243 }
1244 }
1245 }
1246
Ching-Sung Li31fbe5e2019-01-23 19:36:24 +08001247 void onBindIndividualHolder(ViewHolder holder, int position) {
Chuck Liao5a4243b2020-05-20 23:56:39 +08001248 int wallpaperIndex = (shouldShowRotationTile() || mCategory.supportsCustomPhotos())
Jon Miranda16ea1b12017-12-12 14:52:48 -08001249 ? position - 1 : position;
1250 WallpaperInfo wallpaper = mWallpapers.get(wallpaperIndex);
1251 ((IndividualHolder) holder).bindWallpaper(wallpaper);
“Chuck7ef99722020-03-22 04:34:03 +08001252 String appliedWallpaperId = getAppliedWallpaperId();
“Chuckffd832c2020-03-22 02:15:58 +08001253 boolean isWallpaperApplied = wallpaper.getWallpaperId().equals(appliedWallpaperId);
1254 boolean isWallpaperSelected = wallpaper.equals(mSelectedWallpaperInfo);
1255 boolean hasUserSelectedWallpaper = mSelectedWallpaperInfo != null;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001256
“Chuckffd832c2020-03-22 02:15:58 +08001257 if (isWallpaperApplied) {
Jon Miranda16ea1b12017-12-12 14:52:48 -08001258 mSelectedAdapterPosition = position;
“Chuckffd832c2020-03-22 02:15:58 +08001259 mAppliedWallpaperInfo = wallpaper;
Jon Miranda16ea1b12017-12-12 14:52:48 -08001260 }
Chuck Liaof40063f2020-03-03 18:35:24 +08001261
Chuck Liao342f4ab2020-05-26 18:28:09 +08001262 holder.itemView.setActivated(
1263 (isWallpaperApplied && !hasUserSelectedWallpaper) || isWallpaperSelected);
1264 holder.itemView.findViewById(R.id.check_circle).setVisibility(
1265 isWallpaperApplied ? View.VISIBLE : View.GONE);
Chuck Liao46644b92020-06-08 14:20:50 +08001266 if (!NEW_SCROLL_INTERACTION) {
1267 holder.itemView.findViewById(R.id.tile).setOnClickListener(
1268 view -> onWallpaperSelected(wallpaper, position));
1269 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001270 }
1271 }
Chuck Liaoba401232020-03-13 20:11:04 +08001272
1273 private class GridPaddingDecoration extends RecyclerView.ItemDecoration {
1274
1275 private int mPadding;
1276
1277 GridPaddingDecoration(int padding) {
1278 mPadding = padding;
1279 }
1280
1281 @Override
1282 public void getItemOffsets(Rect outRect, View view, RecyclerView parent,
1283 RecyclerView.State state) {
1284 int position = parent.getChildAdapterPosition(view);
1285 if (position >= 0) {
1286 outRect.left = mPadding;
1287 outRect.right = mPadding;
1288 }
1289 }
1290 }
Jon Miranda16ea1b12017-12-12 14:52:48 -08001291}