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