private Bitmap generateShortcutPreview(
      ResolveInfo info, int maxWidth, int maxHeight, Bitmap preview) {
    Bitmap tempBitmap = mCachedShortcutPreviewBitmap.get();
    final Canvas c = mCachedShortcutPreviewCanvas.get();
    if (tempBitmap == null
        || tempBitmap.getWidth() != maxWidth
        || tempBitmap.getHeight() != maxHeight) {
      tempBitmap = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888);
      mCachedShortcutPreviewBitmap.set(tempBitmap);
    } else {
      c.setBitmap(tempBitmap);
      c.drawColor(0, PorterDuff.Mode.CLEAR);
      c.setBitmap(null);
    }
    // Render the icon
    Drawable icon = mIconCache.getFullResIcon(info);

    int paddingTop =
        mContext.getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top);
    int paddingLeft =
        mContext.getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_left);
    int paddingRight =
        mContext.getResources().getDimensionPixelOffset(R.dimen.shortcut_preview_padding_right);

    int scaledIconWidth = (maxWidth - paddingLeft - paddingRight);

    renderDrawableToBitmap(
        icon, tempBitmap, paddingLeft, paddingTop, scaledIconWidth, scaledIconWidth);

    if (preview != null && (preview.getWidth() != maxWidth || preview.getHeight() != maxHeight)) {
      throw new RuntimeException("Improperly sized bitmap passed as argument");
    } else if (preview == null) {
      preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888);
    }

    c.setBitmap(preview);
    // Draw a desaturated/scaled version of the icon in the background as a watermark
    Paint p = mCachedShortcutPreviewPaint.get();
    if (p == null) {
      p = new Paint();
      ColorMatrix colorMatrix = new ColorMatrix();
      colorMatrix.setSaturation(0);
      p.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
      p.setAlpha((int) (255 * 0.06f));
      mCachedShortcutPreviewPaint.set(p);
    }
    c.drawBitmap(tempBitmap, 0, 0, p);
    c.setBitmap(null);

    renderDrawableToBitmap(icon, preview, 0, 0, mAppIconSize, mAppIconSize);

    return preview;
  }
  public Bitmap generateWidgetPreview(
      ComponentName provider,
      int previewImage,
      int iconId,
      int cellHSpan,
      int cellVSpan,
      int maxPreviewWidth,
      int maxPreviewHeight,
      Bitmap preview,
      int[] preScaledWidthOut) {
    // Load the preview image if possible
    String packageName = provider.getPackageName();
    if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
    if (maxPreviewHeight < 0) maxPreviewHeight = Integer.MAX_VALUE;

    Drawable drawable = null;
    if (previewImage != 0) {
      drawable = mPackageManager.getDrawable(packageName, previewImage, null);
      if (drawable == null) {
        Log.w(
            TAG,
            "Can't load widget preview drawable 0x"
                + Integer.toHexString(previewImage)
                + " for provider: "
                + provider);
      }
    }

    int previewWidth;
    int previewHeight;
    Bitmap defaultPreview = null;
    boolean widgetPreviewExists = (drawable != null);
    if (widgetPreviewExists) {
      previewWidth = drawable.getIntrinsicWidth();
      previewHeight = drawable.getIntrinsicHeight();
    } else {
      // Generate a preview image if we couldn't load one
      if (cellHSpan < 1) cellHSpan = 1;
      if (cellVSpan < 1) cellVSpan = 1;

      BitmapDrawable previewDrawable =
          (BitmapDrawable) mContext.getResources().getDrawable(R.drawable.widget_tile);
      final int previewDrawableWidth = previewDrawable.getIntrinsicWidth();
      final int previewDrawableHeight = previewDrawable.getIntrinsicHeight();
      previewWidth = previewDrawableWidth * cellHSpan;
      previewHeight = previewDrawableHeight * cellVSpan;

      defaultPreview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
      final Canvas c = mCachedAppWidgetPreviewCanvas.get();
      c.setBitmap(defaultPreview);
      previewDrawable.setBounds(0, 0, previewWidth, previewHeight);
      previewDrawable.setTileModeXY(Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
      previewDrawable.draw(c);
      c.setBitmap(null);

      // Draw the icon in the top left corner
      int minOffset = (int) (mAppIconSize * sWidgetPreviewIconPaddingPercentage);
      int smallestSide = Math.min(previewWidth, previewHeight);
      float iconScale = Math.min((float) smallestSide / (mAppIconSize + 2 * minOffset), 1f);

      try {
        Drawable icon = null;
        int hoffset = (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2);
        int yoffset = (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2);
        if (iconId > 0) icon = mIconCache.getFullResIcon(packageName, iconId);
        if (icon != null) {
          renderDrawableToBitmap(
              icon,
              defaultPreview,
              hoffset,
              yoffset,
              (int) (mAppIconSize * iconScale),
              (int) (mAppIconSize * iconScale));
        }
      } catch (Resources.NotFoundException e) {
      }
    }

    // Scale to fit width only - let the widget preview be clipped in the
    // vertical dimension
    float scale = 1f;
    if (preScaledWidthOut != null) {
      preScaledWidthOut[0] = previewWidth;
    }
    if (previewWidth > maxPreviewWidth) {
      scale = maxPreviewWidth / (float) previewWidth;
    }
    if (scale != 1f) {
      previewWidth = (int) (scale * previewWidth);
      previewHeight = (int) (scale * previewHeight);
    }

    // If a bitmap is passed in, we use it; otherwise, we create a bitmap of the right size
    if (preview == null) {
      preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
    }

    // Draw the scaled preview into the final bitmap
    int x = (preview.getWidth() - previewWidth) / 2;
    if (widgetPreviewExists) {
      renderDrawableToBitmap(drawable, preview, x, 0, previewWidth, previewHeight);
    } else {
      final Canvas c = mCachedAppWidgetPreviewCanvas.get();
      final Rect src = mCachedAppWidgetPreviewSrcRect.get();
      final Rect dest = mCachedAppWidgetPreviewDestRect.get();
      c.setBitmap(preview);
      src.set(0, 0, defaultPreview.getWidth(), defaultPreview.getHeight());
      dest.set(x, 0, x + previewWidth, previewHeight);

      Paint p = mCachedAppWidgetPreviewPaint.get();
      if (p == null) {
        p = new Paint();
        p.setFilterBitmap(true);
        mCachedAppWidgetPreviewPaint.set(p);
      }
      c.drawBitmap(defaultPreview, src, dest, p);
      c.setBitmap(null);
    }
    return preview;
  }
  public Bitmap getPreview(final Object o) {
    final String name = getObjectName(o);
    final String packageName = getObjectPackage(o);
    // check if the package is valid
    boolean packageValid = true;
    synchronized (sInvalidPackages) {
      packageValid = !sInvalidPackages.contains(packageName);
    }
    if (!packageValid) {
      return null;
    }
    if (packageValid) {
      synchronized (mLoadedPreviews) {
        // check if it exists in our existing cache
        if (mLoadedPreviews.containsKey(name) && mLoadedPreviews.get(name).get() != null) {
          return mLoadedPreviews.get(name).get();
        }
      }
    }

    Bitmap unusedBitmap = null;
    synchronized (mUnusedBitmaps) {
      // not in cache; we need to load it from the db
      while ((unusedBitmap == null
              || !unusedBitmap.isMutable()
              || unusedBitmap.getWidth() != mPreviewBitmapWidth
              || unusedBitmap.getHeight() != mPreviewBitmapHeight)
          && mUnusedBitmaps.size() > 0) {
        unusedBitmap = mUnusedBitmaps.remove(0).get();
      }
      if (unusedBitmap != null) {
        final Canvas c = mCachedAppWidgetPreviewCanvas.get();
        c.setBitmap(unusedBitmap);
        c.drawColor(0, PorterDuff.Mode.CLEAR);
        c.setBitmap(null);
      }
    }

    if (unusedBitmap == null) {
      unusedBitmap =
          Bitmap.createBitmap(mPreviewBitmapWidth, mPreviewBitmapHeight, Bitmap.Config.ARGB_8888);
    }

    Bitmap preview = null;

    if (packageValid) {
      preview = readFromDb(name, unusedBitmap);
    }

    if (preview != null) {
      synchronized (mLoadedPreviews) {
        mLoadedPreviews.put(name, new WeakReference<Bitmap>(preview));
      }
      return preview;
    } else {
      // it's not in the db... we need to generate it
      final Bitmap generatedPreview = generatePreview(o, unusedBitmap);
      preview = generatedPreview;
      if (preview != unusedBitmap) {
        throw new RuntimeException("generatePreview is not recycling the bitmap " + o);
      }

      synchronized (mLoadedPreviews) {
        mLoadedPreviews.put(name, new WeakReference<Bitmap>(preview));
      }

      // write to db on a thread pool... this can be done lazily and improves the performance
      // of the first time widget previews are loaded
      new AsyncTask<Void, Void, Void>() {
        public Void doInBackground(Void... args) {
          writeToDb(o, generatedPreview);
          return null;
        }
      }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR, (Void) null);

      return preview;
    }
  }