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