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