BackgroundManager.java revision 0eb4595e6a4d6e9ebc9f5b8065e0c787d45a0f70
1/* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except 5 * in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the License 10 * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express 11 * or implied. See the License for the specific language governing permissions and limitations under 12 * the License. 13 */ 14package android.support.v17.leanback.app; 15 16import android.support.v17.leanback.R; 17import android.animation.ObjectAnimator; 18import android.app.Activity; 19import android.content.Context; 20import android.content.res.Resources; 21import android.content.res.TypedArray; 22import android.graphics.Bitmap; 23import android.graphics.Canvas; 24import android.graphics.Color; 25import android.graphics.ColorFilter; 26import android.graphics.Matrix; 27import android.graphics.Paint; 28import android.graphics.drawable.ColorDrawable; 29import android.graphics.drawable.Drawable; 30import android.graphics.drawable.LayerDrawable; 31import android.os.Handler; 32import android.util.Log; 33import android.view.LayoutInflater; 34import android.view.View; 35import android.view.ViewGroup; 36import android.view.Window; 37import android.view.WindowManager; 38import android.view.animation.LinearInterpolator; 39 40/** 41 * Supports background image continuity between multiple Activities. 42 * 43 * <p>An Activity should instantiate a BackgroundManager and {@link #attach} 44 * to the Activity's window. When the Activity is started, the background is 45 * initialized to the current background values stored in a continuity service. 46 * The background continuity service is updated as the background is updated. 47 * 48 * <p>At some point, for example when it is stopped, the Activity may release 49 * its background state. 50 * 51 * <p>When an Activity is resumed, if the BackgroundManager has not been 52 * released, the continuity service is updated from the BackgroundManager state. 53 * If the BackgroundManager was released, the BackgroundManager inherits the 54 * current state from the continuity service. 55 * 56 * <p>When the last Activity is destroyed, the background state is reset. 57 * 58 * <p>Backgrounds consist of several layers, from back to front: 59 * <ul> 60 * <li>the background Drawable of the theme</li> 61 * <li>a solid color (set via {@link #setColor})</li> 62 * <li>two Drawables, previous and current (set via {@link #setBitmap} or 63 * {@link #setDrawable}), which may be in transition</li> 64 * </ul> 65 * 66 * <p>BackgroundManager holds references to potentially large bitmap Drawables. 67 * Call {@link #release} to release these references when the Activity is not 68 * visible. 69 */ 70// TODO: support for multiple app processes requires a proper android service 71// instead of the shared memory "service" implemented here. Such a service could 72// support continuity between fragments of different applications if desired. 73public final class BackgroundManager { 74 private static final String TAG = "BackgroundManager"; 75 private static final boolean DEBUG = false; 76 77 private static final int FULL_ALPHA = 255; 78 private static final int DIM_ALPHA_ON_SOLID = (int) (0.8f * FULL_ALPHA); 79 private static final int CHANGE_BG_DELAY_MS = 500; 80 private static final int FADE_DURATION_QUICK = 200; 81 private static final int FADE_DURATION_SLOW = 1000; 82 83 /** 84 * Using a separate window for backgrounds can improve graphics performance by 85 * leveraging hardware display layers. 86 * TODO: support a leanback configuration option. 87 */ 88 private static final boolean USE_SEPARATE_WINDOW = false; 89 90 private static final String WINDOW_NAME = "BackgroundManager"; 91 private static final String FRAGMENT_TAG = BackgroundManager.class.getCanonicalName(); 92 93 private Context mContext; 94 private Handler mHandler; 95 private Window mWindow; 96 private WindowManager mWindowManager; 97 private View mBgView; 98 private BackgroundContinuityService mService; 99 private int mThemeDrawableResourceId; 100 101 private int mHeightPx; 102 private int mWidthPx; 103 private Drawable mBackgroundDrawable; 104 private int mBackgroundColor; 105 private boolean mAttached; 106 107 private static class BitmapDrawable extends Drawable { 108 109 Bitmap mBitmap; 110 Matrix mMatrix; 111 Paint mPaint; 112 113 BitmapDrawable(Resources resources, Bitmap bitmap) { 114 this(resources, bitmap, null); 115 } 116 117 BitmapDrawable(Resources resources, Bitmap bitmap, Matrix matrix) { 118 mBitmap = bitmap; 119 mMatrix = matrix != null ? matrix : new Matrix(); 120 mPaint = new Paint(); 121 mPaint.setFilterBitmap(true); 122 } 123 124 Bitmap getBitmap() { 125 return mBitmap; 126 } 127 128 @Override 129 public void draw(Canvas canvas) { 130 if (mBitmap == null) { 131 return; 132 } 133 canvas.drawBitmap(mBitmap, mMatrix, mPaint); 134 } 135 136 @Override 137 public int getOpacity() { 138 return android.graphics.PixelFormat.OPAQUE; 139 } 140 141 @Override 142 public void setAlpha(int alpha) { 143 if (mPaint.getAlpha() != alpha) { 144 mPaint.setAlpha(alpha); 145 invalidateSelf(); 146 } 147 } 148 149 @Override 150 public void setColorFilter(ColorFilter cf) { 151 // Abstract in Drawable, not implemented 152 } 153 } 154 155 private static class DrawableWrapper { 156 protected int mAlpha; 157 protected Drawable mDrawable; 158 protected ObjectAnimator mAnimator; 159 protected boolean mAnimationPending; 160 161 public DrawableWrapper(Drawable drawable) { 162 mDrawable = drawable; 163 setAlpha(FULL_ALPHA); 164 } 165 166 public Drawable getDrawable() { 167 return mDrawable; 168 } 169 public void setAlpha(int alpha) { 170 mAlpha = alpha; 171 mDrawable.setAlpha(alpha); 172 } 173 public int getAlpha() { 174 return mAlpha; 175 } 176 public void setColor(int color) { 177 ((ColorDrawable) mDrawable).setColor(color); 178 } 179 public void fadeIn(int durationMs, int delayMs) { 180 fade(durationMs, delayMs, FULL_ALPHA); 181 } 182 public void fadeOut(int durationMs) { 183 fade(durationMs, 0, 0); 184 } 185 public void fade(int durationMs, int delayMs, int alpha) { 186 if (mAnimator != null && mAnimator.isStarted()) { 187 mAnimator.cancel(); 188 } 189 mAnimator = ObjectAnimator.ofInt(this, "alpha", alpha); 190 mAnimator.setInterpolator(new LinearInterpolator()); 191 mAnimator.setDuration(durationMs); 192 mAnimator.setStartDelay(delayMs); 193 mAnimationPending = true; 194 } 195 public boolean isAnimationPending() { 196 return mAnimationPending; 197 } 198 public boolean isAnimationStarted() { 199 return mAnimator != null && mAnimator.isStarted(); 200 } 201 public void startAnimation() { 202 mAnimator.start(); 203 mAnimationPending = false; 204 } 205 } 206 207 private LayerDrawable mLayerDrawable; 208 private DrawableWrapper mLayerWrapper; 209 private DrawableWrapper mImageInWrapper; 210 private DrawableWrapper mImageOutWrapper; 211 private DrawableWrapper mColorWrapper; 212 private DrawableWrapper mDimWrapper; 213 214 private Drawable mThemeDrawable; 215 private ChangeBackgroundRunnable mChangeRunnable; 216 217 /** 218 * Shared memory continuity service. 219 */ 220 private static class BackgroundContinuityService { 221 private static final String TAG = "BackgroundContinuityService"; 222 private static boolean DEBUG = BackgroundManager.DEBUG; 223 224 private static BackgroundContinuityService sService = new BackgroundContinuityService(); 225 226 private int mColor; 227 private Drawable mDrawable; 228 private int mCount; 229 230 private BackgroundContinuityService() { 231 reset(); 232 } 233 234 private void reset() { 235 mColor = Color.TRANSPARENT; 236 mDrawable = null; 237 } 238 239 public static BackgroundContinuityService getInstance() { 240 final int count = sService.mCount++; 241 if (DEBUG) Log.v(TAG, "Returning instance with new count " + count); 242 return sService; 243 } 244 245 public void unref() { 246 if (mCount <= 0) throw new IllegalStateException("Can't unref, count " + mCount); 247 if (--mCount == 0) { 248 if (DEBUG) Log.v(TAG, "mCount is zero, resetting"); 249 reset(); 250 } 251 } 252 public int getColor() { 253 return mColor; 254 } 255 public Drawable getDrawable() { 256 return mDrawable; 257 } 258 public void setColor(int color) { 259 mColor = color; 260 } 261 public void setDrawable(Drawable drawable) { 262 mDrawable = drawable; 263 } 264 } 265 266 private Drawable getThemeDrawable() { 267 Drawable drawable = null; 268 if (mThemeDrawableResourceId != -1) { 269 drawable = mContext.getResources().getDrawable(mThemeDrawableResourceId); 270 } 271 if (drawable == null) { 272 drawable = createEmptyDrawable(); 273 } 274 return drawable; 275 } 276 277 /** 278 * Get the BackgroundManager associated with the Activity. 279 * <p> 280 * The BackgroundManager will be created on-demand for each individual 281 * Activity. Subsequent calls will return the same BackgroundManager created 282 * for this Activity. 283 */ 284 public static BackgroundManager getInstance(Activity activity) { 285 BackgroundFragment fragment = (BackgroundFragment) activity.getFragmentManager() 286 .findFragmentByTag(FRAGMENT_TAG); 287 if (fragment != null) { 288 BackgroundManager manager = fragment.getBackgroundManager(); 289 if (manager != null) { 290 return manager; 291 } 292 // manager is null: this is a fragment restored by FragmentManager, 293 // fall through to create a BackgroundManager attach to it. 294 } 295 return new BackgroundManager(activity); 296 } 297 298 /** 299 * Construct a BackgroundManager instance. The Initial background is set 300 * from the continuity service. 301 * @deprecated Use getInstance(Activity). 302 */ 303 @Deprecated 304 public BackgroundManager(Activity activity) { 305 mContext = activity; 306 mService = BackgroundContinuityService.getInstance(); 307 mHeightPx = mContext.getResources().getDisplayMetrics().heightPixels; 308 mWidthPx = mContext.getResources().getDisplayMetrics().widthPixels; 309 mHandler = new Handler(); 310 311 TypedArray ta = activity.getTheme().obtainStyledAttributes(new int[] { 312 android.R.attr.windowBackground }); 313 mThemeDrawableResourceId = ta.getResourceId(0, -1); 314 if (mThemeDrawableResourceId < 0) { 315 if (DEBUG) Log.v(TAG, "BackgroundManager no window background resource!"); 316 } 317 ta.recycle(); 318 319 createFragment(activity); 320 } 321 322 private void createFragment(Activity activity) { 323 // Use a fragment to ensure the background manager gets detached properly. 324 BackgroundFragment fragment = (BackgroundFragment) activity.getFragmentManager() 325 .findFragmentByTag(FRAGMENT_TAG); 326 if (fragment == null) { 327 fragment = new BackgroundFragment(); 328 activity.getFragmentManager().beginTransaction().add(fragment, FRAGMENT_TAG).commit(); 329 } else { 330 if (fragment.getBackgroundManager() != null) { 331 throw new IllegalStateException("Created duplicated BackgroundManager for same " + 332 "activity, please use getInstance() instead"); 333 } 334 } 335 fragment.setBackgroundManager(this); 336 } 337 338 /** 339 * Synchronizes state when the owning Activity is resumed. 340 */ 341 void onActivityResume() { 342 if (mService == null) { 343 return; 344 } 345 if (mLayerDrawable == null) { 346 if (DEBUG) Log.v(TAG, "onActivityResume: released state, syncing with service"); 347 syncWithService(); 348 } else { 349 if (DEBUG) Log.v(TAG, "onActivityResume: updating service color " 350 + mBackgroundColor + " drawable " + mBackgroundDrawable); 351 mService.setColor(mBackgroundColor); 352 mService.setDrawable(mBackgroundDrawable); 353 } 354 } 355 356 private void syncWithService() { 357 int color = mService.getColor(); 358 Drawable drawable = mService.getDrawable(); 359 360 if (DEBUG) Log.v(TAG, "syncWithService color " + Integer.toHexString(color) 361 + " drawable " + drawable); 362 363 mBackgroundColor = color; 364 mBackgroundDrawable = drawable; 365 366 updateImmediate(); 367 } 368 369 private void lazyInit() { 370 if (mLayerDrawable != null) { 371 return; 372 } 373 374 mLayerDrawable = (LayerDrawable) mContext.getResources().getDrawable( 375 R.drawable.lb_background).mutate(); 376 mBgView.setBackground(mLayerDrawable); 377 378 mLayerDrawable.setDrawableByLayerId(R.id.background_imageout, createEmptyDrawable()); 379 380 mDimWrapper = new DrawableWrapper( 381 mLayerDrawable.findDrawableByLayerId(R.id.background_dim)); 382 383 mLayerWrapper = new DrawableWrapper(mLayerDrawable); 384 385 mColorWrapper = new DrawableWrapper( 386 mLayerDrawable.findDrawableByLayerId(R.id.background_color)); 387 } 388 389 /** 390 * Make the background visible on the given Window. 391 */ 392 public void attach(Window window) { 393 if (USE_SEPARATE_WINDOW) { 394 attachBehindWindow(window); 395 } else { 396 attachToView(window.getDecorView()); 397 } 398 } 399 400 private void attachBehindWindow(Window window) { 401 if (DEBUG) Log.v(TAG, "attachBehindWindow " + window); 402 mWindow = window; 403 mWindowManager = window.getWindowManager(); 404 405 WindowManager.LayoutParams params = new WindowManager.LayoutParams( 406 // Media window sits behind the main application window 407 WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA, 408 // Avoid default to software format RGBA 409 WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED, 410 android.graphics.PixelFormat.TRANSLUCENT); 411 params.setTitle(WINDOW_NAME); 412 params.width = ViewGroup.LayoutParams.MATCH_PARENT; 413 params.height = ViewGroup.LayoutParams.MATCH_PARENT; 414 415 View backgroundView = LayoutInflater.from(mContext).inflate( 416 R.layout.lb_background_window, null); 417 mWindowManager.addView(backgroundView, params); 418 419 attachToView(backgroundView); 420 } 421 422 private void attachToView(View sceneRoot) { 423 mBgView = sceneRoot; 424 mAttached = true; 425 syncWithService(); 426 } 427 428 /** 429 * Release references to Drawables and put the BackgroundManager into the 430 * detached state. Called when the associated Activity is destroyed. 431 * @hide 432 */ 433 void detach() { 434 if (DEBUG) Log.v(TAG, "detach"); 435 release(); 436 437 if (mWindowManager != null && mBgView != null) { 438 mWindowManager.removeViewImmediate(mBgView); 439 } 440 441 mWindowManager = null; 442 mWindow = null; 443 mBgView = null; 444 mAttached = false; 445 446 if (mService != null) { 447 mService.unref(); 448 mService = null; 449 } 450 } 451 452 /** 453 * Release references to Drawables. Typically called to reduce memory 454 * overhead when not visible. 455 * <p> 456 * When an Activity is resumed, if the BackgroundManager has not been 457 * released, the continuity service is updated from the BackgroundManager 458 * state. If the BackgroundManager was released, the BackgroundManager 459 * inherits the current state from the continuity service. 460 */ 461 public void release() { 462 if (DEBUG) Log.v(TAG, "release"); 463 if (mLayerDrawable != null) { 464 mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, createEmptyDrawable()); 465 mLayerDrawable.setDrawableByLayerId(R.id.background_imageout, createEmptyDrawable()); 466 mLayerDrawable = null; 467 } 468 mLayerWrapper = null; 469 mImageInWrapper = null; 470 mImageOutWrapper = null; 471 mColorWrapper = null; 472 mDimWrapper = null; 473 mThemeDrawable = null; 474 if (mChangeRunnable != null) { 475 mChangeRunnable.cancel(); 476 mChangeRunnable = null; 477 } 478 releaseBackgroundBitmap(); 479 } 480 481 private void releaseBackgroundBitmap() { 482 mBackgroundDrawable = null; 483 } 484 485 private void updateImmediate() { 486 lazyInit(); 487 488 mColorWrapper.setColor(mBackgroundColor); 489 if (mDimWrapper != null) { 490 mDimWrapper.setAlpha(mBackgroundColor == Color.TRANSPARENT ? 0 : DIM_ALPHA_ON_SOLID); 491 } 492 showWallpaper(mBackgroundColor == Color.TRANSPARENT); 493 494 mThemeDrawable = getThemeDrawable(); 495 mLayerDrawable.setDrawableByLayerId(R.id.background_theme, mThemeDrawable); 496 497 if (mBackgroundDrawable == null) { 498 mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, createEmptyDrawable()); 499 } else { 500 if (DEBUG) Log.v(TAG, "Background drawable is available"); 501 mImageInWrapper = new DrawableWrapper(mBackgroundDrawable); 502 mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, mBackgroundDrawable); 503 if (mDimWrapper != null) { 504 mDimWrapper.setAlpha(FULL_ALPHA); 505 } 506 } 507 } 508 509 /** 510 * Set the background to the given color. The timing for when this becomes 511 * visible in the app is undefined and may take place after a small delay. 512 */ 513 public void setColor(int color) { 514 if (DEBUG) Log.v(TAG, "setColor " + Integer.toHexString(color)); 515 516 mBackgroundColor = color; 517 mService.setColor(mBackgroundColor); 518 519 if (mColorWrapper != null) { 520 mColorWrapper.setColor(mBackgroundColor); 521 } 522 } 523 524 /** 525 * Set the given drawable into the background. The provided Drawable will be 526 * used unmodified as the background, without any scaling or cropping 527 * applied to it. The timing for when this becomes visible in the app is 528 * undefined and may take place after a small delay. 529 */ 530 public void setDrawable(Drawable drawable) { 531 if (DEBUG) Log.v(TAG, "setBackgroundDrawable " + drawable); 532 setDrawableInternal(drawable); 533 } 534 535 private void setDrawableInternal(Drawable drawable) { 536 if (!mAttached) { 537 throw new IllegalStateException("Must attach before setting background drawable"); 538 } 539 540 if (mChangeRunnable != null) { 541 mChangeRunnable.cancel(); 542 } 543 mChangeRunnable = new ChangeBackgroundRunnable(drawable); 544 545 mHandler.postDelayed(mChangeRunnable, CHANGE_BG_DELAY_MS); 546 } 547 548 /** 549 * Set the given bitmap into the background. When using setBitmap to set the 550 * background, the provided bitmap will be scaled and cropped to correctly 551 * fit within the dimensions of the view. The timing for when this becomes 552 * visible in the app is undefined and may take place after a small delay. 553 */ 554 public void setBitmap(Bitmap bitmap) { 555 if (DEBUG) { 556 Log.v(TAG, "setBitmap " + bitmap); 557 } 558 559 if (bitmap == null) { 560 setDrawableInternal(null); 561 return; 562 } 563 564 if (bitmap.getWidth() <= 0 || bitmap.getHeight() <= 0) { 565 if (DEBUG) { 566 Log.v(TAG, "invalid bitmap width or height"); 567 } 568 return; 569 } 570 571 Matrix matrix = null; 572 573 if ((bitmap.getWidth() != mWidthPx || bitmap.getHeight() != mHeightPx)) { 574 int dwidth = bitmap.getWidth(); 575 int dheight = bitmap.getHeight(); 576 float scale; 577 578 // Scale proportionately to fit width and height. 579 if (dwidth * mHeightPx > mWidthPx * dheight) { 580 scale = (float) mHeightPx / (float) dheight; 581 } else { 582 scale = (float) mWidthPx / (float) dwidth; 583 } 584 585 int subX = Math.min((int) (mWidthPx / scale), dwidth); 586 int dx = Math.max(0, (dwidth - subX) / 2); 587 588 matrix = new Matrix(); 589 matrix.setScale(scale, scale); 590 matrix.preTranslate(-dx, 0); 591 592 if (DEBUG) Log.v(TAG, "original image size " + bitmap.getWidth() + "x" + bitmap.getHeight() + 593 " scale " + scale + " dx " + dx); 594 } 595 596 BitmapDrawable bitmapDrawable = new BitmapDrawable(mContext.getResources(), bitmap, matrix); 597 598 setDrawableInternal(bitmapDrawable); 599 } 600 601 private void applyBackgroundChanges() { 602 if (!mAttached || mLayerWrapper == null) { 603 return; 604 } 605 606 if (DEBUG) Log.v(TAG, "applyBackgroundChanges drawable " + mBackgroundDrawable); 607 608 int dimAlpha = 0; 609 610 if (mImageOutWrapper != null && mImageOutWrapper.isAnimationPending()) { 611 if (DEBUG) Log.v(TAG, "mImageOutWrapper animation starting"); 612 mImageOutWrapper.startAnimation(); 613 mImageOutWrapper = null; 614 dimAlpha = DIM_ALPHA_ON_SOLID; 615 } 616 617 if (mImageInWrapper == null && mBackgroundDrawable != null) { 618 if (DEBUG) Log.v(TAG, "creating new imagein drawable"); 619 mImageInWrapper = new DrawableWrapper(mBackgroundDrawable); 620 mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, mBackgroundDrawable); 621 if (DEBUG) Log.v(TAG, "mImageInWrapper animation starting"); 622 mImageInWrapper.setAlpha(0); 623 mImageInWrapper.fadeIn(FADE_DURATION_SLOW, 0); 624 mImageInWrapper.startAnimation(); 625 dimAlpha = FULL_ALPHA; 626 } 627 628 if (mDimWrapper != null && dimAlpha != 0) { 629 if (DEBUG) Log.v(TAG, "dimwrapper animation starting to " + dimAlpha); 630 mDimWrapper.fade(FADE_DURATION_SLOW, 0, dimAlpha); 631 mDimWrapper.startAnimation(); 632 } 633 } 634 635 /** 636 * Returns the current background color. 637 */ 638 public final int getColor() { 639 return mBackgroundColor; 640 } 641 642 /** 643 * Returns the current background {@link Drawable}. 644 */ 645 public Drawable getDrawable() { 646 return mBackgroundDrawable; 647 } 648 649 private boolean sameDrawable(Drawable first, Drawable second) { 650 if (first == null || second == null) { 651 return false; 652 } 653 if (first == second) { 654 return true; 655 } 656 if (first instanceof BitmapDrawable && second instanceof BitmapDrawable) { 657 if (((BitmapDrawable) first).getBitmap().sameAs(((BitmapDrawable) second).getBitmap())) { 658 return true; 659 } 660 } 661 return false; 662 } 663 664 /** 665 * Task which changes the background. 666 */ 667 class ChangeBackgroundRunnable implements Runnable { 668 private Drawable mDrawable; 669 private boolean mCancel; 670 671 ChangeBackgroundRunnable(Drawable drawable) { 672 mDrawable = drawable; 673 } 674 675 public void cancel() { 676 mCancel = true; 677 } 678 679 @Override 680 public void run() { 681 if (!mCancel) { 682 runTask(); 683 } 684 } 685 686 private void runTask() { 687 lazyInit(); 688 689 if (sameDrawable(mDrawable, mBackgroundDrawable)) { 690 if (DEBUG) Log.v(TAG, "same bitmap detected"); 691 return; 692 } 693 694 releaseBackgroundBitmap(); 695 696 if (mImageInWrapper != null) { 697 mImageOutWrapper = new DrawableWrapper(mImageInWrapper.getDrawable()); 698 mImageOutWrapper.setAlpha(mImageInWrapper.getAlpha()); 699 mImageOutWrapper.fadeOut(FADE_DURATION_QUICK); 700 701 // Order is important! Setting a drawable "removes" the 702 // previous one from the view 703 mLayerDrawable.setDrawableByLayerId(R.id.background_imagein, createEmptyDrawable()); 704 mLayerDrawable.setDrawableByLayerId(R.id.background_imageout, 705 mImageOutWrapper.getDrawable()); 706 mImageInWrapper.setAlpha(0); 707 mImageInWrapper = null; 708 } 709 710 mBackgroundDrawable = mDrawable; 711 mService.setDrawable(mBackgroundDrawable); 712 713 applyBackgroundChanges(); 714 } 715 } 716 717 private Drawable createEmptyDrawable() { 718 Bitmap bitmap = null; 719 return new BitmapDrawable(mContext.getResources(), bitmap); 720 } 721 722 private void showWallpaper(boolean show) { 723 if (mWindow == null) { 724 return; 725 } 726 727 WindowManager.LayoutParams layoutParams = mWindow.getAttributes(); 728 if (show) { 729 if ((layoutParams.flags & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) != 0) { 730 return; 731 } 732 if (DEBUG) Log.v(TAG, "showing wallpaper"); 733 layoutParams.flags |= WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 734 } else { 735 if ((layoutParams.flags & WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER) == 0) { 736 return; 737 } 738 if (DEBUG) Log.v(TAG, "hiding wallpaper"); 739 layoutParams.flags &= ~WindowManager.LayoutParams.FLAG_SHOW_WALLPAPER; 740 } 741 742 mWindow.setAttributes(layoutParams); 743 } 744} 745