Merge "Cleaning up widget preview loader" into ub-launcher3-burnaby

This commit is contained in:
Sunny Goyal
2015-03-19 21:05:16 +00:00
committed by Android (Google) Code Review
4 changed files with 104 additions and 261 deletions
@@ -38,7 +38,6 @@ import android.util.Log;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.GridLayout;
import android.widget.ImageView;
import android.widget.Toast;
@@ -48,9 +47,7 @@ import com.android.launcher3.FocusHelper.PagedViewKeyListener;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
/**
* A simple callback interface which also provides the results of the task.
@@ -171,7 +168,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// Dimens
private int mContentWidth, mContentHeight;
private int mWidgetCountX, mWidgetCountY;
private PagedViewCellLayout mWidgetSpacingLayout;
private int mNumWidgetPages;
// Previews & outlines
@@ -219,7 +215,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
mWidgetCountX = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountX, 2);
mWidgetCountY = a.getInt(R.styleable.AppsCustomizePagedView_widgetCountY, 2);
a.recycle();
mWidgetSpacingLayout = new PagedViewCellLayout(getContext());
// The padding on the non-matched dimension for the default widget preview icons
// (top + bottom)
@@ -316,9 +311,6 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
// Force a measure to update recalculate the gaps
mContentWidth = getMeasuredWidth() - getPaddingLeft() - getPaddingRight();
mContentHeight = getMeasuredHeight() - getPaddingTop() - getPaddingBottom();
int widthSpec = MeasureSpec.makeMeasureSpec(mContentWidth, MeasureSpec.AT_MOST);
int heightSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.AT_MOST);
mWidgetSpacingLayout.measure(widthSpec, heightSpec);
final boolean hostIsTransitioning = getTabHost().isInTransition();
int page = getPageForComponent(mSaveInstanceStateItemIndex);
@@ -596,13 +588,11 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
FastBitmapDrawable previewDrawable = (FastBitmapDrawable) image.getDrawable();
float minScale = 1.25f;
int maxWidth, maxHeight;
maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
maxHeight = Math.min((int) (previewDrawable.getIntrinsicHeight() * minScale), size[1]);
int maxWidth = Math.min((int) (previewDrawable.getIntrinsicWidth() * minScale), size[0]);
int[] previewSizeBeforeScale = new int[1];
preview = getWidgetPreviewLoader().generateWidgetPreview(createWidgetInfo.info,
spanX, spanY, maxWidth, maxHeight, null, previewSizeBeforeScale);
maxWidth, null, previewSizeBeforeScale);
// Compare the size of the drag preview to the preview in the AppsCustomize tray
int previewWidthInAppsCustomize = Math.min(previewSizeBeforeScale[0],
@@ -1075,8 +1065,7 @@ public class AppsCustomizePagedView extends PagedViewWithDraggableItems implemen
maxPreviewHeight = maxSize[1];
}
getWidgetPreviewLoader().setPreviewSize(
maxPreviewWidth, maxPreviewHeight, mWidgetSpacingLayout);
getWidgetPreviewLoader().setPreviewSize(maxPreviewWidth, maxPreviewHeight);
if (immediate) {
AsyncTaskPageData data = new AsyncTaskPageData(page, items,
maxPreviewWidth, maxPreviewHeight, null, null, getWidgetPreviewLoader());
@@ -631,7 +631,6 @@ public class LauncherBackupHelper implements BackupHelper {
}
final ContentResolver cr = mContext.getContentResolver();
final WidgetPreviewLoader previewLoader = new WidgetPreviewLoader(mContext);
final PagedViewCellLayout widgetSpacingLayout = new PagedViewCellLayout(mContext);
final int dpi = mContext.getResources().getDisplayMetrics().densityDpi;
final DeviceProfile profile = appState.getDynamicGrid().getDeviceProfile();
if (DEBUG) Log.d(TAG, "cellWidthPx: " + profile.cellWidthPx);
@@ -666,8 +665,9 @@ public class LauncherBackupHelper implements BackupHelper {
if (DEBUG) Log.d(TAG, "I can count this high: " + backupWidgetCount);
if (backupWidgetCount < MAX_WIDGETS_PER_PASS) {
if (DEBUG) Log.d(TAG, "saving widget " + backupKey);
previewLoader.setPreviewSize(spanX * profile.cellWidthPx,
spanY * profile.cellHeightPx, widgetSpacingLayout);
previewLoader.setPreviewSize(
spanX * profile.cellWidthPx,
spanY * profile.cellHeightPx);
writeRowToBackup(key, packWidget(dpi, previewLoader, mIconCache, provider), data);
mKeys.add(key);
backupWidgetCount ++;
@@ -348,22 +348,6 @@ public class PagedViewCellLayout extends ViewGroup implements Page {
requestLayout();
}
/**
* Estimates the width that the number of hSpan cells will take up.
*/
public int estimateCellWidth(int hSpan) {
// TODO: we need to take widthGap into effect
return hSpan * mCellWidth;
}
/**
* Estimates the height that the number of vSpan cells will take up.
*/
public int estimateCellHeight(int vSpan) {
// TODO: we need to take heightGap into effect
return vSpan * mCellHeight;
}
@Override
public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) {
return new PagedViewCellLayout.LayoutParams(getContext(), attrs);
@@ -16,19 +16,19 @@ import android.database.sqlite.SQLiteReadOnlyDatabaseException;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.Shader;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.AsyncTask;
import android.os.Build;
import android.util.Log;
import com.android.launcher3.compat.AppWidgetManagerCompat;
import java.io.ByteArrayOutputStream;
@@ -46,107 +46,27 @@ import java.util.concurrent.ExecutionException;
public class WidgetPreviewLoader {
private static abstract class SoftReferenceThreadLocal<T> {
private ThreadLocal<SoftReference<T>> mThreadLocal;
public SoftReferenceThreadLocal() {
mThreadLocal = new ThreadLocal<SoftReference<T>>();
}
abstract T initialValue();
public void set(T t) {
mThreadLocal.set(new SoftReference<T>(t));
}
public T get() {
SoftReference<T> reference = mThreadLocal.get();
T obj;
if (reference == null) {
obj = initialValue();
mThreadLocal.set(new SoftReference<T>(obj));
return obj;
} else {
obj = reference.get();
if (obj == null) {
obj = initialValue();
mThreadLocal.set(new SoftReference<T>(obj));
}
return obj;
}
}
}
private static class CanvasCache extends SoftReferenceThreadLocal<Canvas> {
@Override
protected Canvas initialValue() {
return new Canvas();
}
}
private static class PaintCache extends SoftReferenceThreadLocal<Paint> {
@Override
protected Paint initialValue() {
return null;
}
}
private static class BitmapCache extends SoftReferenceThreadLocal<Bitmap> {
@Override
protected Bitmap initialValue() {
return null;
}
}
private static class RectCache extends SoftReferenceThreadLocal<Rect> {
@Override
protected Rect initialValue() {
return new Rect();
}
}
private static class BitmapFactoryOptionsCache extends
SoftReferenceThreadLocal<BitmapFactory.Options> {
@Override
protected BitmapFactory.Options initialValue() {
return new BitmapFactory.Options();
}
}
private static final String TAG = "WidgetPreviewLoader";
private static final String ANDROID_INCREMENTAL_VERSION_NAME_KEY = "android.incremental.version";
private static final float WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE = 0.25f;
private static final HashSet<String> sInvalidPackages = new HashSet<String>();
// Used for drawing shortcut previews
private final BitmapCache mCachedShortcutPreviewBitmap = new BitmapCache();
private final PaintCache mCachedShortcutPreviewPaint = new PaintCache();
private final CanvasCache mCachedShortcutPreviewCanvas = new CanvasCache();
// Used for drawing widget previews
private final CanvasCache mCachedAppWidgetPreviewCanvas = new CanvasCache();
private final RectCache mCachedAppWidgetPreviewSrcRect = new RectCache();
private final RectCache mCachedAppWidgetPreviewDestRect = new RectCache();
private final PaintCache mCachedAppWidgetPreviewPaint = new PaintCache();
private final PaintCache mDefaultAppWidgetPreviewPaint = new PaintCache();
private final BitmapFactoryOptionsCache mCachedBitmapFactoryOptions = new BitmapFactoryOptionsCache();
private final HashMap<String, WeakReference<Bitmap>> mLoadedPreviews = new HashMap<>();
private final ArrayList<SoftReference<Bitmap>> mUnusedBitmaps = new ArrayList<>();
private final Context mContext;
private final int mAppIconSize;
private final int mCellWidth;
private final Context mContext;
private final IconCache mIconCache;
private final AppWidgetManagerCompat mManager;
private int mPreviewBitmapWidth;
private int mPreviewBitmapHeight;
private String mSize;
private PagedViewCellLayout mWidgetSpacingLayout;
private String mCachedSelectQuery;
private CacheDb mDb;
private final MainThreadExecutor mMainThreadExecutor = new MainThreadExecutor();
@@ -157,9 +77,10 @@ public class WidgetPreviewLoader {
mContext = context;
mAppIconSize = grid.iconSizePx;
mCellWidth = grid.cellWidthPx;
mIconCache = app.getIconCache();
mManager = AppWidgetManagerCompat.getInstance(context);
mDb = app.getWidgetPreviewCacheDb();
SharedPreferences sp = context.getSharedPreferences(
@@ -193,12 +114,10 @@ public class WidgetPreviewLoader {
mDb = app.getWidgetPreviewCacheDb();
}
public void setPreviewSize(int previewWidth, int previewHeight,
PagedViewCellLayout widgetSpacingLayout) {
public void setPreviewSize(int previewWidth, int previewHeight) {
mPreviewBitmapWidth = previewWidth;
mPreviewBitmapHeight = previewHeight;
mSize = previewWidth + "x" + previewHeight;
mWidgetSpacingLayout = widgetSpacingLayout;
}
public Bitmap getPreview(final Object o) {
@@ -233,12 +152,6 @@ public class WidgetPreviewLoader {
unusedBitmap = candidate;
}
}
if (unusedBitmap != null) {
final Canvas c = mCachedAppWidgetPreviewCanvas.get();
c.setBitmap(unusedBitmap);
c.drawColor(0, PorterDuff.Mode.CLEAR);
c.setBitmap(null);
}
}
if (unusedBitmap == null) {
@@ -468,7 +381,7 @@ public class WidgetPreviewLoader {
result.moveToFirst();
byte[] blob = result.getBlob(0);
result.close();
final BitmapFactory.Options opts = mCachedBitmapFactoryOptions.get();
final BitmapFactory.Options opts = new BitmapFactory.Options();
opts.inBitmap = b;
opts.inSampleSize = 1;
try {
@@ -499,26 +412,17 @@ public class WidgetPreviewLoader {
public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, Bitmap preview) {
int maxWidth = maxWidthForWidgetPreview(info.spanX);
int maxHeight = maxHeightForWidgetPreview(info.spanY);
return generateWidgetPreview(info, info.spanX, info.spanY, maxWidth,
maxHeight, preview, null);
return generateWidgetPreview(info, maxWidth, preview, null);
}
public int maxWidthForWidgetPreview(int spanX) {
return Math.min(mPreviewBitmapWidth,
mWidgetSpacingLayout.estimateCellWidth(spanX));
return Math.min(mPreviewBitmapWidth, spanX * mCellWidth);
}
public int maxHeightForWidgetPreview(int spanY) {
return Math.min(mPreviewBitmapHeight,
mWidgetSpacingLayout.estimateCellHeight(spanY));
}
public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info, int cellHSpan, int cellVSpan,
int maxPreviewWidth, int maxPreviewHeight, Bitmap preview, int[] preScaledWidthOut) {
public Bitmap generateWidgetPreview(LauncherAppWidgetProviderInfo info,
int maxPreviewWidth, Bitmap preview, int[] preScaledWidthOut) {
// Load the preview image if possible
if (maxPreviewWidth < 0) maxPreviewWidth = Integer.MAX_VALUE;
if (maxPreviewHeight < 0) maxPreviewHeight = Integer.MAX_VALUE;
Drawable drawable = null;
if (info.previewImage != 0) {
@@ -531,61 +435,23 @@ public class WidgetPreviewLoader {
}
}
final boolean widgetPreviewExists = (drawable != null);
final int spanX = info.spanX < 1 ? 1 : info.spanX;
final int spanY = info.spanY < 1 ? 1 : info.spanY;
int previewWidth;
int previewHeight;
Bitmap defaultPreview = null;
boolean widgetPreviewExists = (drawable != null);
Bitmap tileBitmap = 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;
// This Drawable is not directly drawn, so there's no need to mutate it.
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);
Paint p = mDefaultAppWidgetPreviewPaint.get();
if (p == null) {
p = new Paint();
p.setShader(new BitmapShader(previewDrawable.getBitmap(),
Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
mDefaultAppWidgetPreviewPaint.set(p);
}
final Rect dest = mCachedAppWidgetPreviewDestRect.get();
dest.set(0, 0, previewWidth, previewHeight);
c.drawRect(dest, p);
c.setBitmap(null);
// Draw the icon in the top left corner
int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE);
int smallestSide = Math.min(previewWidth, previewHeight);
float iconScale = Math.min((float) smallestSide
/ (mAppIconSize + 2 * minOffset), 1f);
try {
Drawable icon = mManager.loadIcon(info, mIconCache);
if (icon != null) {
int hoffset = (int) ((previewDrawableWidth - mAppIconSize * iconScale) / 2);
int yoffset = (int) ((previewDrawableHeight - mAppIconSize * iconScale) / 2);
icon = mutateOnMainThread(icon);
renderDrawableToBitmap(icon, defaultPreview, hoffset,
yoffset, (int) (mAppIconSize * iconScale),
(int) (mAppIconSize * iconScale));
}
} catch (Resources.NotFoundException e) {
}
tileBitmap = ((BitmapDrawable) mContext.getResources().getDrawable(
R.drawable.widget_tile)).getBitmap();
previewWidth = tileBitmap.getWidth() * spanX;
previewHeight = tileBitmap.getHeight() * spanY;
}
// Scale to fit width only - let the widget preview be clipped in the
@@ -603,30 +469,58 @@ public class WidgetPreviewLoader {
}
// If a bitmap is passed in, we use it; otherwise, we create a bitmap of the right size
final Canvas c = new Canvas();
if (preview == null) {
preview = Bitmap.createBitmap(previewWidth, previewHeight, Config.ARGB_8888);
c.setBitmap(preview);
} else {
// Reusing bitmap. Clear it.
c.setBitmap(preview);
c.drawColor(0, PorterDuff.Mode.CLEAR);
}
// Draw the scaled preview into the final bitmap
int x = (preview.getWidth() - previewWidth) / 2;
if (widgetPreviewExists) {
renderDrawableToBitmap(drawable, preview, x, 0, previewWidth,
previewHeight);
drawable.setBounds(x, 0, x + previewWidth, previewHeight);
drawable.draw(c);
} 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);
final Paint p = new Paint();
p.setFilterBitmap(true);
Paint p = mCachedAppWidgetPreviewPaint.get();
if (p == null) {
p = new Paint();
p.setFilterBitmap(true);
mCachedAppWidgetPreviewPaint.set(p);
// draw the spanX x spanY tiles
final Rect src = new Rect(0, 0, tileBitmap.getWidth(), tileBitmap.getHeight());
float tileW = scale * tileBitmap.getWidth();
float tileH = scale * tileBitmap.getHeight();
final RectF dst = new RectF(0, 0, tileW, tileH);
float tx = x;
for (int i = 0; i < spanX; i++, tx += tileW) {
float ty = 0;
for (int j = 0; j < spanY; j++, ty += tileH) {
dst.offsetTo(tx, ty);
c.drawBitmap(tileBitmap, src, dst, p);
}
}
c.drawBitmap(defaultPreview, src, dest, p);
// Draw the icon in the top left corner
// TODO: use top right for RTL
int minOffset = (int) (mAppIconSize * WIDGET_PREVIEW_ICON_PADDING_PERCENTAGE);
int smallestSide = Math.min(previewWidth, previewHeight);
float iconScale = Math.min((float) smallestSide / (mAppIconSize + 2 * minOffset), scale);
try {
Drawable icon = mutateOnMainThread(mManager.loadIcon(info, mIconCache));
if (icon != null) {
int hoffset = (int) ((tileW - mAppIconSize * iconScale) / 2) + x;
int yoffset = (int) ((tileH - mAppIconSize * iconScale) / 2);
icon.setBounds(hoffset, yoffset,
hoffset + (int) (mAppIconSize * iconScale),
yoffset + (int) (mAppIconSize * iconScale));
icon.draw(c);
}
} catch (Resources.NotFoundException e) { }
c.setBitmap(null);
}
return mManager.getBadgeBitmap(info, preview);
@@ -634,71 +528,47 @@ public class WidgetPreviewLoader {
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 = mutateOnMainThread(mIconCache.getFullResIcon(info.activityInfo));
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) {
final Canvas c = new Canvas();
if (preview == null) {
preview = Bitmap.createBitmap(maxWidth, maxHeight, Config.ARGB_8888);
c.setBitmap(preview);
} else if (preview.getWidth() != maxWidth || preview.getHeight() != maxHeight) {
throw new RuntimeException("Improperly sized bitmap passed as argument");
} else {
// Reusing bitmap. Clear it.
c.setBitmap(preview);
c.drawColor(0, PorterDuff.Mode.CLEAR);
}
c.setBitmap(preview);
Drawable icon = mutateOnMainThread(mIconCache.getFullResIcon(info.activityInfo));
icon.setFilterBitmap(true);
// 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);
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(0);
icon.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
icon.setAlpha((int) (255 * 0.06f));
Resources res = mContext.getResources();
int paddingTop = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_top);
int paddingLeft = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_left);
int paddingRight = res.getDimensionPixelOffset(R.dimen.shortcut_preview_padding_right);
int scaledIconWidth = (maxWidth - paddingLeft - paddingRight);
icon.setBounds(paddingLeft, paddingTop,
paddingLeft + scaledIconWidth, paddingTop + scaledIconWidth);
icon.draw(c);
// Draw the final icon at top left corner.
// TODO: use top right for RTL
icon.setAlpha(255);
icon.setColorFilter(null);
icon.setBounds(0, 0, mAppIconSize, mAppIconSize);
icon.draw(c);
c.setBitmap(null);
renderDrawableToBitmap(icon, preview, 0, 0, mAppIconSize, mAppIconSize);
return preview;
}
private static void renderDrawableToBitmap(
Drawable d, Bitmap bitmap, int x, int y, int w, int h) {
if (bitmap != null) {
Canvas c = new Canvas(bitmap);
Rect oldBounds = d.copyBounds();
d.setBounds(x, y, x + w, y + h);
d.draw(c);
d.setBounds(oldBounds); // Restore the bounds
c.setBitmap(null);
}
}
private Drawable mutateOnMainThread(final Drawable drawable) {
try {
return mMainThreadExecutor.submit(new Callable<Drawable>() {