blob: 9f7459687396323816956360d6d43f11b98fdae6 [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;
17
18import android.Manifest.permission;
19import android.app.Activity;
20import android.app.ProgressDialog;
21import android.app.WallpaperManager;
22import android.content.Context;
23import android.content.Intent;
24import android.content.pm.PackageManager;
25import android.graphics.Color;
26import android.graphics.Point;
27import android.graphics.PorterDuff.Mode;
28import android.graphics.drawable.Drawable;
29import android.net.Uri;
30import android.os.AsyncTask;
31import android.os.Build.VERSION;
32import android.os.Build.VERSION_CODES;
33import android.os.Bundle;
34import android.support.annotation.NonNull;
35import android.support.annotation.Nullable;
36import android.support.design.widget.BottomSheetBehavior;
37import android.support.design.widget.BottomSheetBehavior.BottomSheetCallback;
38import android.support.design.widget.TabLayout;
39import android.support.design.widget.TabLayout.OnTabSelectedListener;
40import android.support.design.widget.TabLayout.Tab;
41import android.support.v4.app.Fragment;
42import android.support.v4.app.FragmentManager;
43import android.support.v7.app.AlertDialog;
44import android.support.v7.widget.Toolbar;
45import android.util.Log;
46import android.view.View;
47import android.view.View.OnClickListener;
48import android.widget.Button;
49import android.widget.FrameLayout;
50import android.widget.ImageView;
51import android.widget.LinearLayout;
52import android.widget.TextView;
53
54import com.android.wallpaper.R;
55import com.android.wallpaper.asset.Asset;
56import com.android.wallpaper.compat.ButtonDrawableSetterCompat;
57import com.android.wallpaper.compat.WallpaperManagerCompat;
58import com.android.wallpaper.config.Flags;
59import com.android.wallpaper.model.Category;
60import com.android.wallpaper.model.CategoryProvider;
61import com.android.wallpaper.model.CategoryReceiver;
62import com.android.wallpaper.model.ImageWallpaperInfo;
63import com.android.wallpaper.model.InlinePreviewIntentFactory;
64import com.android.wallpaper.model.WallpaperInfo;
65import com.android.wallpaper.module.CurrentWallpaperInfoFactory;
66import com.android.wallpaper.module.CurrentWallpaperInfoFactory.WallpaperInfoCallback;
67import com.android.wallpaper.module.DailyLoggingAlarmScheduler;
68import com.android.wallpaper.module.ExploreIntentChecker;
69import com.android.wallpaper.module.FormFactorChecker;
70import com.android.wallpaper.module.FormFactorChecker.FormFactor;
71import com.android.wallpaper.module.Injector;
72import com.android.wallpaper.module.InjectorProvider;
73import com.android.wallpaper.module.NetworkStatusNotifier;
74import com.android.wallpaper.module.NetworkStatusNotifier.NetworkStatus;
75import com.android.wallpaper.module.UserEventLogger;
76import com.android.wallpaper.module.UserEventLogger.WallpaperSetFailureReason;
77import com.android.wallpaper.module.WallpaperPersister;
78import com.android.wallpaper.module.WallpaperPersister.Destination;
79import com.android.wallpaper.module.WallpaperPersister.SetWallpaperCallback;
80import com.android.wallpaper.module.WallpaperPersister.WallpaperPosition;
81import com.android.wallpaper.module.WallpaperPreferences;
82import com.android.wallpaper.module.WallpaperPreferences.PresentationMode;
83import com.android.wallpaper.module.WallpaperRotationRefresher;
84import com.android.wallpaper.module.WallpaperRotationRefresher.Listener;
85import com.android.wallpaper.picker.PreviewActivity.PreviewActivityIntentFactory;
86import com.android.wallpaper.picker.ViewOnlyPreviewActivity.ViewOnlyPreviewActivityIntentFactory;
87import com.android.wallpaper.picker.WallpaperDisabledFragment.WallpaperSupportLevel;
88import com.android.wallpaper.picker.individual.IndividualPickerActivity.IndividualPickerActivityIntentFactory;
89import com.android.wallpaper.picker.individual.IndividualPickerFragment;
90import com.android.wallpaper.util.ScreenSizeCalculator;
91import com.android.wallpaper.util.ThrowableAnalyzer;
92
93import java.util.ArrayList;
94import java.util.List;
95
96/**
97 * Activity allowing users to select a category of wallpapers to choose from.
98 */
99public class TopLevelPickerActivity extends BaseActivity implements WallpapersUiContainer,
100 CurrentWallpaperBottomSheetPresenter, SetWallpaperErrorDialogFragment.Listener,
101 MyPhotosLauncher {
102 private static final int SHOW_CATEGORY_REQUEST_CODE = 0;
103 private static final int PREVIEW_WALLPAPER_REQUEST_CODE = 1;
104 private static final int VIEW_ONLY_PREVIEW_WALLPAPER_REQUEST_CODE = 2;
105 private static final int READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE = 3;
106
107 private static final String TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT =
108 "toplevel_set_wallpaper_error_dialog";
109
110 private static final String TAG = "TopLevelPicker";
111 private static final String KEY_SELECTED_CATEGORY_TAB = "selected_category_tab";
112
113 private IndividualPickerActivityIntentFactory mPickerIntentFactory;
114 private InlinePreviewIntentFactory mPreviewIntentFactory;
115 private InlinePreviewIntentFactory mViewOnlyPreviewIntentFactory;
116 private ArrayList<Category> mCategories;
117 private int mLastSelectedCategoryTabIndex;
118 @FormFactor
119 private int mFormFactor;
120 private WallpaperPreferences mPreferences;
121 private UserEventLogger mUserEventLogger;
122 private NetworkStatusNotifier mNetworkStatusNotifier;
123 private NetworkStatusNotifier.Listener mNetworkStatusListener;
124 private WallpaperPersister mWallpaperPersister;
125 private boolean mWasCustomPhotoWallpaperSet;
126 @WallpaperPosition
127 private int mCustomPhotoWallpaperPosition;
128
129 /**
130 * Progress dialogs for "refresh daily wallpaper" and "set wallpaper" operations.
131 */
132 private ProgressDialog mRefreshWallpaperProgressDialog;
133 private ProgressDialog mSetWallpaperProgressDialog;
134
135 /**
136 * Designates a test mode of operation -- in which certain UI features are disabled to allow for
137 * UI tests to run correctly.
138 */
139 private boolean mTestingMode;
140
141 /**
142 * UI for the "currently set wallpaper" BottomSheet.
143 */
144 private LinearLayout mBottomSheet;
145 private ImageView mCurrentWallpaperImage;
146 private TextView mCurrentWallpaperPresentationMode;
147 private TextView mCurrentWallpaperTitle;
148 private TextView mCurrentWallpaperSubtitle;
149 private Button mCurrentWallpaperExploreButton;
150 private Button mCurrentWallpaperSkipWallpaperButton;
151 private FrameLayout mFragmentContainer;
152 private FrameLayout mLoadingIndicatorContainer;
153 private LinearLayout mWallpaperPositionOptions;
154
155 private List<PermissionChangedListener> mPermissionChangedListeners;
156
157 /**
158 * Staged error dialog fragments that were unable to be shown when the activity didn't allow
159 * committing fragment transactions.
160 */
161 private SetWallpaperErrorDialogFragment mStagedSetWallpaperErrorDialogFragment;
162
163 /**
164 * A wallpaper pending set to the device--we retain a reference to this in order to facilitate
165 * retry or re-crop operations.
166 */
167 private WallpaperInfo mPendingSetWallpaperInfo;
168
169 private static int getTextColorIdForWallpaperPositionButton(boolean isSelected) {
170 return isSelected ? R.color.accent_color : R.color.material_grey500;
171 }
172
173 @Override
174 protected void onCreate(Bundle savedInstanceState) {
175 super.onCreate(savedInstanceState);
176
177 mPickerIntentFactory = new IndividualPickerActivityIntentFactory();
178 mPreviewIntentFactory = new PreviewActivityIntentFactory();
179 mViewOnlyPreviewIntentFactory = new ViewOnlyPreviewActivityIntentFactory();
180 mCategories = new ArrayList<>();
181 mLastSelectedCategoryTabIndex = -1;
182
183 Injector injector = InjectorProvider.getInjector();
184 mPreferences = injector.getPreferences(this);
185 mUserEventLogger = injector.getUserEventLogger(this);
186 mNetworkStatusNotifier = injector.getNetworkStatusNotifier(this);
187 final FormFactorChecker formFactorChecker = injector.getFormFactorChecker(this);
188 mFormFactor = formFactorChecker.getFormFactor();
189 mWallpaperPersister = injector.getWallpaperPersister(this);
190 mWasCustomPhotoWallpaperSet = false;
191
192 mPermissionChangedListeners = new ArrayList<>();
193
194 @WallpaperSupportLevel int wallpaperSupportLevel = getWallpaperSupportLevel();
195 if (wallpaperSupportLevel != WallpaperDisabledFragment.SUPPORTED_CAN_SET) {
196 setContentView(R.layout.activity_single_fragment);
197
198 FragmentManager fm = getSupportFragmentManager();
199 WallpaperDisabledFragment wallpaperDisabledFragment =
200 WallpaperDisabledFragment.newInstance(wallpaperSupportLevel);
201 fm.beginTransaction()
202 .add(R.id.fragment_container, wallpaperDisabledFragment)
203 .commit();
204 return;
205 }
206
207 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
208 initializeMobile();
209 } else { // DESKTOP
210 initializeDesktop(savedInstanceState);
211 }
212 }
213
214 @Override
215 protected void onResume() {
216 super.onResume();
217
218 // Show the staged 'load wallpaper' or 'set wallpaper' error dialog fragments if there is one
219 // that was unable to be shown earlier when this fragment's hosting activity didn't allow
220 // committing fragment transactions.
221 if (mStagedSetWallpaperErrorDialogFragment != null) {
222 mStagedSetWallpaperErrorDialogFragment.show(
223 getSupportFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
224 mStagedSetWallpaperErrorDialogFragment = null;
225 }
226 }
227
228 @Override
229 protected void onDestroy() {
230 super.onDestroy();
231
232 if (mNetworkStatusListener != null) {
233 mNetworkStatusNotifier.unregisterListener(mNetworkStatusListener);
234 }
235
236 if (mRefreshWallpaperProgressDialog != null) {
237 mRefreshWallpaperProgressDialog.dismiss();
238 }
239 if (mSetWallpaperProgressDialog != null) {
240 mSetWallpaperProgressDialog.dismiss();
241 }
242
243 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP && mWasCustomPhotoWallpaperSet) {
244 mUserEventLogger.logWallpaperPosition(mCustomPhotoWallpaperPosition);
245 }
246 }
247
248 @Override
249 public void requestCustomPhotoPicker(PermissionChangedListener listener) {
250 if (!isReadExternalStoragePermissionGranted()) {
251 PermissionChangedListener wrappedListener = new PermissionChangedListener() {
252 @Override
253 public void onPermissionsGranted() {
254 listener.onPermissionsGranted();
255 showCustomPhotoPicker();
256 }
257
258 @Override
259 public void onPermissionsDenied(boolean dontAskAgain) {
260 listener.onPermissionsDenied(dontAskAgain);
261 }
262 };
263 requestExternalStoragePermission(wrappedListener);
264
265 return;
266 }
267
268 showCustomPhotoPicker();
269 }
270
271 void requestExternalStoragePermission(PermissionChangedListener listener) {
272 mPermissionChangedListeners.add(listener);
273 requestPermissions(
274 new String[]{permission.READ_EXTERNAL_STORAGE},
275 READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE);
276 }
277
278 /**
279 * Returns whether READ_EXTERNAL_STORAGE has been granted for the application.
280 */
281 boolean isReadExternalStoragePermissionGranted() {
282 return getPackageManager().checkPermission(permission.READ_EXTERNAL_STORAGE,
283 getPackageName()) == PackageManager.PERMISSION_GRANTED;
284 }
285
286 private void showCustomPhotoPicker() {
287 Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
288 intent.setType("image/*");
289 startActivityForResult(intent, SHOW_CATEGORY_REQUEST_CODE);
290 }
291
292 private void initializeMobile() {
293 setContentView(R.layout.activity_single_fragment_with_toolbar);
294
295 // Set toolbar as the action bar.
296 Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
297 setSupportActionBar(toolbar);
298
299 FragmentManager fm = getSupportFragmentManager();
300 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
301
302 boolean forceCategoryRefresh = false;
303 if (fragment == null) {
304 // App launch specific logic: log the "app launched" event and set up daily logging.
305 mUserEventLogger.logAppLaunched();
306 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
307
308 CategoryPickerFragment newFragment = new CategoryPickerFragment();
309 fm.beginTransaction()
310 .add(R.id.fragment_container, newFragment)
311 .commit();
312
313 forceCategoryRefresh = true;
314 }
315
316 populateCategories(-1, forceCategoryRefresh);
317 }
318
319 private void initializeDesktop(Bundle savedInstanceState) {
320 setContentView(R.layout.activity_top_level_desktop);
321
322 mBottomSheet = (LinearLayout) findViewById(R.id.bottom_sheet);
323 mCurrentWallpaperImage = (ImageView) mBottomSheet.findViewById(R.id.current_wallpaper_image);
324 mCurrentWallpaperImage.getLayoutParams().width = getSingleWallpaperImageWidthPx();
325
326 mCurrentWallpaperPresentationMode =
327 (TextView) mBottomSheet.findViewById(R.id.current_wallpaper_presentation_mode);
328 mCurrentWallpaperTitle = (TextView) findViewById(R.id.current_wallpaper_title);
329 mCurrentWallpaperSubtitle = (TextView) findViewById(R.id.current_wallpaper_subtitle);
330 mCurrentWallpaperExploreButton = (Button) findViewById(
331 R.id.current_wallpaper_explore_button);
332 mCurrentWallpaperSkipWallpaperButton = (Button) findViewById(
333 R.id.current_wallpaper_skip_wallpaper_button);
334 mFragmentContainer = (FrameLayout) findViewById(R.id.fragment_container);
335 mLoadingIndicatorContainer = (FrameLayout) findViewById(R.id.loading_indicator_container);
336 mWallpaperPositionOptions = (LinearLayout) findViewById(
337 R.id.desktop_wallpaper_position_options);
338
339 final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
340 tabLayout.addOnTabSelectedListener(new OnTabSelectedListener() {
341 @Override
342 public void onTabSelected(Tab tab) {
343 Category category = (Category) tab.getTag();
344 show(category.getCollectionId());
345 mLastSelectedCategoryTabIndex = tabLayout.getSelectedTabPosition();
346 }
347
348 @Override
349 public void onTabUnselected(Tab tab) {
350 }
351
352 @Override
353 public void onTabReselected(Tab tab) {
354 Category category = (Category) tab.getTag();
355 // If offline, "My photos" may be the only visible category. In this case we want to allow
356 // re-selection so user can still select a photo as wallpaper while offline.
357 if (!category.isEnumerable()) {
358 onTabSelected(tab);
359 }
360 }
361 });
362
363 FragmentManager fm = getSupportFragmentManager();
364 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
365
366 if (fragment == null) {
367 // App launch specific logic: log the "app launched" event and set up daily logging.
368 mUserEventLogger.logAppLaunched();
369 DailyLoggingAlarmScheduler.setAlarm(getApplicationContext());
370 }
371
372 mNetworkStatusListener = new NetworkStatusNotifier.Listener() {
373 @Override
374 public void onNetworkChanged(@NetworkStatus int networkStatus) {
375 initializeDesktopBasedOnNetwork(networkStatus, savedInstanceState);
376 }
377 };
378 // Upon registering a listener, the onNetworkChanged method is immediately called with the
379 // initial network status.
380 mNetworkStatusNotifier.registerListener(mNetworkStatusListener);
381 }
382
383 private void initializeDesktopBasedOnNetwork(@NetworkStatus int networkStatus,
384 Bundle savedInstanceState) {
385 if (networkStatus == NetworkStatusNotifier.NETWORK_CONNECTED) {
386 initializeDesktopOnline(savedInstanceState);
387 } else {
388 initializeDesktopOffline();
389 }
390 }
391
392 private void initializeDesktopOnline(Bundle savedInstanceState) {
393 FragmentManager fm = getSupportFragmentManager();
394 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
395
396 // Require a category refresh if this is the first load of the app or if the app is now
397 // returning online after having been offline.
398 boolean forceCategoryRefresh = fragment == null || fragment instanceof OfflineDesktopFragment;
399
400 if (fragment != null) {
401 fm.beginTransaction()
402 .remove(fragment)
403 .commit();
404 }
405
406 int selectedTabPosition = savedInstanceState != null
407 ? savedInstanceState.getInt(KEY_SELECTED_CATEGORY_TAB) : -1;
408 populateCategories(selectedTabPosition, forceCategoryRefresh);
409
410 setDesktopLoading(true);
411 setUpBottomSheet();
412 refreshCurrentWallpapers(null /* refreshListener */);
413 }
414
415 private void initializeDesktopOffline() {
416 FragmentManager fm = getSupportFragmentManager();
417 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
418
419 if (fragment != null) {
420 fm.beginTransaction()
421 .remove(fragment)
422 .commit();
423 }
424 OfflineDesktopFragment newFragment = new OfflineDesktopFragment();
425 fm.beginTransaction()
426 .add(R.id.fragment_container, newFragment)
427 .commit();
428
429 // Reset the last selected category tab index to ensure the app doesn't try to reselect a tab
430 // for a category not yet repopulated.
431 mLastSelectedCategoryTabIndex = -1;
432
433 populateCategories(-1 /* selectedTabPosition */, true /* forceCategoryRefresh */);
434
435 setDesktopLoading(false);
436 setCurrentWallpapersExpanded(false);
437 }
438
439 /**
440 * Sets the status of the loading indicator overlay in desktop mode.
441 *
442 * @param loading Whether an indeterminate loading indicator is displayed in place of the main
443 * fragment.
444 */
445 private void setDesktopLoading(boolean loading) {
446 if (loading) {
447 mLoadingIndicatorContainer.setVisibility(View.VISIBLE);
448 mFragmentContainer.setVisibility(View.GONE);
449 } else {
450 mLoadingIndicatorContainer.setVisibility(View.GONE);
451 mFragmentContainer.setVisibility(View.VISIBLE);
452 }
453 }
454
455 /**
456 * Returns the width (in physical px) to use for the "currently set wallpaper" thumbnail.
457 */
458 private int getSingleWallpaperImageWidthPx() {
459 Point screenSize = ScreenSizeCalculator.getInstance().getScreenSize(
460 getWindowManager().getDefaultDisplay());
461
462 int height = getResources().getDimensionPixelSize(
463 R.dimen.current_wallpaper_bottom_sheet_thumb_height);
464 return height * screenSize.x / screenSize.y;
465 }
466
467 /**
468 * Enables and populates the "Currently set" wallpaper BottomSheet.
469 */
470 private void setUpBottomSheet() {
471 mBottomSheet.setVisibility(View.VISIBLE);
472
Jon Miranda16ea1b12017-12-12 14:52:48 -0800473 if (Flags.skipDailyWallpaperButtonEnabled) {
474 // Add "next" icon to the Next Wallpaper button
475 Drawable nextWallpaperButtonDrawable = getResources().getDrawable(
Santiago Etchebehered24506c2018-04-05 17:02:42 -0700476 R.drawable.ic_refresh_18px);
Jon Miranda16ea1b12017-12-12 14:52:48 -0800477
478 // This Drawable's state is shared across the app, so make a copy of it before applying a
479 // color tint as not to affect other clients elsewhere in the app.
480 nextWallpaperButtonDrawable =
481 nextWallpaperButtonDrawable.getConstantState().newDrawable().mutate();
482 // Color the "compass" icon with the accent color.
483 nextWallpaperButtonDrawable.setColorFilter(
484 getResources().getColor(R.color.accent_color), Mode.SRC_IN);
485 ButtonDrawableSetterCompat.setDrawableToButtonStart(
486 mCurrentWallpaperSkipWallpaperButton, nextWallpaperButtonDrawable);
487 }
488
489 final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
490 BottomSheetBehavior.from(mBottomSheet);
491 bottomSheetBehavior.setBottomSheetCallback(new BottomSheetCallback() {
492 @Override
493 public void onStateChanged(@NonNull View view, int i) {
494 }
495
496 @Override
497 public void onSlide(@NonNull View view, float slideOffset) {
498 float alpha;
499 if (slideOffset >= 0) {
500 alpha = slideOffset;
501 } else {
502 alpha = 1f - slideOffset;
503 }
504 LinearLayout bottomSheetContents = (LinearLayout) findViewById(R.id.bottom_sheet_contents);
505 bottomSheetContents.setAlpha(alpha);
506 }
507 });
508 }
509
510 /**
511 * Enables a test mode of operation -- in which certain UI features are disabled to allow for
512 * UI tests to run correctly. Works around issue in ProgressDialog currently where the dialog
513 * constantly keeps the UI thread alive and blocks a test forever.
514 */
515 void setTestingMode(boolean testingMode) {
516 mTestingMode = testingMode;
517 }
518
519 /**
520 * Obtains the {@link WallpaperInfo} object(s) representing the wallpaper(s) currently set to the
521 * device from the {@link CurrentWallpaperInfoFactory} and displays them in the BottomSheet.
522 */
523 @Override
524 public void refreshCurrentWallpapers(@Nullable RefreshListener refreshListener) {
525 final Injector injector = InjectorProvider.getInjector();
526 final Context appContext = getApplicationContext();
527
528 CurrentWallpaperInfoFactory factory = injector.getCurrentWallpaperFactory(this);
529 factory.createCurrentWallpaperInfos(new WallpaperInfoCallback() {
530 @Override
531 public void onWallpaperInfoCreated(
532 final WallpaperInfo homeWallpaper,
533 @Nullable final WallpaperInfo lockWallpaper,
534 @PresentationMode final int presentationMode) {
535
536 if (isDestroyed()) {
537 return;
538 }
539
540 // Fetch the home wallpaper's thumbnail asset asynchronously to work around expensive
541 // method call to WallpaperManager#getWallpaperFile made from the CurrentWallpaperInfoVN
542 // getAsset() method.
543 AssetReceiver assetReceiver = (Asset thumbAsset) -> {
544 if (isDestroyed()) {
545 return;
546 }
547
548 homeWallpaper.getThumbAsset(appContext).loadDrawableWithTransition(
549 TopLevelPickerActivity.this,
550 mCurrentWallpaperImage,
551 200 /* transitionDurationMillis */,
552 () -> {
553 if (refreshListener != null) {
554 refreshListener.onCurrentWallpaperRefreshed();
555 }
556 },
557 Color.TRANSPARENT);
558 };
559 new FetchThumbAssetTask(appContext, homeWallpaper, assetReceiver).executeOnExecutor(
560 AsyncTask.THREAD_POOL_EXECUTOR);
561
562 mCurrentWallpaperPresentationMode.setText(
563 AttributionFormatter.getHumanReadableWallpaperPresentationMode(
564 TopLevelPickerActivity.this, presentationMode));
565
566 List<String> attributions = homeWallpaper.getAttributions(appContext);
567 if (attributions.size() > 0 && attributions.get(0) != null) {
568 mCurrentWallpaperTitle.setText(attributions.get(0));
569 }
570
571 mCurrentWallpaperSubtitle.setText(
572 AttributionFormatter.formatWallpaperSubtitle(appContext, homeWallpaper));
573
574 final String actionUrl = homeWallpaper.getActionUrl(appContext);
575 if (actionUrl != null && !actionUrl.isEmpty()) {
576 Uri exploreUri = Uri.parse(actionUrl);
577
578 ExploreIntentChecker intentChecker = injector.getExploreIntentChecker(appContext);
579 intentChecker.fetchValidActionViewIntent(exploreUri, (@Nullable Intent exploreIntent) -> {
580 if (exploreIntent != null && !isDestroyed()) {
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700581 // Set the icon for the button
582 Drawable exploreButtonDrawable = getResources().getDrawable(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700583 homeWallpaper.getActionIconRes(appContext));
Santiago Etchebehered1bd5092018-04-18 16:03:30 -0700584
585 // This Drawable's state is shared across the app, so make a copy of it
586 // before applying a color tint as not to affect other clients elsewhere
587 // in the app.
588 exploreButtonDrawable = exploreButtonDrawable.getConstantState()
589 .newDrawable().mutate();
590 // Color the "compass" icon with the accent color.
591 exploreButtonDrawable.setColorFilter(
592 getResources().getColor(R.color.accent_color), Mode.SRC_IN);
593
594 ButtonDrawableSetterCompat.setDrawableToButtonStart(
595 mCurrentWallpaperExploreButton, exploreButtonDrawable);
596 mCurrentWallpaperExploreButton.setText(getString(
Santiago Etchebeheree0810d02018-05-10 17:39:40 -0700597 homeWallpaper.getActionLabelRes(appContext)));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800598 mCurrentWallpaperExploreButton.setVisibility(View.VISIBLE);
599 mCurrentWallpaperExploreButton.setOnClickListener(new OnClickListener() {
600 @Override
601 public void onClick(View v) {
Santiago Etchebeherece5613f2018-06-01 13:22:47 -0700602 mUserEventLogger.logActionClicked(
603 homeWallpaper.getCollectionId(appContext),
604 homeWallpaper.getActionLabelRes(appContext));
Jon Miranda16ea1b12017-12-12 14:52:48 -0800605 startActivity(exploreIntent);
606 }
607 });
608 }
609 });
610 } else {
611 mCurrentWallpaperExploreButton.setVisibility(View.GONE);
612 }
613
614 // Hide the wallpaper position options UI if the current home wallpaper is not from
615 // "my photos".
616 String homeCollectionId = homeWallpaper.getCollectionId(TopLevelPickerActivity.this);
617 if (mWallpaperPositionOptions != null
618 && homeCollectionId != null // May be null if app is being used for the first time.
619 && !homeCollectionId.equals(getString(R.string.image_wallpaper_collection_id))) {
620 mWallpaperPositionOptions.setVisibility(View.GONE);
621 }
622
623 boolean showSkipWallpaperButton = Flags.skipDailyWallpaperButtonEnabled
624 && presentationMode == WallpaperPreferences.PRESENTATION_MODE_ROTATING;
625 if (showSkipWallpaperButton) {
626 mCurrentWallpaperSkipWallpaperButton.setVisibility(View.VISIBLE);
627 mCurrentWallpaperSkipWallpaperButton.setOnClickListener(new OnClickListener() {
628 @Override
629 public void onClick(View v) {
630 refreshDailyWallpaper();
631 }
632 });
633 } else {
634 mCurrentWallpaperSkipWallpaperButton.setVisibility(View.GONE);
635 }
636
637 if (refreshListener != null) {
638 refreshListener.onCurrentWallpaperRefreshed();
639 }
640 }
641 }, true /* forceRefresh */);
642 }
643
644 @Override
645 public void onSaveInstanceState(Bundle savedInstanceState) {
646 FormFactorChecker formFactorChecker = InjectorProvider.getInjector().getFormFactorChecker(this);
647 if (formFactorChecker.getFormFactor() == FormFactorChecker.FORM_FACTOR_DESKTOP) {
648 TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
649
650 // tabLayout is only present when the main IndividualPickerFragment is present (as opposed to
651 // the WallpaperDisabledFragment), so need this null check.
652 if (tabLayout != null) {
653 savedInstanceState.putInt(KEY_SELECTED_CATEGORY_TAB, tabLayout.getSelectedTabPosition());
654 }
655 }
656
657 super.onSaveInstanceState(savedInstanceState);
658 }
659
660 /**
661 * Populates the categories appropriately depending on the device form factor.
662 *
663 * @param selectedTabPosition The position of the tab to show as selected, or -1 if no tab
664 * should be selected (i.e. because there is no tab layout present, as on MOBILE form factor).
665 * @param forceRefresh Whether to force a refresh of categories from the CategoryProvider. True if
666 * on first launch.
667 */
668 private void populateCategories(final int selectedTabPosition, boolean forceRefresh) {
669 mCategories.clear();
670
671 Injector injector = InjectorProvider.getInjector();
672 CategoryProvider categoryProvider = injector.getCategoryProvider(this);
673 categoryProvider.fetchCategories(new CategoryReceiver() {
674 @Override
675 public void onCategoryReceived(Category category) {
676 int priority = category.getPriority();
677
678 int index = 0;
679 while (index < mCategories.size() && priority >= mCategories.get(index).getPriority()) {
680 index++;
681 }
682 mCategories.add(index, category);
683
684 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
685 final FragmentManager fm = getSupportFragmentManager();
686 CategoryPickerFragment categoryPickerFragment =
687 (CategoryPickerFragment) fm.findFragmentById(R.id.fragment_container);
688 if (categoryPickerFragment != null) {
689 categoryPickerFragment.addCategory(category);
690 }
691 }
692 }
693
694 @Override
695 public void doneFetchingCategories() {
696 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
697 final FragmentManager fm = getSupportFragmentManager();
698 CategoryPickerFragment categoryPickerFragment =
699 (CategoryPickerFragment) fm.findFragmentById(R.id.fragment_container);
700 if (categoryPickerFragment != null) {
701 categoryPickerFragment.doneFetchingCategories();
702 }
703 } else { // DESKTOP
704 populateCategoryTabs(selectedTabPosition);
705 }
706 }
707 }, forceRefresh);
708 }
709
710 /**
711 * Populates the category tabs on DESKTOP form factor.
712 *
713 * @param selectedTabPosition The position of the tab to show as selected, or -1 if no particular
714 * tab should be selected (in which case: the tab of the category for the currently set
715 * wallpaper will be selected if enumerable; if not, the first enumerable category's tab will
716 * be selected).
717 */
718 private void populateCategoryTabs(int selectedTabPosition) {
719 final TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
720 tabLayout.removeAllTabs();
721
722 String currentlySetCollectionId = mPreferences.getHomeWallpaperCollectionId();
723
724 Tab tabToSelect = null;
725 Tab firstEnumerableCategoryTab = null;
726 for (int i = 0; i < mCategories.size(); i++) {
727 Category category = mCategories.get(i);
728
729 Tab tab = tabLayout.newTab();
730 tab.setText(category.getTitle());
731 tab.setTag(category);
732 tabLayout.addTab(tab, false /* setSelected */);
733
734 if (firstEnumerableCategoryTab == null && category.isEnumerable()) {
735 firstEnumerableCategoryTab = tab;
736 }
737
738 boolean shouldSelectTab = (i == selectedTabPosition)
739 || (selectedTabPosition == -1
740 && tabToSelect == null
741 && category.isEnumerable()
742 && currentlySetCollectionId != null
743 && currentlySetCollectionId.equals(category.getCollectionId()));
744
745 if (shouldSelectTab) {
746 tabToSelect = tab;
747 }
748 }
749
750 // If the above loop did not identify a specific tab to select, then just select the tab for
751 // the first enumerable category.
752 if (tabToSelect == null) {
753 tabToSelect = firstEnumerableCategoryTab;
754 }
755
756 // There may be no enumerable tabs (e.g., offline case), so we need to null-check again.
757 if (tabToSelect != null) {
758 tabToSelect.select();
759 }
760 }
761
762 /**
763 * Refreshes the current wallpaper in a daily wallpaper rotation.
764 */
765 private void refreshDailyWallpaper() {
766 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
767 // causes Espresso to hang once the dialog is shown.
768 if (!mTestingMode) {
769 int themeResId;
770 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
771 themeResId = R.style.ProgressDialogThemePreL;
772 } else {
773 themeResId = R.style.LightDialogTheme;
774 }
775 mRefreshWallpaperProgressDialog = new ProgressDialog(this, themeResId);
776 mRefreshWallpaperProgressDialog.setTitle(null);
777 mRefreshWallpaperProgressDialog.setMessage(
778 getResources().getString(R.string.refreshing_daily_wallpaper_dialog_message));
779 mRefreshWallpaperProgressDialog.setIndeterminate(true);
780 mRefreshWallpaperProgressDialog.show();
781 }
782
783 WallpaperRotationRefresher wallpaperRotationRefresher =
784 InjectorProvider.getInjector().getWallpaperRotationRefresher();
785 wallpaperRotationRefresher.refreshWallpaper(this, new Listener() {
786 @Override
787 public void onRefreshed() {
788 if (isDestroyed()) {
789 return;
790 }
791
792 if (mRefreshWallpaperProgressDialog != null) {
793 mRefreshWallpaperProgressDialog.dismiss();
794 }
795
796 refreshCurrentWallpapers(null /* refreshListener */);
797 }
798
799 @Override
800 public void onError() {
801 if (mRefreshWallpaperProgressDialog != null) {
802 mRefreshWallpaperProgressDialog.dismiss();
803 }
804
805 AlertDialog errorDialog = new AlertDialog.Builder(
806 TopLevelPickerActivity.this, R.style.LightDialogTheme)
807 .setMessage(R.string.refresh_daily_wallpaper_failed_message)
808 .setPositiveButton(android.R.string.ok, null /* onClickListener */)
809 .create();
810 errorDialog.show();
811 }
812 });
813 }
814
815 @Override
816 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
817 super.onActivityResult(requestCode, resultCode, data);
818
819 if (requestCode == SHOW_CATEGORY_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
820 Uri imageUri = (data == null) ? null : data.getData();
821 if (imageUri != null) {
822 // User selected an image from the system picker, so launch the preview for that image.
823 ImageWallpaperInfo imageWallpaper = new ImageWallpaperInfo(imageUri);
824 if (mFormFactor == FormFactorChecker.FORM_FACTOR_DESKTOP) {
825 setCustomPhotoWallpaper(imageWallpaper);
826 return;
827 }
828
829 imageWallpaper.showPreview(this, mPreviewIntentFactory, PREVIEW_WALLPAPER_REQUEST_CODE);
830 } else {
831 // User finished viewing a category without any data, which implies that the user previewed
832 // and selected a wallpaper in-app, so finish this activity.
833 finishActivityWithResultOk();
834 }
835 } else if (requestCode == PREVIEW_WALLPAPER_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
836 // User previewed and selected a wallpaper, so finish this activity.
837 finishActivityWithResultOk();
838 }
839 }
840
841 /**
842 * Shows the view-only preview activity for the given wallpaper.
843 */
844 public void showViewOnlyPreview(WallpaperInfo wallpaperInfo) {
845 wallpaperInfo.showPreview(
846 this, mViewOnlyPreviewIntentFactory, VIEW_ONLY_PREVIEW_WALLPAPER_REQUEST_CODE);
847 }
848
849 @Override
850 public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
851 @NonNull int[] grantResults) {
852 if (requestCode == READ_EXTERNAL_STORAGE_PERMISSION_REQUEST_CODE
853 && permissions.length > 0
854 && permissions[0].equals(permission.READ_EXTERNAL_STORAGE)
855 && grantResults.length > 0) {
856 if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
857 for (PermissionChangedListener listener : mPermissionChangedListeners) {
858 listener.onPermissionsGranted();
859 }
860 } else if (!shouldShowRequestPermissionRationale(permission.READ_EXTERNAL_STORAGE)) {
861 for (PermissionChangedListener listener : mPermissionChangedListeners) {
862 listener.onPermissionsDenied(true /* dontAskAgain */);
863 }
864 } else {
865 for (PermissionChangedListener listener : mPermissionChangedListeners) {
866 listener.onPermissionsDenied(false /* dontAskAgain */);
867 }
868 }
869 }
870 mPermissionChangedListeners.clear();
871 }
872
873 /**
874 * Shows the picker activity for the given category.
875 */
876 public void show(String collectionId) {
877 Category category = findCategoryForCollectionId(collectionId);
878 if (category == null) {
879 return;
880 }
881
882 if (mFormFactor == FormFactorChecker.FORM_FACTOR_MOBILE) {
883 category.show(this, mPickerIntentFactory, SHOW_CATEGORY_REQUEST_CODE);
884 } else { // DESKTOP
885 showCategoryDesktop(collectionId);
886 }
887 }
888
889 private void reselectLastTab() {
890 TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
891
892 // In the offline case, "My photos" could be the only category. Thus we need this check --
893 // to ensure that we don't try to select the "previously selected" category which was -1.
894 if (mLastSelectedCategoryTabIndex > -1) {
895 Tab tabToSelect = tabLayout.getTabAt(mLastSelectedCategoryTabIndex);
896 if (((Category) tabToSelect.getTag()).isEnumerable()) {
897 tabToSelect.select();
898 }
899 }
900 }
901
902 @Nullable
903 private Category findCategoryForCollectionId(String collectionId) {
904 for (Category category : mCategories) {
905 if (category.getCollectionId().equals(collectionId)) {
906 return category;
907 }
908 }
909 return null;
910 }
911
912 private void showCategoryDesktop(String collectionId) {
913 Category category = findCategoryForCollectionId(collectionId);
914 if (category == null) {
915 return;
916 }
917
918 if (category.isEnumerable()) {
919 // Replace contained IndividualPickerFragment with a new instance for the given category.
920 final FragmentManager fm = getSupportFragmentManager();
921 Fragment fragment = fm.findFragmentById(R.id.fragment_container);
922 if (fragment != null) {
923 fm.beginTransaction()
924 .remove(fragment)
925 .commit();
926 }
927 IndividualPickerFragment newFragment = IndividualPickerFragment.newInstance(collectionId);
928 fm.beginTransaction()
929 .add(R.id.fragment_container, newFragment)
930 .commit();
931 newFragment.setCurrentWallpaperBottomSheetPresenter(this);
932 newFragment.setWallpapersUiContainer(this);
933 } else {
934 category.show(this, mPickerIntentFactory, SHOW_CATEGORY_REQUEST_CODE);
935
936 // Need to select the tab here in case we are coming back from a "My photos" in which case
937 // the tab would have been set to "My photos" while viewing a regular image category.
938 reselectLastTab();
939 }
940 }
941
942 private void finishActivityWithResultOk() {
943 overridePendingTransition(R.anim.fade_in, R.anim.fade_out);
944 setResult(Activity.RESULT_OK);
945 finish();
946 }
947
948 @WallpaperSupportLevel
949 private int getWallpaperSupportLevel() {
950 WallpaperManager wallpaperManager = WallpaperManager.getInstance(this);
951
952 if (VERSION.SDK_INT >= VERSION_CODES.N) {
953 if (wallpaperManager.isWallpaperSupported()) {
954 return wallpaperManager.isSetWallpaperAllowed()
955 ? WallpaperDisabledFragment.SUPPORTED_CAN_SET
956 : WallpaperDisabledFragment.NOT_SUPPORTED_BLOCKED_BY_ADMIN;
957 }
958 return WallpaperDisabledFragment.NOT_SUPPORTED_BY_DEVICE;
959 } else if (VERSION.SDK_INT >= VERSION_CODES.M) {
960 return wallpaperManager.isWallpaperSupported() ? WallpaperDisabledFragment.SUPPORTED_CAN_SET
961 : WallpaperDisabledFragment.NOT_SUPPORTED_BY_DEVICE;
962 } else {
963 WallpaperManagerCompat wallpaperManagerCompat =
964 InjectorProvider.getInjector().getWallpaperManagerCompat(this);
965 boolean isSupported = wallpaperManagerCompat.getDrawable() != null;
966 wallpaperManager.forgetLoadedWallpaper();
967 return isSupported ? WallpaperDisabledFragment.SUPPORTED_CAN_SET
968 : WallpaperDisabledFragment.NOT_SUPPORTED_BY_DEVICE;
969 }
970 }
971
972 @Override
973 public void setCurrentWallpapersExpanded(boolean expanded) {
974 final BottomSheetBehavior<LinearLayout> bottomSheetBehavior =
975 BottomSheetBehavior.from(mBottomSheet);
976 bottomSheetBehavior.setState(
977 expanded ? BottomSheetBehavior.STATE_EXPANDED : BottomSheetBehavior.STATE_COLLAPSED);
978 }
979
980 @Override
981 public void onWallpapersReady() {
982 setDesktopLoading(false);
983 setCurrentWallpapersExpanded(true);
984 }
985
986 @Override
987 public void onClickTryAgain(@Destination int unused) {
988 // Retry the set wallpaper operation with the default center-crop setting.
989 if (mPendingSetWallpaperInfo != null) {
990 setCustomPhotoWallpaper(mPendingSetWallpaperInfo);
991 }
992 }
993
994 /**
995 * Sets the provides wallpaper to the device with center-cropped and scaled to fit the device's
996 * default display.
997 */
998 private void setCustomPhotoWallpaper(final WallpaperInfo wallpaper) {
999 // Save this WallpaperInfo so we can retry this operation later if it fails.
1000 mPendingSetWallpaperInfo = wallpaper;
1001
1002 showSettingWallpaperProgressDialog();
1003
1004 mWallpaperPersister.setIndividualWallpaperWithPosition(this, wallpaper,
1005 WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP, new SetWallpaperCallback() {
1006 @Override
1007 public void onSuccess() {
1008 dismissSettingWallpaperProgressDialog();
1009 refreshCurrentWallpapers(null /* refreshListener */);
1010
1011 mPreferences.setPendingWallpaperSetStatus(
1012 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
1013 mUserEventLogger.logWallpaperSet(
1014 wallpaper.getCollectionId(getApplicationContext()),
1015 wallpaper.getWallpaperId());
1016 mUserEventLogger.logWallpaperSetResult(UserEventLogger.WALLPAPER_SET_RESULT_SUCCESS);
1017
1018 // The user may have closed the activity before the set wallpaper operation completed.
1019 if (isDestroyed()) {
1020 return;
1021 }
1022
1023 // Show the wallpaper crop option selector and bind click event handlers.
1024 mWallpaperPositionOptions.setVisibility(View.VISIBLE);
1025
1026 mWasCustomPhotoWallpaperSet = true;
1027 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP;
1028
1029 initializeWallpaperPositionOptionClickHandlers(wallpaper);
1030 }
1031
1032 @Override
1033 public void onError(Throwable throwable) {
1034 dismissSettingWallpaperProgressDialog();
1035 showSetWallpaperErrorDialog();
1036
1037 mPreferences.setPendingWallpaperSetStatus(
1038 WallpaperPreferences.WALLPAPER_SET_NOT_PENDING);
1039 mUserEventLogger.logWallpaperSetResult(
1040 UserEventLogger.WALLPAPER_SET_RESULT_FAILURE);
1041 @WallpaperSetFailureReason int failureReason = ThrowableAnalyzer.isOOM(throwable)
1042 ? UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OOM
1043 : UserEventLogger.WALLPAPER_SET_FAILURE_REASON_OTHER;
1044 mUserEventLogger.logWallpaperSetFailureReason(failureReason);
1045 Log.e(TAG, "Unable to set wallpaper from 'my photos'.");
1046 }
1047 });
1048 }
1049
1050 /**
1051 * Initializes the wallpaper position button click handlers to change the way the provided
1052 * wallpaper is set to the device.
1053 */
1054 private void initializeWallpaperPositionOptionClickHandlers(final WallpaperInfo wallpaperInfo) {
1055 Button centerCropOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_center_crop);
1056 Button stretchOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_stretched);
1057 Button centerOptionBtn = (Button) findViewById(R.id.wallpaper_position_option_center);
1058
1059 // The "center crop" wallpaper position button is selected by default.
1060 setCenterCropWallpaperPositionButtonSelected(centerCropOptionBtn, true /* isSelected */);
1061 centerCropOptionBtn.setOnClickListener(new OnClickListener() {
1062 @Override
1063 public void onClick(View view) {
1064 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
1065 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP,
1066 new SetWallpaperCallback() {
1067 @Override
1068 public void onSuccess() {
1069 // The user may have closed the activity before the set wallpaper operation
1070 // completed.
1071 if (isDestroyed()) {
1072 return;
1073 }
1074
1075 refreshCurrentWallpapers(null /* refreshListener */);
1076
1077 setCenterCropWallpaperPositionButtonSelected(
1078 centerCropOptionBtn, true /* isSelected */);
1079 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
1080 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
1081
1082 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER_CROP;
1083 }
1084
1085 @Override
1086 public void onError(@Nullable Throwable throwable) {
1087 // no-op
1088 }
1089 });
1090 }
1091 });
1092
1093 // "Stretch" is not selected by default.
1094 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
1095 stretchOptionBtn.setOnClickListener(new OnClickListener() {
1096 @Override
1097 public void onClick(View view) {
1098 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
1099 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_STRETCH,
1100 new SetWallpaperCallback() {
1101 @Override
1102 public void onSuccess() {
1103 // The user may have closed the activity before the set wallpaper operation
1104 // completed.
1105 if (isDestroyed()) {
1106 return;
1107 }
1108
1109 refreshCurrentWallpapers(null /* refreshListener */);
1110
1111 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, true /* isSelected */);
1112 setCenterCropWallpaperPositionButtonSelected(
1113 centerCropOptionBtn, false /* isSelected */);
1114 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
1115
1116 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_STRETCH;
1117 }
1118
1119 @Override
1120 public void onError(@Nullable Throwable throwable) {
1121 // no-op
1122 }
1123 });
1124 }
1125 });
1126
1127 // "Center" is not selected by default.
1128 setCenterWallpaperPositionButtonSelected(centerOptionBtn, false /* isSelected */);
1129 centerOptionBtn.setOnClickListener(new OnClickListener() {
1130 @Override
1131 public void onClick(View view) {
1132 mWallpaperPersister.setIndividualWallpaperWithPosition(TopLevelPickerActivity.this,
1133 wallpaperInfo, WallpaperPersister.WALLPAPER_POSITION_CENTER,
1134 new SetWallpaperCallback() {
1135 @Override
1136 public void onSuccess() {
1137 // The user may have closed the activity before the set wallpaper operation
1138 // completed.
1139 if (isDestroyed()) {
1140 return;
1141 }
1142
1143 refreshCurrentWallpapers(null /* refreshListener */);
1144
1145 setCenterWallpaperPositionButtonSelected(centerOptionBtn, true /* isSelected */);
1146 setCenterCropWallpaperPositionButtonSelected(
1147 centerCropOptionBtn, false /* isSelected */);
1148 setStretchWallpaperPositionButtonSelected(stretchOptionBtn, false /* isSelected */);
1149
1150 mCustomPhotoWallpaperPosition = WallpaperPersister.WALLPAPER_POSITION_CENTER;
1151 }
1152
1153 @Override
1154 public void onError(@Nullable Throwable throwable) {
1155 // no-op
1156 }
1157 });
1158 }
1159 });
1160 }
1161
1162 private void setCenterWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1163 int drawableId = isSelected ? R.drawable.center_blue : R.drawable.center_grey;
1164 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
1165 button.setTextColor(getColor(getTextColorIdForWallpaperPositionButton(isSelected)));
1166 }
1167
1168 private void setCenterCropWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1169 int drawableId = isSelected ? R.drawable.center_crop_blue : R.drawable.center_crop_grey;
1170 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
1171 button.setTextColor(getColor(getTextColorIdForWallpaperPositionButton(isSelected)));
1172 }
1173
1174 private void setStretchWallpaperPositionButtonSelected(Button button, boolean isSelected) {
1175 int drawableId = isSelected ? R.drawable.stretch_blue : R.drawable.stretch_grey;
1176 ButtonDrawableSetterCompat.setDrawableToButtonStart(button, getDrawable(drawableId));
1177 button.setTextColor(getColor(getTextColorIdForWallpaperPositionButton(isSelected)));
1178 }
1179
1180 private void showSettingWallpaperProgressDialog() {
1181 // ProgressDialog endlessly updates the UI thread, keeping it from going idle which therefore
1182 // causes Espresso to hang once the dialog is shown.
1183 if (!mTestingMode) {
1184 int themeResId;
1185 if (VERSION.SDK_INT < VERSION_CODES.LOLLIPOP) {
1186 themeResId = R.style.ProgressDialogThemePreL;
1187 } else {
1188 themeResId = R.style.LightDialogTheme;
1189 }
1190 mSetWallpaperProgressDialog = new ProgressDialog(this, themeResId);
1191 mSetWallpaperProgressDialog.setTitle(null);
1192 mSetWallpaperProgressDialog.setMessage(
1193 getResources().getString(R.string.set_wallpaper_progress_message));
1194 mSetWallpaperProgressDialog.setIndeterminate(true);
1195 mSetWallpaperProgressDialog.show();
1196 }
1197 }
1198
1199 private void dismissSettingWallpaperProgressDialog() {
1200 if (mSetWallpaperProgressDialog != null) {
1201 mSetWallpaperProgressDialog.dismiss();
1202 }
1203 }
1204
1205 private void showSetWallpaperErrorDialog() {
1206 SetWallpaperErrorDialogFragment dialogFragment = SetWallpaperErrorDialogFragment.newInstance(
1207 R.string.set_wallpaper_error_message, WallpaperPersister.DEST_BOTH);
1208
1209 if (isSafeToCommitFragmentTransaction()) {
1210 dialogFragment.show(getSupportFragmentManager(), TAG_SET_WALLPAPER_ERROR_DIALOG_FRAGMENT);
1211 } else {
1212 mStagedSetWallpaperErrorDialogFragment = dialogFragment;
1213 }
1214 }
1215
1216 private interface AssetReceiver {
1217 void onAssetReceived(Asset asset);
1218 }
1219
1220 /**
1221 * An AsyncTask for asynchronously fetching the thumbnail asset for a given WallpaperInfo.
1222 * Used to work around expensive method call to WallpaperManager#getWallpaperFile made from the
1223 * CurrentWallpaperInfoVN getAsset() method.
1224 */
1225 private static class FetchThumbAssetTask extends AsyncTask<Void, Void, Asset> {
1226 private Context mAppContext;
1227 private WallpaperInfo mWallpaperInfo;
1228 private AssetReceiver mReceiver;
1229
1230 public FetchThumbAssetTask(Context appContext, WallpaperInfo wallpaperInfo,
1231 AssetReceiver receiver) {
1232 mAppContext = appContext;
1233 mWallpaperInfo = wallpaperInfo;
1234 mReceiver = receiver;
1235 }
1236
1237 @Override
1238 protected Asset doInBackground(Void... params) {
1239 return mWallpaperInfo.getThumbAsset(mAppContext);
1240 }
1241
1242 @Override
1243 protected void onPostExecute(Asset thumbAsset) {
1244 mReceiver.onAssetReceived(thumbAsset);
1245 }
1246 }
1247}