1 /* 2 * Copyright (C) 2022 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 static android.view.View.MeasureSpec.EXACTLY; 19 import static android.view.View.MeasureSpec.makeMeasureSpec; 20 21 import static com.android.wallpaper.util.WallpaperSurfaceCallback.LOW_RES_BITMAP_BLUR_RADIUS; 22 23 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_EXPANDED; 24 import static com.google.android.material.bottomsheet.BottomSheetBehavior.STATE_HIDDEN; 25 26 import android.animation.Animator; 27 import android.animation.AnimatorListenerAdapter; 28 import android.app.Activity; 29 import android.app.WallpaperManager; 30 import android.content.Context; 31 import android.content.res.Resources; 32 import android.graphics.Bitmap; 33 import android.graphics.Color; 34 import android.graphics.Point; 35 import android.graphics.PointF; 36 import android.graphics.Rect; 37 import android.graphics.RenderEffect; 38 import android.graphics.Shader; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.util.Log; 42 import android.view.LayoutInflater; 43 import android.view.Surface; 44 import android.view.SurfaceControlViewHost; 45 import android.view.SurfaceHolder; 46 import android.view.SurfaceView; 47 import android.view.View; 48 import android.view.ViewGroup; 49 import android.view.ViewGroup.LayoutParams; 50 import android.view.animation.Interpolator; 51 import android.view.animation.PathInterpolator; 52 import android.widget.ImageView; 53 54 import androidx.annotation.NonNull; 55 import androidx.annotation.Nullable; 56 import androidx.annotation.VisibleForTesting; 57 58 import com.android.wallpaper.R; 59 import com.android.wallpaper.asset.Asset; 60 import com.android.wallpaper.asset.CurrentWallpaperAssetVN; 61 import com.android.wallpaper.model.SetWallpaperViewModel; 62 import com.android.wallpaper.model.WallpaperInfo.ColorInfo; 63 import com.android.wallpaper.module.BitmapCropper; 64 import com.android.wallpaper.module.Injector; 65 import com.android.wallpaper.module.InjectorProvider; 66 import com.android.wallpaper.module.WallpaperPersister.Destination; 67 import com.android.wallpaper.module.WallpaperPreferences; 68 import com.android.wallpaper.util.DisplayUtils; 69 import com.android.wallpaper.util.OnFullResImageViewStateChangedListener; 70 import com.android.wallpaper.util.ResourceUtils; 71 import com.android.wallpaper.util.RtlUtils; 72 import com.android.wallpaper.util.ScreenSizeCalculator; 73 import com.android.wallpaper.util.WallpaperColorsExtractor; 74 import com.android.wallpaper.util.WallpaperCropUtils; 75 76 import com.bumptech.glide.Glide; 77 import com.bumptech.glide.MemoryCategory; 78 import com.davemorrissey.labs.subscaleview.ImageSource; 79 import com.davemorrissey.labs.subscaleview.SubsamplingScaleImageView; 80 import com.google.android.material.bottomsheet.BottomSheetBehavior; 81 82 import java.util.concurrent.ExecutionException; 83 import java.util.concurrent.Executor; 84 import java.util.concurrent.Executors; 85 import java.util.concurrent.Future; 86 87 /** 88 * Fragment which displays the UI for previewing an individual static image wallpaper and its 89 * attribution information. 90 */ 91 public class ImagePreviewFragment extends PreviewFragment { 92 93 private static final String TAG = "ImagePreviewFragment"; 94 95 private static final float DEFAULT_WALLPAPER_MAX_ZOOM = 8f; 96 private static final Interpolator ALPHA_OUT = new PathInterpolator(0f, 0f, 0.8f, 1f); 97 private static final Executor sExecutor = Executors.newCachedThreadPool(); 98 99 private final WallpaperSurfaceCallback mWallpaperSurfaceCallback = 100 new WallpaperSurfaceCallback(); 101 private final Injector mInjector = InjectorProvider.getInjector(); 102 103 /** 104 * Size of the screen considered for cropping the wallpaper (typically the same as 105 * {@link #mScreenSize} but it could be different on multi-display) 106 */ 107 private Point mWallpaperScreenSize; 108 /** 109 * The size of the current screen 110 */ 111 private Point mScreenSize; 112 protected Point mRawWallpaperSize; // Native size of wallpaper image. 113 private WallpaperPreferences mWallpaperPreferences; 114 protected Asset mWallpaperAsset; 115 protected Future<ColorInfo> mColorFuture; 116 private WallpaperPreviewBitmapTransformation mPreviewBitmapTransformation; 117 private BitmapCropper mBitmapCropper; 118 private WallpaperColorsExtractor mWallpaperColorsExtractor; 119 private DisplayUtils mDisplayUtils; 120 private WallpaperManager mWallpaperManager; 121 122 // UI 123 protected SurfaceView mWallpaperSurface; 124 protected ImageView mLowResImageView; 125 protected SubsamplingScaleImageView mFullResImageView; 126 127 @Override onCreate(Bundle savedInstanceState)128 public void onCreate(Bundle savedInstanceState) { 129 super.onCreate(savedInstanceState); 130 Context context = requireContext(); 131 Context appContext = context.getApplicationContext(); 132 mWallpaperAsset = mWallpaper.getAsset(appContext); 133 mColorFuture = mWallpaper.computeColorInfo(context); 134 mWallpaperPreferences = mInjector.getPreferences(context); 135 mPreviewBitmapTransformation = new WallpaperPreviewBitmapTransformation( 136 appContext, RtlUtils.isRtl(context)); 137 mBitmapCropper = mInjector.getBitmapCropper(); 138 mWallpaperColorsExtractor = new WallpaperColorsExtractor(sExecutor, Handler.getMain()); 139 mWallpaperManager = context.getSystemService(WallpaperManager.class); 140 } 141 142 @Override onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)143 public View onCreateView(LayoutInflater inflater, ViewGroup container, 144 Bundle savedInstanceState) { 145 View view = super.onCreateView(inflater, container, savedInstanceState); 146 if (view == null) { 147 return null; 148 } 149 // Until we have initialized mRawWallpaperSize, we can't set wallpaper 150 mSetWallpaperButton.setEnabled(false); 151 mSetWallpaperButtonContainer.setEnabled(false); 152 Activity activity = requireActivity(); 153 mDisplayUtils = mInjector.getDisplayUtils(activity); 154 ScreenSizeCalculator screenSizeCalculator = ScreenSizeCalculator.getInstance(); 155 mScreenSize = screenSizeCalculator.getScreenSize( 156 activity.getWindowManager().getDefaultDisplay()); 157 // "Wallpaper screen" size will be the size of the largest screen available 158 mWallpaperScreenSize = screenSizeCalculator.getScreenSize( 159 mDisplayUtils.getWallpaperDisplay()); 160 // Touch forwarding layout 161 setUpTouchForwardingLayout(); 162 // Wallpaper surface 163 mWallpaperSurface = view.findViewById(R.id.wallpaper_surface); 164 mWallpaperSurface.getHolder().addCallback(mWallpaperSurfaceCallback); 165 // Trim memory from Glide to make room for the full-size image in this fragment. 166 Glide.get(activity).setMemoryCategory(MemoryCategory.LOW); 167 return view; 168 } 169 170 @VisibleForTesting(otherwise = VisibleForTesting.NONE) getFullResImageView()171 public SubsamplingScaleImageView getFullResImageView() { 172 return mFullResImageView; 173 } 174 setUpTouchForwardingLayout()175 private void setUpTouchForwardingLayout() { 176 mTouchForwardingLayout.setForwardingEnabled(true); 177 mTouchForwardingLayout.setOnClickListener(v -> { 178 toggleWallpaperPreviewControl(); 179 mTouchForwardingLayout.announceForAccessibility( 180 getString(mPreviewScrim.getVisibility() == View.VISIBLE 181 ? R.string.show_preview_controls_content_description 182 : R.string.hide_preview_controls_content_description) 183 ); 184 }); 185 mFloatingSheet.addFloatingSheetCallback( 186 new BottomSheetBehavior.BottomSheetCallback() { 187 @Override 188 public void onStateChanged(@NonNull View bottomSheet, int newState) { 189 if (newState == STATE_EXPANDED) { 190 mTouchForwardingLayout.setForwardingEnabled(false); 191 } else if (newState == STATE_HIDDEN) { 192 mTouchForwardingLayout.setForwardingEnabled(true); 193 } 194 } 195 196 @Override 197 public void onSlide(@NonNull View bottomSheet, float slideOffset) { 198 } 199 }); 200 } 201 202 @Override onDestroy()203 public void onDestroy() { 204 if (mFullResImageView != null) { 205 mFullResImageView.recycle(); 206 } 207 mWallpaperSurfaceCallback.cleanUp(); 208 super.onDestroy(); 209 } 210 211 @Override setWallpaper(@estination int destination)212 protected void setWallpaper(@Destination int destination) { 213 Context context = getContext(); 214 if (context == null) { 215 return; 216 } 217 if (mRawWallpaperSize == null) { 218 // This shouldn't happen, avoid direct call into setWallpaper without initializing 219 // mRawWallpaperSize first 220 showSetWallpaperErrorDialog(); 221 return; 222 } 223 // Only crop extra wallpaper width for single display devices. 224 Rect cropRect = calculateCropRect(context, !mDisplayUtils.hasMultiInternalDisplays()); 225 float screenScale = WallpaperCropUtils.getScaleOfScreenResolution( 226 mFullResImageView.getScale(), cropRect, mWallpaperScreenSize.x, 227 mWallpaperScreenSize.y); 228 Rect scaledCropRect = new Rect( 229 Math.round((float) cropRect.left * screenScale), 230 Math.round((float) cropRect.top * screenScale), 231 Math.round((float) cropRect.right * screenScale), 232 Math.round((float) cropRect.bottom * screenScale)); 233 mWallpaperSetter.setCurrentWallpaper(getActivity(), mWallpaper, mWallpaperAsset, 234 destination, mFullResImageView.getScale() * screenScale, scaledCropRect, 235 mWallpaperColors, SetWallpaperViewModel.getCallback(mViewModelProvider)); 236 } 237 238 /** 239 * Initializes image view by initializing tiling, setting a fallback page bitmap, and 240 * initializing a zoom-scroll observer and click listener. 241 */ initFullResView()242 private synchronized void initFullResView() { 243 if (mRawWallpaperSize == null || mFullResImageView == null 244 || mFullResImageView.isImageLoaded()) { 245 return; 246 } 247 248 final String storedWallpaperId = mWallpaper.getStoredWallpaperId(getContext()); 249 final boolean isWallpaperColorCached = 250 storedWallpaperId != null && mWallpaperPreferences.getWallpaperColors( 251 storedWallpaperId) != null; 252 if (isWallpaperColorCached) { 253 // Post-execute onWallpaperColorsChanged() to avoid UI blocking from the call 254 Handler.getMain().post(() -> onWallpaperColorsChanged( 255 mWallpaperPreferences.getWallpaperColors( 256 mWallpaper.getStoredWallpaperId(getContext())))); 257 } 258 259 // Minimum scale will only be respected under this scale type. 260 mFullResImageView.setMinimumScaleType(SubsamplingScaleImageView.SCALE_TYPE_CUSTOM); 261 // When we set a minimum scale bigger than the scale with which the full image is shown, 262 // disallow user to pan outside the view we show the wallpaper in. 263 mFullResImageView.setPanLimit(SubsamplingScaleImageView.PAN_LIMIT_INSIDE); 264 265 Point targetPageBitmapSize = new Point(mRawWallpaperSize); 266 mWallpaperAsset.decodeBitmap(targetPageBitmapSize.x, targetPageBitmapSize.y, 267 pageBitmap -> { 268 if (getActivity() == null || mFullResImageView == null) { 269 return; 270 } 271 272 if (pageBitmap == null) { 273 showLoadWallpaperErrorDialog(); 274 return; 275 } 276 277 mFullResImageView.setImage(ImageSource.bitmap(pageBitmap)); 278 setDefaultWallpaperZoomAndScroll( 279 mWallpaperAsset instanceof CurrentWallpaperAssetVN); 280 mFullResImageView.setOnStateChangedListener( 281 new OnFullResImageViewStateChangedListener() { 282 @Override 283 public void onDebouncedCenterChanged(PointF newCenter, int origin) { 284 recalculateColors(); 285 } 286 } 287 ); 288 if (!isWallpaperColorCached) { 289 mFullResImageView.setAlpha(0); 290 // If not cached, delay the cross fade until the colors extracted 291 extractColorFromBitmap(pageBitmap, true); 292 } else { 293 onSurfaceReady(); 294 } 295 }); 296 } 297 298 /** 299 * Recalculate the color from a new crop of the wallpaper. Note that we do not cache the 300 * extracted. We only cache the color the first time we extract from the wallpaper as its 301 * original size. 302 */ recalculateColors()303 private void recalculateColors() { 304 Context context = getContext(); 305 if (context == null) { 306 return; 307 } 308 309 mBitmapCropper.cropAndScaleBitmap(mWallpaperAsset, mFullResImageView.getScale(), 310 calculateCropRect(context, /* cropExtraWidth= */ true), /* adjustForRtl= */ false, 311 new BitmapCropper.Callback() { 312 @Override 313 public void onBitmapCropped(Bitmap croppedBitmap) { 314 extractColorFromBitmap(croppedBitmap, false); 315 } 316 317 @Override 318 public void onError(@Nullable Throwable e) { 319 Log.w(TAG, "Recalculate colors, crop and scale bitmap failed.", e); 320 } 321 }); 322 } 323 extractColorFromBitmap(Bitmap croppedBitmap, boolean cacheColor)324 private void extractColorFromBitmap(Bitmap croppedBitmap, boolean cacheColor) { 325 Context context = getContext(); 326 if (context == null) { 327 return; 328 } 329 330 mWallpaperColorsExtractor.extractWallpaperColors(croppedBitmap, 331 colors -> { 332 if (mFullResImageView.getAlpha() == 0) { 333 onSurfaceReady(); 334 } 335 onWallpaperColorsChanged(colors); 336 if (cacheColor) { 337 mWallpaperPreferences.storeWallpaperColors( 338 mWallpaper.getStoredWallpaperId(context), colors); 339 } 340 }); 341 } 342 343 /** 344 * This should be called when the full resolution image is loaded and the wallpaper color is 345 * ready, either extracted from the wallpaper or retrieved from cache. 346 */ onSurfaceReady()347 private void onSurfaceReady() { 348 mProgressBar.setVisibility(View.GONE); 349 crossFadeInFullResView(); 350 // Set button enabled for the visual change 351 mSetWallpaperButton.setEnabled(true); 352 // Set button container enabled to make it clickable 353 mSetWallpaperButtonContainer.setEnabled(true); 354 } 355 356 /** 357 * Fade in the full resolution view. 358 */ crossFadeInFullResView()359 protected void crossFadeInFullResView() { 360 if (getActivity() == null || !isAdded()) { 361 return; 362 } 363 long shortAnimationDuration = getResources().getInteger( 364 android.R.integer.config_shortAnimTime); 365 366 mFullResImageView.setAlpha(0f); 367 mFullResImageView.animate() 368 .alpha(1f) 369 .setInterpolator(ALPHA_OUT) 370 .setDuration(shortAnimationDuration) 371 .setListener(new AnimatorListenerAdapter() { 372 @Override 373 public void onAnimationEnd(Animator animation) { 374 if (mLowResImageView != null) { 375 mLowResImageView.setImageBitmap(null); 376 } 377 } 378 }); 379 } 380 381 /** 382 * Sets the default wallpaper zoom and scroll position based on a "crop surface" (with extra 383 * width to account for parallax) superimposed on the screen. Shows as much of the wallpaper as 384 * possible on the crop surface and align screen to crop surface such that the default preview 385 * matches what would be seen by the user in the left-most home screen. 386 * 387 * <p>This method is called once in the Fragment lifecycle after the wallpaper asset has loaded 388 * and rendered to the layout. 389 * 390 * @param offsetToStart {@code true} if we want to offset the visible rectangle to the start 391 * side of the raw wallpaper; {@code false} otherwise. 392 */ setDefaultWallpaperZoomAndScroll(boolean offsetToStart)393 private void setDefaultWallpaperZoomAndScroll(boolean offsetToStart) { 394 // Determine minimum zoom to fit maximum visible area of wallpaper on crop surface. 395 int cropWidth = mWallpaperSurface.getMeasuredWidth(); 396 int cropHeight = mWallpaperSurface.getMeasuredHeight(); 397 Point crop = new Point(cropWidth, cropHeight); 398 Rect visibleRawWallpaperRect = 399 WallpaperCropUtils.calculateVisibleRect(mRawWallpaperSize, crop); 400 if (offsetToStart && mDisplayUtils.isSingleDisplayOrUnfoldedHorizontalHinge( 401 requireActivity())) { 402 if (RtlUtils.isRtl(requireContext())) { 403 visibleRawWallpaperRect.offsetTo(mRawWallpaperSize.x 404 - visibleRawWallpaperRect.width(), visibleRawWallpaperRect.top); 405 } else { 406 visibleRawWallpaperRect.offsetTo(/* newLeft= */ 0, visibleRawWallpaperRect.top); 407 } 408 } 409 410 final PointF centerPosition = new PointF(visibleRawWallpaperRect.centerX(), 411 visibleRawWallpaperRect.centerY()); 412 413 Point visibleRawWallpaperSize = new Point(visibleRawWallpaperRect.width(), 414 visibleRawWallpaperRect.height()); 415 416 final float defaultWallpaperZoom = WallpaperCropUtils.calculateMinZoom( 417 visibleRawWallpaperSize, crop); 418 419 // Set min wallpaper zoom and max zoom for the full resolution image view 420 mFullResImageView.setMaxScale(Math.max(DEFAULT_WALLPAPER_MAX_ZOOM, defaultWallpaperZoom)); 421 mFullResImageView.setMinScale(defaultWallpaperZoom); 422 423 // Set center to composite positioning between scaled wallpaper and screen 424 mFullResImageView.setScaleAndCenter(defaultWallpaperZoom, centerPosition); 425 } 426 calculateCropRect(Context context, boolean cropExtraWidth)427 private Rect calculateCropRect(Context context, boolean cropExtraWidth) { 428 float wallpaperZoom = mFullResImageView.getScale(); 429 Context appContext = context.getApplicationContext(); 430 431 Rect visibleFileRect = new Rect(); 432 mFullResImageView.visibleFileRect(visibleFileRect); 433 434 int cropWidth = mWallpaperSurface.getMeasuredWidth(); 435 int cropHeight = mWallpaperSurface.getMeasuredHeight(); 436 int maxCrop = Math.max(cropWidth, cropHeight); 437 int minCrop = Math.min(cropWidth, cropHeight); 438 Point hostViewSize = new Point(cropWidth, cropHeight); 439 440 Resources res = appContext.getResources(); 441 Point cropSurfaceSize = WallpaperCropUtils.calculateCropSurfaceSize(res, maxCrop, minCrop, 442 cropWidth, cropHeight); 443 Rect result = WallpaperCropUtils.calculateCropRect(appContext, hostViewSize, 444 cropSurfaceSize, mRawWallpaperSize, visibleFileRect, wallpaperZoom, cropExtraWidth); 445 446 // Cancel the rescaling in the multi crop case. In that case the crop will be sent to 447 // WallpaperManager. WallpaperManager expects a crop that is not yet rescaled to match 448 // the screen size (as opposed to BitmapCropper which is used in the single crop case). 449 // TODO(b/270726737, b/281648899) clean that comment and that part of the code 450 if (mWallpaperManager.isMultiCropEnabled()) result.scale(1f / mFullResImageView.getScale()); 451 return result; 452 } 453 454 /** 455 * surfaceCreated() is called right after Fragment.onResume() and surfaceDestroyed() is called 456 * after Fragment.onPause(). We do not clean up the surface when surfaceDestroyed() and hold 457 * it till the next onResume(). We do not need to decode the image again and thus can skip the 458 * whole logic in surfaceCreated(). 459 */ 460 private class WallpaperSurfaceCallback implements SurfaceHolder.Callback { 461 private Surface mLastSurface; 462 private SurfaceControlViewHost mHost; 463 464 @Override surfaceCreated(SurfaceHolder holder)465 public void surfaceCreated(SurfaceHolder holder) { 466 Context context = getContext(); 467 Activity activity = getActivity(); 468 if (context == null || activity == null || mLastSurface == holder.getSurface()) { 469 return; 470 } 471 472 mLastSurface = holder.getSurface(); 473 if (mFullResImageView != null) { 474 mFullResImageView.recycle(); 475 } 476 477 mProgressBar.setVisibility(View.VISIBLE); 478 View wallpaperPreviewContainer = LayoutInflater.from(context).inflate( 479 R.layout.fullscreen_wallpaper_preview, null); 480 mFullResImageView = wallpaperPreviewContainer.findViewById(R.id.full_res_image); 481 mLowResImageView = wallpaperPreviewContainer.findViewById(R.id.low_res_image); 482 mLowResImageView.setRenderEffect( 483 RenderEffect.createBlurEffect(LOW_RES_BITMAP_BLUR_RADIUS, 484 LOW_RES_BITMAP_BLUR_RADIUS, Shader.TileMode.CLAMP)); 485 // Calculate the size of mWallpaperSurface based on system zoom's scale and 486 // on the larger screen size (if more than one) so that the wallpaper is 487 // rendered in a larger surface than what preview shows, simulating the behavior of 488 // the actual wallpaper surface and so we can crop it to a size that fits in all 489 // screens. 490 float scale = WallpaperCropUtils.getSystemWallpaperMaximumScale(context); 491 int origWidth = mWallpaperSurface.getWidth(); 492 int origHeight = mWallpaperSurface.getHeight(); 493 494 int scaledOrigWidth = origWidth; 495 int scaledOrigHeight = origHeight; 496 497 if (mDisplayUtils.hasMultiInternalDisplays()) { 498 final Point maxDisplaysDimen = mDisplayUtils.getMaxDisplaysDimension(); 499 scaledOrigWidth = Math.round( 500 origWidth * Math.max(1, (float) maxDisplaysDimen.x / mScreenSize.x)); 501 scaledOrigHeight = Math.round( 502 origHeight * Math.max(1, (float) maxDisplaysDimen.y / mScreenSize.y)); 503 } 504 int width = (int) (scaledOrigWidth * scale); 505 int height = (int) (scaledOrigHeight * scale); 506 int left = (origWidth - width) / 2; 507 int top = (origHeight - height) / 2; 508 509 if (RtlUtils.isRtl(context)) { 510 left *= -1; 511 } 512 513 LayoutParams params = mWallpaperSurface.getLayoutParams(); 514 params.width = width; 515 params.height = height; 516 mWallpaperSurface.setX(left); 517 mWallpaperSurface.setY(top); 518 mWallpaperSurface.setLayoutParams(params); 519 mWallpaperSurface.requestLayout(); 520 521 // Load low res image first before the full res image is available 522 int placeHolderColor = ResourceUtils.getColorAttr(activity, 523 android.R.attr.colorBackground); 524 if (mColorFuture.isDone()) { 525 try { 526 int colorValue = mColorFuture.get().getPlaceholderColor(); 527 if (colorValue != Color.TRANSPARENT) { 528 placeHolderColor = colorValue; 529 } 530 } catch (InterruptedException | ExecutionException e) { 531 // Do nothing intended 532 } 533 } 534 mWallpaperAsset.loadLowResDrawable(activity, mLowResImageView, placeHolderColor, 535 mPreviewBitmapTransformation); 536 537 wallpaperPreviewContainer.measure( 538 makeMeasureSpec(width, EXACTLY), 539 makeMeasureSpec(height, EXACTLY)); 540 wallpaperPreviewContainer.layout(0, 0, width, height); 541 mTouchForwardingLayout.setTargetView(mFullResImageView); 542 543 cleanUp(); 544 mHost = new SurfaceControlViewHost(context, 545 context.getDisplay(), mWallpaperSurface.getHostToken()); 546 mHost.setView(wallpaperPreviewContainer, wallpaperPreviewContainer.getWidth(), 547 wallpaperPreviewContainer.getHeight()); 548 mWallpaperSurface.setChildSurfacePackage(mHost.getSurfacePackage()); 549 550 mWallpaperAsset.decodeRawDimensions(getActivity(), dimensions -> { 551 if (getActivity() == null) { 552 return; 553 } 554 555 if (dimensions == null) { 556 showLoadWallpaperErrorDialog(); 557 return; 558 } 559 560 mRawWallpaperSize = dimensions; 561 // We can enable set wallpaper now but defer to full res view ready 562 initFullResView(); 563 }); 564 } 565 566 @Override surfaceChanged(SurfaceHolder holder, int format, int width, int height)567 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 568 // Do nothing intended 569 } 570 571 @Override surfaceDestroyed(SurfaceHolder holder)572 public void surfaceDestroyed(SurfaceHolder holder) { 573 // Do nothing intended 574 } 575 cleanUp()576 public void cleanUp() { 577 if (mHost != null) { 578 mHost.release(); 579 mHost = null; 580 } 581 } 582 } 583 } 584