From 10629b077c65b648be6a8603a092322d8a2c2c50 Mon Sep 17 00:00:00 2001 From: Sunny Goyal Date: Thu, 1 Sep 2016 12:50:11 -0700 Subject: Moving some image handling classes to .graphics package Change-Id: Id6d3d0b9c345a503ff2e09f073eb4b6449e21c7e --- src/com/android/launcher3/AutoInstallsLayout.java | 3 +- .../launcher3/BaseRecyclerViewFastScrollPopup.java | 4 +- src/com/android/launcher3/BubbleTextView.java | 5 +- .../launcher3/HolographicOutlineHelper.java | 227 ------------------ src/com/android/launcher3/IconCache.java | 11 +- src/com/android/launcher3/LauncherClings.java | 30 --- src/com/android/launcher3/LauncherModel.java | 9 +- src/com/android/launcher3/LauncherProvider.java | 7 +- src/com/android/launcher3/ShortcutInfo.java | 9 +- src/com/android/launcher3/Utilities.java | 236 ------------------- .../launcher3/graphics/DragPreviewProvider.java | 3 +- .../graphics/HolographicOutlineHelper.java | 229 ++++++++++++++++++ .../android/launcher3/graphics/IconNormalizer.java | 250 ++++++++++++++++++++ .../android/launcher3/graphics/LauncherIcons.java | 258 +++++++++++++++++++++ .../shortcuts/DeepShortcutsContainer.java | 3 +- .../shortcuts/ShortcutDragPreviewProvider.java | 4 +- src/com/android/launcher3/util/CursorIconInfo.java | 5 +- src/com/android/launcher3/util/IconNormalizer.java | 250 -------------------- .../widget/PendingItemPreviewProvider.java | 4 +- .../launcher3/widget/WidgetsContainerView.java | 3 +- 20 files changed, 773 insertions(+), 777 deletions(-) delete mode 100644 src/com/android/launcher3/HolographicOutlineHelper.java delete mode 100644 src/com/android/launcher3/LauncherClings.java create mode 100644 src/com/android/launcher3/graphics/HolographicOutlineHelper.java create mode 100644 src/com/android/launcher3/graphics/IconNormalizer.java create mode 100644 src/com/android/launcher3/graphics/LauncherIcons.java delete mode 100644 src/com/android/launcher3/util/IconNormalizer.java (limited to 'src/com') diff --git a/src/com/android/launcher3/AutoInstallsLayout.java b/src/com/android/launcher3/AutoInstallsLayout.java index d5309b4f0..8b5a8a863 100644 --- a/src/com/android/launcher3/AutoInstallsLayout.java +++ b/src/com/android/launcher3/AutoInstallsLayout.java @@ -38,6 +38,7 @@ import android.util.Patterns; import com.android.launcher3.LauncherProvider.SqlArguments; import com.android.launcher3.LauncherSettings.Favorites; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.util.Thunk; import org.xmlpull.v1.XmlPullParser; @@ -436,7 +437,7 @@ public class AutoInstallsLayout { return -1; } - ItemInfo.writeBitmap(mValues, Utilities.createIconBitmap(icon, mContext)); + ItemInfo.writeBitmap(mValues, LauncherIcons.createIconBitmap(icon, mContext)); mValues.put(Favorites.ICON_PACKAGE, mIconRes.getResourcePackageName(iconId)); mValues.put(Favorites.ICON_RESOURCE, mIconRes.getResourceName(iconId)); diff --git a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java index b9e627775..b9b044db9 100644 --- a/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java +++ b/src/com/android/launcher3/BaseRecyclerViewFastScrollPopup.java @@ -25,6 +25,8 @@ import android.graphics.Paint; import android.graphics.Rect; import android.graphics.drawable.Drawable; +import com.android.launcher3.graphics.HolographicOutlineHelper; + /** * The fast scroller popup that shows the section name the list will jump to. */ @@ -116,7 +118,7 @@ public class BaseRecyclerViewFastScrollPopup { mBgBounds.bottom = mBgBounds.top + bgHeight; // Generate a bitmap for a shadow matching these bounds - mShadow = HolographicOutlineHelper.obtain( + mShadow = HolographicOutlineHelper.getInstance( mRv.getContext()).createMediumDropShadow(mBg, false /* shouldCache */); } else { mShadow = null; diff --git a/src/com/android/launcher3/BubbleTextView.java b/src/com/android/launcher3/BubbleTextView.java index 33b3ad347..5cb2d4f31 100644 --- a/src/com/android/launcher3/BubbleTextView.java +++ b/src/com/android/launcher3/BubbleTextView.java @@ -42,6 +42,7 @@ import android.widget.TextView; import com.android.launcher3.IconCache.IconLoadRequest; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.graphics.HolographicOutlineHelper; import com.android.launcher3.model.PackageItemInfo; import java.text.NumberFormat; @@ -148,7 +149,7 @@ public class BubbleTextView extends TextView mLongPressHelper = new CheckLongPressHelper(this); mStylusEventHelper = new StylusEventHelper(new SimpleOnStylusPressListener(this), this); - mOutlineHelper = HolographicOutlineHelper.obtain(getContext()); + mOutlineHelper = HolographicOutlineHelper.getInstance(getContext()); setAccessibilityDelegate(mLauncher.getAccessibilityDelegate()); } @@ -326,7 +327,7 @@ public class BubbleTextView extends TextView void setStayPressed(boolean stayPressed) { mStayPressed = stayPressed; if (!stayPressed) { - HolographicOutlineHelper.obtain(getContext()).recycleShadowBitmap(mPressedBackground); + HolographicOutlineHelper.getInstance(getContext()).recycleShadowBitmap(mPressedBackground); mPressedBackground = null; } else { if (mPressedBackground == null) { diff --git a/src/com/android/launcher3/HolographicOutlineHelper.java b/src/com/android/launcher3/HolographicOutlineHelper.java deleted file mode 100644 index 9dec7d9e4..000000000 --- a/src/com/android/launcher3/HolographicOutlineHelper.java +++ /dev/null @@ -1,227 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3; - -import android.content.Context; -import android.content.res.Resources; -import android.graphics.Bitmap; -import android.graphics.BlurMaskFilter; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.Paint; -import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.util.SparseArray; - -import com.android.launcher3.config.ProviderConfig; - -import java.nio.ByteBuffer; - -/** - * Utility class to generate shadow and outline effect, which are used for click feedback - * and drag-n-drop respectively. - */ -public class HolographicOutlineHelper { - - private static HolographicOutlineHelper sInstance; - - private final Canvas mCanvas = new Canvas(); - private final Paint mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - private final Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - private final Paint mErasePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); - - private final BlurMaskFilter mMediumOuterBlurMaskFilter; - private final BlurMaskFilter mThinOuterBlurMaskFilter; - private final BlurMaskFilter mMediumInnerBlurMaskFilter; - - private final float mShadowBitmapShift; - private final BlurMaskFilter mShadowBlurMaskFilter; - - // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps - private final SparseArray mBitmapCache = new SparseArray<>(4); - - private HolographicOutlineHelper(Context context) { - Resources res = context.getResources(); - - float mediumBlur = res.getDimension(R.dimen.blur_size_medium_outline); - mMediumOuterBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.OUTER); - mMediumInnerBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.NORMAL); - - mThinOuterBlurMaskFilter = new BlurMaskFilter( - res.getDimension(R.dimen.blur_size_thin_outline), BlurMaskFilter.Blur.OUTER); - - mShadowBitmapShift = res.getDimension(R.dimen.blur_size_click_shadow); - mShadowBlurMaskFilter = new BlurMaskFilter(mShadowBitmapShift, BlurMaskFilter.Blur.NORMAL); - - mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); - } - - public static HolographicOutlineHelper obtain(Context context) { - if (sInstance == null) { - sInstance = new HolographicOutlineHelper(context); - } - return sInstance; - } - - /** - * Applies a more expensive and accurate outline to whatever is currently drawn in a specified - * bitmap. - */ - public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) { - applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, true); - } - - public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, - boolean clipAlpha) { - if (ProviderConfig.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) { - throw new RuntimeException("Outline blue is only supported on alpha bitmaps"); - } - - // We start by removing most of the alpha channel so as to ignore shadows, and - // other types of partial transparency when defining the shape of the object - if (clipAlpha) { - byte[] pixels = new byte[srcDst.getWidth() * srcDst.getHeight()]; - ByteBuffer buffer = ByteBuffer.wrap(pixels); - buffer.rewind(); - srcDst.copyPixelsToBuffer(buffer); - - for (int i = 0; i < pixels.length; i++) { - if ((pixels[i] & 0xFF) < 188) { - pixels[i] = 0; - } - } - - buffer.rewind(); - srcDst.copyPixelsFromBuffer(buffer); - } - - // calculate the outer blur first - mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter); - int[] outerBlurOffset = new int[2]; - Bitmap thickOuterBlur = srcDst.extractAlpha(mBlurPaint, outerBlurOffset); - - mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter); - int[] brightOutlineOffset = new int[2]; - Bitmap brightOutline = srcDst.extractAlpha(mBlurPaint, brightOutlineOffset); - - // calculate the inner blur - srcDstCanvas.setBitmap(srcDst); - srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT); - mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter); - int[] thickInnerBlurOffset = new int[2]; - Bitmap thickInnerBlur = srcDst.extractAlpha(mBlurPaint, thickInnerBlurOffset); - - // mask out the inner blur - srcDstCanvas.setBitmap(thickInnerBlur); - srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0], - -thickInnerBlurOffset[1], mErasePaint); - srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(), - mErasePaint); - srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1], - mErasePaint); - - // draw the inner and outer blur - srcDstCanvas.setBitmap(srcDst); - srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR); - srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1], - mDrawPaint); - srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1], - mDrawPaint); - - // draw the bright outline - srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1], - mDrawPaint); - - // cleanup - srcDstCanvas.setBitmap(null); - brightOutline.recycle(); - thickOuterBlur.recycle(); - thickInnerBlur.recycle(); - } - - Bitmap createMediumDropShadow(BubbleTextView view) { - return createMediumDropShadow(view.getIcon(), view.getScaleX(), view.getScaleY(), true); - } - - Bitmap createMediumDropShadow(Drawable drawable, boolean shouldCache) { - return createMediumDropShadow(drawable, 1f, 1f, shouldCache); - } - - Bitmap createMediumDropShadow(Drawable drawable, float scaleX, float scaleY, - boolean shouldCache) { - if (drawable == null) { - return null; - } - Rect rect = drawable.getBounds(); - - int bitmapWidth = (int) (rect.width() * scaleX); - int bitmapHeight = (int) (rect.height() * scaleY); - if (bitmapHeight <= 0 || bitmapWidth <= 0) { - return null; - } - - int key = (bitmapWidth << 16) | bitmapHeight; - Bitmap cache = shouldCache ? mBitmapCache.get(key) : null; - if (cache == null) { - cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ALPHA_8); - mCanvas.setBitmap(cache); - - if (shouldCache) { - mBitmapCache.put(key, cache); - } - } else { - mCanvas.setBitmap(cache); - mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); - } - - int saveCount = mCanvas.save(); - mCanvas.scale(scaleX, scaleY); - mCanvas.translate(-rect.left, -rect.top); - drawable.draw(mCanvas); - mCanvas.restoreToCount(saveCount); - mCanvas.setBitmap(null); - - mBlurPaint.setMaskFilter(mShadowBlurMaskFilter); - - int extraSize = (int) (2 * mShadowBitmapShift); - - int resultWidth = bitmapWidth + extraSize; - int resultHeight = bitmapHeight + extraSize; - key = (resultWidth << 16) | resultHeight; - Bitmap result = shouldCache ? mBitmapCache.get(key) : null; - if (result == null) { - result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ALPHA_8); - mCanvas.setBitmap(result); - } else { - // Use put instead of delete, to avoid unnecessary shrinking of cache array - mBitmapCache.put(key, null); - mCanvas.setBitmap(result); - mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); - } - mCanvas.drawBitmap(cache, mShadowBitmapShift, mShadowBitmapShift, mBlurPaint); - mCanvas.setBitmap(null); - return result; - } - - public void recycleShadowBitmap(Bitmap bitmap) { - if (bitmap != null) { - mBitmapCache.put((bitmap.getWidth() << 16) | bitmap.getHeight(), bitmap); - } - } -} diff --git a/src/com/android/launcher3/IconCache.java b/src/com/android/launcher3/IconCache.java index a49162c8b..9ac484738 100644 --- a/src/com/android/launcher3/IconCache.java +++ b/src/com/android/launcher3/IconCache.java @@ -47,6 +47,7 @@ import com.android.launcher3.compat.LauncherAppsCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.config.FeatureFlags; +import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.model.PackageItemInfo; import com.android.launcher3.util.ComponentKey; import com.android.launcher3.util.SQLiteCacheHelper; @@ -187,7 +188,7 @@ public class IconCache { private Bitmap makeDefaultIcon(UserHandleCompat user) { Drawable unbadged = getFullResDefaultActivityIcon(); - return Utilities.createBadgedIconBitmap(unbadged, user, mContext); + return LauncherIcons.createBadgedIconBitmap(unbadged, user, mContext); } /** @@ -387,7 +388,7 @@ public class IconCache { } if (entry == null) { entry = new CacheEntry(); - entry.icon = Utilities.createBadgedIconBitmap( + entry.icon = LauncherIcons.createBadgedIconBitmap( mIconProvider.getIcon(app, mIconDpi), app.getUser(), mContext); } @@ -555,7 +556,7 @@ public class IconCache { // Check the DB first. if (!getEntryFromDB(cacheKey, entry, useLowResIcon) || DEBUG_IGNORE_CACHE) { if (info != null) { - entry.icon = Utilities.createBadgedIconBitmap( + entry.icon = LauncherIcons.createBadgedIconBitmap( mIconProvider.getIcon(info, mIconDpi), info.getUser(), mContext); } else { @@ -606,7 +607,7 @@ public class IconCache { entry.title = title; } if (icon != null) { - entry.icon = Utilities.createIconBitmap(icon, mContext); + entry.icon = LauncherIcons.createIconBitmap(icon, mContext); } } @@ -641,7 +642,7 @@ public class IconCache { // Load the full res icon for the application, but if useLowResIcon is set, then // only keep the low resolution icon instead of the larger full-sized icon - Bitmap icon = Utilities.createBadgedIconBitmap( + Bitmap icon = LauncherIcons.createBadgedIconBitmap( appInfo.loadIcon(mPackageManager), user, mContext); Bitmap lowResIcon = generateLowResIcon(icon, mPackageBgColor); entry.title = appInfo.loadLabel(mPackageManager); diff --git a/src/com/android/launcher3/LauncherClings.java b/src/com/android/launcher3/LauncherClings.java deleted file mode 100644 index c1282b51c..000000000 --- a/src/com/android/launcher3/LauncherClings.java +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3; - -import android.content.Context; - -@Deprecated -public class LauncherClings { - private static final String WORKSPACE_CLING_DISMISSED_KEY = "cling_gel.workspace.dismissed"; - - public static void markFirstRunClingDismissed(Context ctx) { - Utilities.getPrefs(ctx).edit() - .putBoolean(WORKSPACE_CLING_DISMISSED_KEY, true) - .apply(); - } -} diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java index 68450e7a7..c4987298a 100644 --- a/src/com/android/launcher3/LauncherModel.java +++ b/src/com/android/launcher3/LauncherModel.java @@ -57,6 +57,7 @@ import com.android.launcher3.config.ProviderConfig; import com.android.launcher3.dynamicui.ExtractionUtils; import com.android.launcher3.folder.Folder; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.logging.FileLog; import com.android.launcher3.model.GridSizeMigrationTask; import com.android.launcher3.model.WidgetsModel; @@ -3159,7 +3160,7 @@ public class LauncherModel extends BroadcastReceiver // Update shortcuts which use iconResource. if ((si.iconResource != null) && pkgFilter.matches(si.iconResource.packageName)) { - Bitmap icon = Utilities.createIconBitmap( + Bitmap icon = LauncherIcons.createIconBitmap( si.iconResource.packageName, si.iconResource.resourceName, context); if (icon != null) { @@ -3753,17 +3754,15 @@ public class LauncherModel extends BroadcastReceiver } Bitmap icon = null; - boolean customIcon = false; ShortcutIconResource iconResource = null; if (bitmap instanceof Bitmap) { - icon = Utilities.createIconBitmap((Bitmap) bitmap, context); - customIcon = true; + icon = LauncherIcons.createIconBitmap((Bitmap) bitmap, context); } else { Parcelable extra = data.getParcelableExtra(Intent.EXTRA_SHORTCUT_ICON_RESOURCE); if (extra instanceof ShortcutIconResource) { iconResource = (ShortcutIconResource) extra; - icon = Utilities.createIconBitmap(iconResource.packageName, + icon = LauncherIcons.createIconBitmap(iconResource.packageName, iconResource.resourceName, context); } } diff --git a/src/com/android/launcher3/LauncherProvider.java b/src/com/android/launcher3/LauncherProvider.java index f3d949326..e1ff6dbc1 100644 --- a/src/com/android/launcher3/LauncherProvider.java +++ b/src/com/android/launcher3/LauncherProvider.java @@ -764,12 +764,7 @@ public class LauncherProvider extends ContentProvider { } } case 16: { - // We use the db version upgrade here to identify users who may not have seen - // clings yet (because they weren't available), but for whom the clings are now - // available (tablet users). Because one of the possible cling flows (migration) - // is very destructive (wipes out workspaces), we want to prevent this from showing - // until clear data. We do so by marking that the clings have been shown. - LauncherClings.markFirstRunClingDismissed(mContext); + // No-op } case 17: { // No-op diff --git a/src/com/android/launcher3/ShortcutInfo.java b/src/com/android/launcher3/ShortcutInfo.java index 9d7be1693..9b4bfbcad 100644 --- a/src/com/android/launcher3/ShortcutInfo.java +++ b/src/com/android/launcher3/ShortcutInfo.java @@ -31,6 +31,7 @@ import com.android.launcher3.compat.LauncherActivityInfoCompat; import com.android.launcher3.compat.UserHandleCompat; import com.android.launcher3.compat.UserManagerCompat; import com.android.launcher3.folder.FolderIcon; +import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.shortcuts.ShortcutInfoCompat; /** @@ -323,13 +324,13 @@ public class ShortcutInfo extends ItemInfo { IconCache cache = launcherAppState.getIconCache(); Bitmap unbadgedBitmap = unbadgedDrawable == null ? cache.getDefaultIcon(UserHandleCompat.myUserHandle()) - : Utilities.createScaledBitmapWithoutShadow(unbadgedDrawable, context); + : LauncherIcons.createScaledBitmapWithoutShadow(unbadgedDrawable, context); setIcon(getBadgedIcon(unbadgedBitmap, shortcutInfo, cache, context)); } protected Bitmap getBadgedIcon(Bitmap unbadgedBitmap, ShortcutInfoCompat shortcutInfo, IconCache cache, Context context) { - unbadgedBitmap = Utilities.addShadowToIcon(unbadgedBitmap); + unbadgedBitmap = LauncherIcons.addShadowToIcon(unbadgedBitmap); // Get the app info for the source activity. AppInfo appInfo = new AppInfo(); appInfo.user = user; @@ -338,9 +339,9 @@ public class ShortcutInfo extends ItemInfo { cache.getTitleAndIcon(appInfo, shortcutInfo.getActivityInfo(context), false); } catch (NullPointerException e) { // This may happen when we fail to load the activity info. Worst case ignore badging. - return Utilities.badgeIconForUser(unbadgedBitmap, user, context); + return LauncherIcons.badgeIconForUser(unbadgedBitmap, user, context); } - return Utilities.badgeWithBitmap(unbadgedBitmap, appInfo.iconBitmap, context); + return LauncherIcons.badgeWithBitmap(unbadgedBitmap, appInfo.iconBitmap, context); } /** Returns the ShortcutInfo id associated with the deep shortcut. */ diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java index e34f509da..17afe051a 100644 --- a/src/com/android/launcher3/Utilities.java +++ b/src/com/android/launcher3/Utilities.java @@ -31,19 +31,11 @@ import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.content.res.TypedArray; -import android.database.Cursor; import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Matrix; import android.graphics.Paint; -import android.graphics.PaintFlagsDrawFilter; import android.graphics.Rect; -import android.graphics.RectF; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.graphics.drawable.PaintDrawable; import android.os.Build; import android.os.Bundle; import android.os.PowerManager; @@ -62,11 +54,7 @@ import android.view.accessibility.AccessibilityEvent; import android.view.accessibility.AccessibilityManager; import android.widget.Toast; -import com.android.launcher3.compat.UserHandleCompat; -import com.android.launcher3.config.FeatureFlags; import com.android.launcher3.config.ProviderConfig; -import com.android.launcher3.graphics.ShadowGenerator; -import com.android.launcher3.util.IconNormalizer; import java.io.ByteArrayOutputStream; import java.io.Closeable; @@ -90,19 +78,9 @@ public final class Utilities { private static final String TAG = "Launcher.Utilities"; - private static final Rect sOldBounds = new Rect(); - private static final Canvas sCanvas = new Canvas(); - private static final Pattern sTrimPattern = Pattern.compile("^[\\s|\\p{javaSpaceChar}]*(.*)[\\s|\\p{javaSpaceChar}]*$"); - static { - sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, - Paint.FILTER_BITMAP_FLAG)); - } - static int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff }; - static int sColorIndex = 0; - private static final int[] sLoc0 = new int[2]; private static final int[] sLoc1 = new int[2]; @@ -170,198 +148,6 @@ public final class Utilities { return false; } - public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { - byte[] data = c.getBlob(iconIndex); - try { - return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context); - } catch (Exception e) { - return null; - } - } - - /** - * Returns a bitmap suitable for the all apps view. If the package or the resource do not - * exist, it returns null. - */ - public static Bitmap createIconBitmap(String packageName, String resourceName, - Context context) { - PackageManager packageManager = context.getPackageManager(); - // the resource - try { - Resources resources = packageManager.getResourcesForApplication(packageName); - if (resources != null) { - final int id = resources.getIdentifier(resourceName, null, null); - return createIconBitmap( - resources.getDrawableForDensity(id, LauncherAppState.getInstance() - .getInvariantDeviceProfile().fillResIconDpi), context); - } - } catch (Exception e) { - // Icon not found. - } - return null; - } - - private static int getIconBitmapSize() { - return LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize; - } - - /** - * Returns a bitmap which is of the appropriate size to be displayed as an icon - */ - public static Bitmap createIconBitmap(Bitmap icon, Context context) { - final int iconBitmapSize = getIconBitmapSize(); - if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) { - return icon; - } - return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context); - } - - /** - * Returns a bitmap suitable for the all apps view. The icon is badged for {@param user}. - * The bitmap is also visually normalized with other icons. - */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public static Bitmap createBadgedIconBitmap( - Drawable icon, UserHandleCompat user, Context context) { - float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? - 1 : IconNormalizer.getInstance().getScale(icon, null); - Bitmap bitmap = createIconBitmap(icon, context, scale); - return badgeIconForUser(bitmap, user, context); - } - - /** - * Badges the provided icon with the user badge if required. - */ - public static Bitmap badgeIconForUser(Bitmap icon, UserHandleCompat user, Context context) { - if (Utilities.ATLEAST_LOLLIPOP && user != null - && !UserHandleCompat.myUserHandle().equals(user)) { - BitmapDrawable drawable = new FixedSizeBitmapDrawable(icon); - Drawable badged = context.getPackageManager().getUserBadgedIcon( - drawable, user.getUser()); - if (badged instanceof BitmapDrawable) { - return ((BitmapDrawable) badged).getBitmap(); - } else { - return createIconBitmap(badged, context); - } - } else { - return icon; - } - } - - /** - * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually - * normalized with other icons and has enough spacing to add shadow. - */ - public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context) { - RectF iconBounds = new RectF(); - float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? - 1 : IconNormalizer.getInstance().getScale(icon, iconBounds); - scale = Math.min(scale, ShadowGenerator.getScaleForBounds(iconBounds)); - return createIconBitmap(icon, context, scale); - } - - /** - * Adds a shadow to the provided icon. It assumes that the icon has already been scaled using - * {@link #createScaledBitmapWithoutShadow(Drawable, Context)} - */ - public static Bitmap addShadowToIcon(Bitmap icon) { - return ShadowGenerator.getInstance().recreateIcon(icon); - } - - /** - * Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions. - */ - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) { - int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size); - synchronized (sCanvas) { - sCanvas.setBitmap(srcTgt); - sCanvas.drawBitmap(badge, new Rect(0, 0, badge.getWidth(), badge.getHeight()), - new Rect(srcTgt.getWidth() - badgeSize, - srcTgt.getHeight() - badgeSize, srcTgt.getWidth(), srcTgt.getHeight()), - new Paint(Paint.FILTER_BITMAP_FLAG)); - sCanvas.setBitmap(null); - } - return srcTgt; - } - - /** - * Returns a bitmap suitable for the all apps view. - */ - public static Bitmap createIconBitmap(Drawable icon, Context context) { - return createIconBitmap(icon, context, 1.0f /* scale */); - } - - /** - * @param scale the scale to apply before drawing {@param icon} on the canvas - */ - public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) { - synchronized (sCanvas) { - final int iconBitmapSize = getIconBitmapSize(); - - int width = iconBitmapSize; - int height = iconBitmapSize; - - if (icon instanceof PaintDrawable) { - PaintDrawable painter = (PaintDrawable) icon; - painter.setIntrinsicWidth(width); - painter.setIntrinsicHeight(height); - } else if (icon instanceof BitmapDrawable) { - // Ensure the bitmap has a density. - BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; - Bitmap bitmap = bitmapDrawable.getBitmap(); - if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) { - bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics()); - } - } - int sourceWidth = icon.getIntrinsicWidth(); - int sourceHeight = icon.getIntrinsicHeight(); - if (sourceWidth > 0 && sourceHeight > 0) { - // Scale the icon proportionally to the icon dimensions - final float ratio = (float) sourceWidth / sourceHeight; - if (sourceWidth > sourceHeight) { - height = (int) (width / ratio); - } else if (sourceHeight > sourceWidth) { - width = (int) (height * ratio); - } - } - - // no intrinsic size --> use default size - int textureWidth = iconBitmapSize; - int textureHeight = iconBitmapSize; - - final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, - Bitmap.Config.ARGB_8888); - final Canvas canvas = sCanvas; - canvas.setBitmap(bitmap); - - final int left = (textureWidth-width) / 2; - final int top = (textureHeight-height) / 2; - - @SuppressWarnings("all") // suppress dead code warning - final boolean debug = false; - if (debug) { - // draw a big box for the icon for debugging - canvas.drawColor(sColors[sColorIndex]); - if (++sColorIndex >= sColors.length) sColorIndex = 0; - Paint debugPaint = new Paint(); - debugPaint.setColor(0xffcccc00); - canvas.drawRect(left, top, left+width, top+height, debugPaint); - } - - sOldBounds.set(icon.getBounds()); - icon.setBounds(left, top, left+width, top+height); - canvas.save(Canvas.MATRIX_SAVE_FLAG); - canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2); - icon.draw(canvas); - canvas.restore(); - icon.setBounds(sOldBounds); - canvas.setBitmap(null); - - return bitmap; - } - } - /** * Given a coordinate relative to the descendant, find the coordinate in a parent view's * coordinates. @@ -880,28 +666,6 @@ public final class Utilities { return c == null || c.isEmpty(); } - /** - * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size. - * This allows the badging to be done based on the action bitmap size rather than - * the scaled bitmap size. - */ - private static class FixedSizeBitmapDrawable extends BitmapDrawable { - - public FixedSizeBitmapDrawable(Bitmap bitmap) { - super(null, bitmap); - } - - @Override - public int getIntrinsicHeight() { - return getBitmap().getWidth(); - } - - @Override - public int getIntrinsicWidth() { - return getBitmap().getWidth(); - } - } - public static int getColorAccent(Context context) { TypedArray ta = context.obtainStyledAttributes(new int[]{android.R.attr.colorAccent}); int colorAccent = ta.getColor(0, 0); diff --git a/src/com/android/launcher3/graphics/DragPreviewProvider.java b/src/com/android/launcher3/graphics/DragPreviewProvider.java index bc91c15be..7ad1e3a43 100644 --- a/src/com/android/launcher3/graphics/DragPreviewProvider.java +++ b/src/com/android/launcher3/graphics/DragPreviewProvider.java @@ -24,7 +24,6 @@ import android.graphics.drawable.Drawable; import android.view.View; import android.widget.TextView; -import com.android.launcher3.HolographicOutlineHelper; import com.android.launcher3.Launcher; import com.android.launcher3.PreloadIconDrawable; import com.android.launcher3.Workspace; @@ -137,7 +136,7 @@ public class DragPreviewProvider { mView.getHeight() + DRAG_BITMAP_PADDING, Bitmap.Config.ALPHA_8); canvas.setBitmap(b); drawDragView(canvas); - HolographicOutlineHelper.obtain(mView.getContext()) + HolographicOutlineHelper.getInstance(mView.getContext()) .applyExpensiveOutlineWithBlur(b, canvas); canvas.setBitmap(null); return b; diff --git a/src/com/android/launcher3/graphics/HolographicOutlineHelper.java b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java new file mode 100644 index 000000000..0d70bdee1 --- /dev/null +++ b/src/com/android/launcher3/graphics/HolographicOutlineHelper.java @@ -0,0 +1,229 @@ +/* + * Copyright (C) 2008 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.graphics; + +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Bitmap; +import android.graphics.BlurMaskFilter; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.Paint; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffXfermode; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.util.SparseArray; + +import com.android.launcher3.BubbleTextView; +import com.android.launcher3.R; +import com.android.launcher3.config.ProviderConfig; + +import java.nio.ByteBuffer; + +/** + * Utility class to generate shadow and outline effect, which are used for click feedback + * and drag-n-drop respectively. + */ +public class HolographicOutlineHelper { + + private static HolographicOutlineHelper sInstance; + + private final Canvas mCanvas = new Canvas(); + private final Paint mDrawPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private final Paint mBlurPaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + private final Paint mErasePaint = new Paint(Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG); + + private final BlurMaskFilter mMediumOuterBlurMaskFilter; + private final BlurMaskFilter mThinOuterBlurMaskFilter; + private final BlurMaskFilter mMediumInnerBlurMaskFilter; + + private final float mShadowBitmapShift; + private final BlurMaskFilter mShadowBlurMaskFilter; + + // We have 4 different icon sizes: homescreen, hotseat, folder & all-apps + private final SparseArray mBitmapCache = new SparseArray<>(4); + + private HolographicOutlineHelper(Context context) { + Resources res = context.getResources(); + + float mediumBlur = res.getDimension(R.dimen.blur_size_medium_outline); + mMediumOuterBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.OUTER); + mMediumInnerBlurMaskFilter = new BlurMaskFilter(mediumBlur, BlurMaskFilter.Blur.NORMAL); + + mThinOuterBlurMaskFilter = new BlurMaskFilter( + res.getDimension(R.dimen.blur_size_thin_outline), BlurMaskFilter.Blur.OUTER); + + mShadowBitmapShift = res.getDimension(R.dimen.blur_size_click_shadow); + mShadowBlurMaskFilter = new BlurMaskFilter(mShadowBitmapShift, BlurMaskFilter.Blur.NORMAL); + + mErasePaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT)); + } + + public static HolographicOutlineHelper getInstance(Context context) { + if (sInstance == null) { + sInstance = new HolographicOutlineHelper(context.getApplicationContext()); + } + return sInstance; + } + + /** + * Applies a more expensive and accurate outline to whatever is currently drawn in a specified + * bitmap. + */ + public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas) { + applyExpensiveOutlineWithBlur(srcDst, srcDstCanvas, true); + } + + public void applyExpensiveOutlineWithBlur(Bitmap srcDst, Canvas srcDstCanvas, + boolean clipAlpha) { + if (ProviderConfig.IS_DOGFOOD_BUILD && srcDst.getConfig() != Bitmap.Config.ALPHA_8) { + throw new RuntimeException("Outline blue is only supported on alpha bitmaps"); + } + + // We start by removing most of the alpha channel so as to ignore shadows, and + // other types of partial transparency when defining the shape of the object + if (clipAlpha) { + byte[] pixels = new byte[srcDst.getWidth() * srcDst.getHeight()]; + ByteBuffer buffer = ByteBuffer.wrap(pixels); + buffer.rewind(); + srcDst.copyPixelsToBuffer(buffer); + + for (int i = 0; i < pixels.length; i++) { + if ((pixels[i] & 0xFF) < 188) { + pixels[i] = 0; + } + } + + buffer.rewind(); + srcDst.copyPixelsFromBuffer(buffer); + } + + // calculate the outer blur first + mBlurPaint.setMaskFilter(mMediumOuterBlurMaskFilter); + int[] outerBlurOffset = new int[2]; + Bitmap thickOuterBlur = srcDst.extractAlpha(mBlurPaint, outerBlurOffset); + + mBlurPaint.setMaskFilter(mThinOuterBlurMaskFilter); + int[] brightOutlineOffset = new int[2]; + Bitmap brightOutline = srcDst.extractAlpha(mBlurPaint, brightOutlineOffset); + + // calculate the inner blur + srcDstCanvas.setBitmap(srcDst); + srcDstCanvas.drawColor(0xFF000000, PorterDuff.Mode.SRC_OUT); + mBlurPaint.setMaskFilter(mMediumInnerBlurMaskFilter); + int[] thickInnerBlurOffset = new int[2]; + Bitmap thickInnerBlur = srcDst.extractAlpha(mBlurPaint, thickInnerBlurOffset); + + // mask out the inner blur + srcDstCanvas.setBitmap(thickInnerBlur); + srcDstCanvas.drawBitmap(srcDst, -thickInnerBlurOffset[0], + -thickInnerBlurOffset[1], mErasePaint); + srcDstCanvas.drawRect(0, 0, -thickInnerBlurOffset[0], thickInnerBlur.getHeight(), + mErasePaint); + srcDstCanvas.drawRect(0, 0, thickInnerBlur.getWidth(), -thickInnerBlurOffset[1], + mErasePaint); + + // draw the inner and outer blur + srcDstCanvas.setBitmap(srcDst); + srcDstCanvas.drawColor(0, PorterDuff.Mode.CLEAR); + srcDstCanvas.drawBitmap(thickInnerBlur, thickInnerBlurOffset[0], thickInnerBlurOffset[1], + mDrawPaint); + srcDstCanvas.drawBitmap(thickOuterBlur, outerBlurOffset[0], outerBlurOffset[1], + mDrawPaint); + + // draw the bright outline + srcDstCanvas.drawBitmap(brightOutline, brightOutlineOffset[0], brightOutlineOffset[1], + mDrawPaint); + + // cleanup + srcDstCanvas.setBitmap(null); + brightOutline.recycle(); + thickOuterBlur.recycle(); + thickInnerBlur.recycle(); + } + + public Bitmap createMediumDropShadow(BubbleTextView view) { + return createMediumDropShadow(view.getIcon(), view.getScaleX(), view.getScaleY(), true); + } + + public Bitmap createMediumDropShadow(Drawable drawable, boolean shouldCache) { + return createMediumDropShadow(drawable, 1f, 1f, shouldCache); + } + + Bitmap createMediumDropShadow(Drawable drawable, float scaleX, float scaleY, + boolean shouldCache) { + if (drawable == null) { + return null; + } + Rect rect = drawable.getBounds(); + + int bitmapWidth = (int) (rect.width() * scaleX); + int bitmapHeight = (int) (rect.height() * scaleY); + if (bitmapHeight <= 0 || bitmapWidth <= 0) { + return null; + } + + int key = (bitmapWidth << 16) | bitmapHeight; + Bitmap cache = shouldCache ? mBitmapCache.get(key) : null; + if (cache == null) { + cache = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ALPHA_8); + mCanvas.setBitmap(cache); + + if (shouldCache) { + mBitmapCache.put(key, cache); + } + } else { + mCanvas.setBitmap(cache); + mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); + } + + int saveCount = mCanvas.save(); + mCanvas.scale(scaleX, scaleY); + mCanvas.translate(-rect.left, -rect.top); + drawable.draw(mCanvas); + mCanvas.restoreToCount(saveCount); + mCanvas.setBitmap(null); + + mBlurPaint.setMaskFilter(mShadowBlurMaskFilter); + + int extraSize = (int) (2 * mShadowBitmapShift); + + int resultWidth = bitmapWidth + extraSize; + int resultHeight = bitmapHeight + extraSize; + key = (resultWidth << 16) | resultHeight; + Bitmap result = shouldCache ? mBitmapCache.get(key) : null; + if (result == null) { + result = Bitmap.createBitmap(resultWidth, resultHeight, Bitmap.Config.ALPHA_8); + mCanvas.setBitmap(result); + } else { + // Use put instead of delete, to avoid unnecessary shrinking of cache array + mBitmapCache.put(key, null); + mCanvas.setBitmap(result); + mCanvas.drawColor(Color.BLACK, PorterDuff.Mode.CLEAR); + } + mCanvas.drawBitmap(cache, mShadowBitmapShift, mShadowBitmapShift, mBlurPaint); + mCanvas.setBitmap(null); + return result; + } + + public void recycleShadowBitmap(Bitmap bitmap) { + if (bitmap != null) { + mBitmapCache.put((bitmap.getWidth() << 16) | bitmap.getHeight(), bitmap); + } + } +} diff --git a/src/com/android/launcher3/graphics/IconNormalizer.java b/src/com/android/launcher3/graphics/IconNormalizer.java new file mode 100644 index 000000000..f90b2d72e --- /dev/null +++ b/src/com/android/launcher3/graphics/IconNormalizer.java @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.graphics; + +import android.graphics.Bitmap; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.RectF; +import android.graphics.drawable.Drawable; + +import com.android.launcher3.LauncherAppState; + +import java.nio.ByteBuffer; + +public class IconNormalizer { + + // Ratio of icon visible area to full icon size for a square shaped icon + private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576; + // Ratio of icon visible area to full icon size for a circular shaped icon + private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576; + + private static final float CIRCLE_AREA_BY_RECT = (float) Math.PI / 4; + + // Slope used to calculate icon visible area to full icon size for any generic shaped icon. + private static final float LINEAR_SCALE_SLOPE = + (MAX_CIRCLE_AREA_FACTOR - MAX_SQUARE_AREA_FACTOR) / (1 - CIRCLE_AREA_BY_RECT); + + private static final int MIN_VISIBLE_ALPHA = 40; + + private static final Object LOCK = new Object(); + private static IconNormalizer sIconNormalizer; + + private final int mMaxSize; + private final Bitmap mBitmap; + private final Canvas mCanvas; + private final byte[] mPixels; + + // for each y, stores the position of the leftmost x and the rightmost x + private final float[] mLeftBorder; + private final float[] mRightBorder; + + private IconNormalizer() { + // Use twice the icon size as maximum size to avoid scaling down twice. + mMaxSize = LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize * 2; + mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8); + mCanvas = new Canvas(mBitmap); + mPixels = new byte[mMaxSize * mMaxSize]; + + mLeftBorder = new float[mMaxSize]; + mRightBorder = new float[mMaxSize]; + } + + /** + * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it + * matches the design guidelines for a launcher icon. + * + * We first calculate the convex hull of the visible portion of the icon. + * This hull then compared with the bounding rectangle of the hull to find how closely it + * resembles a circle and a square, by comparing the ratio of the areas. Note that this is not an + * ideal solution but it gives satisfactory result without affecting the performance. + * + * This closeness is used to determine the ratio of hull area to the full icon size. + * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR} + * + * @param outBounds optional rect to receive the fraction distance from each edge. + */ + public synchronized float getScale(Drawable d, RectF outBounds) { + int width = d.getIntrinsicWidth(); + int height = d.getIntrinsicHeight(); + if (width <= 0 || height <= 0) { + width = width <= 0 || width > mMaxSize ? mMaxSize : width; + height = height <= 0 || height > mMaxSize ? mMaxSize : height; + } else if (width > mMaxSize || height > mMaxSize) { + int max = Math.max(width, height); + width = mMaxSize * width / max; + height = mMaxSize * height / max; + } + + mBitmap.eraseColor(Color.TRANSPARENT); + d.setBounds(0, 0, width, height); + d.draw(mCanvas); + + ByteBuffer buffer = ByteBuffer.wrap(mPixels); + buffer.rewind(); + mBitmap.copyPixelsToBuffer(buffer); + + // Overall bounds of the visible icon. + int topY = -1; + int bottomY = -1; + int leftX = mMaxSize + 1; + int rightX = -1; + + // Create border by going through all pixels one row at a time and for each row find + // the first and the last non-transparent pixel. Set those values to mLeftBorder and + // mRightBorder and use -1 if there are no visible pixel in the row. + + // buffer position + int index = 0; + // buffer shift after every row, width of buffer = mMaxSize + int rowSizeDiff = mMaxSize - width; + // first and last position for any row. + int firstX, lastX; + + for (int y = 0; y < height; y++) { + firstX = lastX = -1; + for (int x = 0; x < width; x++) { + if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) { + if (firstX == -1) { + firstX = x; + } + lastX = x; + } + index++; + } + index += rowSizeDiff; + + mLeftBorder[y] = firstX; + mRightBorder[y] = lastX; + + // If there is at least one visible pixel, update the overall bounds. + if (firstX != -1) { + bottomY = y; + if (topY == -1) { + topY = y; + } + + leftX = Math.min(leftX, firstX); + rightX = Math.max(rightX, lastX); + } + } + + if (topY == -1 || rightX == -1) { + // No valid pixels found. Do not scale. + return 1; + } + + convertToConvexArray(mLeftBorder, 1, topY, bottomY); + convertToConvexArray(mRightBorder, -1, topY, bottomY); + + // Area of the convex hull + float area = 0; + for (int y = 0; y < height; y++) { + if (mLeftBorder[y] <= -1) { + continue; + } + area += mRightBorder[y] - mLeftBorder[y] + 1; + } + + // Area of the rectangle required to fit the convex hull + float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX); + float hullByRect = area / rectArea; + + float scaleRequired; + if (hullByRect < CIRCLE_AREA_BY_RECT) { + scaleRequired = MAX_CIRCLE_AREA_FACTOR; + } else { + scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); + } + + if (outBounds != null) { + outBounds.left = ((float) leftX) / width; + outBounds.right = 1 - ((float) rightX) / width; + + outBounds.top = ((float) topY) / height; + outBounds.bottom = 1 - ((float) bottomY) / height; + } + + float areaScale = area / (width * height); + // Use sqrt of the final ratio as the images is scaled across both width and height. + float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; + return scale; + } + + /** + * Modifies {@param xCordinates} to represent a convex border. Fills in all missing values + * (except on either ends) with appropriate values. + * @param xCordinates map of x coordinate per y. + * @param direction 1 for left border and -1 for right border. + * @param topY the first Y position (inclusive) with a valid value. + * @param bottomY the last Y position (inclusive) with a valid value. + */ + private static void convertToConvexArray( + float[] xCordinates, int direction, int topY, int bottomY) { + int total = xCordinates.length; + // The tangent at each pixel. + float[] angles = new float[total - 1]; + + int first = topY; // First valid y coordinate + int last = -1; // Last valid y coordinate which didn't have a missing value + + float lastAngle = Float.MAX_VALUE; + + for (int i = topY + 1; i <= bottomY; i++) { + if (xCordinates[i] <= -1) { + continue; + } + int start; + + if (lastAngle == Float.MAX_VALUE) { + start = first; + } else { + float currentAngle = (xCordinates[i] - xCordinates[last]) / (i - last); + start = last; + // If this position creates a concave angle, keep moving up until we find a + // position which creates a convex angle. + if ((currentAngle - lastAngle) * direction < 0) { + while (start > first) { + start --; + currentAngle = (xCordinates[i] - xCordinates[start]) / (i - start); + if ((currentAngle - angles[start]) * direction >= 0) { + break; + } + } + } + } + + // Reset from last check + lastAngle = (xCordinates[i] - xCordinates[start]) / (i - start); + // Update all the points from start. + for (int j = start; j < i; j++) { + angles[j] = lastAngle; + xCordinates[j] = xCordinates[start] + lastAngle * (j - start); + } + last = i; + } + } + + public static IconNormalizer getInstance() { + synchronized (LOCK) { + if (sIconNormalizer == null) { + sIconNormalizer = new IconNormalizer(); + } + } + return sIconNormalizer; + } +} diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java new file mode 100644 index 000000000..9f3f3571d --- /dev/null +++ b/src/com/android/launcher3/graphics/LauncherIcons.java @@ -0,0 +1,258 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.launcher3.graphics; + +import android.annotation.TargetApi; +import android.content.Context; +import android.content.pm.PackageManager; +import android.content.res.Resources; +import android.database.Cursor; +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.PaintFlagsDrawFilter; +import android.graphics.Rect; +import android.graphics.RectF; +import android.graphics.drawable.BitmapDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.PaintDrawable; +import android.os.Build; + +import com.android.launcher3.LauncherAppState; +import com.android.launcher3.R; +import com.android.launcher3.Utilities; +import com.android.launcher3.compat.UserHandleCompat; +import com.android.launcher3.config.FeatureFlags; + +/** + * Helper methods for generating various launcher icons + */ +public class LauncherIcons { + + private static final Rect sOldBounds = new Rect(); + private static final Canvas sCanvas = new Canvas(); + + static { + sCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG, + Paint.FILTER_BITMAP_FLAG)); + } + + + public static Bitmap createIconBitmap(Cursor c, int iconIndex, Context context) { + byte[] data = c.getBlob(iconIndex); + try { + return createIconBitmap(BitmapFactory.decodeByteArray(data, 0, data.length), context); + } catch (Exception e) { + return null; + } + } + + /** + * Returns a bitmap suitable for the all apps view. If the package or the resource do not + * exist, it returns null. + */ + public static Bitmap createIconBitmap(String packageName, String resourceName, + Context context) { + PackageManager packageManager = context.getPackageManager(); + // the resource + try { + Resources resources = packageManager.getResourcesForApplication(packageName); + if (resources != null) { + final int id = resources.getIdentifier(resourceName, null, null); + return createIconBitmap( + resources.getDrawableForDensity(id, LauncherAppState.getInstance() + .getInvariantDeviceProfile().fillResIconDpi), context); + } + } catch (Exception e) { + // Icon not found. + } + return null; + } + + private static int getIconBitmapSize() { + return LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize; + } + + /** + * Returns a bitmap which is of the appropriate size to be displayed as an icon + */ + public static Bitmap createIconBitmap(Bitmap icon, Context context) { + final int iconBitmapSize = getIconBitmapSize(); + if (iconBitmapSize == icon.getWidth() && iconBitmapSize == icon.getHeight()) { + return icon; + } + return createIconBitmap(new BitmapDrawable(context.getResources(), icon), context); + } + + /** + * Returns a bitmap suitable for the all apps view. The icon is badged for {@param user}. + * The bitmap is also visually normalized with other icons. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public static Bitmap createBadgedIconBitmap( + Drawable icon, UserHandleCompat user, Context context) { + float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? + 1 : IconNormalizer.getInstance().getScale(icon, null); + Bitmap bitmap = createIconBitmap(icon, context, scale); + return badgeIconForUser(bitmap, user, context); + } + + /** + * Badges the provided icon with the user badge if required. + */ + public static Bitmap badgeIconForUser(Bitmap icon, UserHandleCompat user, Context context) { + if (Utilities.ATLEAST_LOLLIPOP && user != null + && !UserHandleCompat.myUserHandle().equals(user)) { + BitmapDrawable drawable = new FixedSizeBitmapDrawable(icon); + Drawable badged = context.getPackageManager().getUserBadgedIcon( + drawable, user.getUser()); + if (badged instanceof BitmapDrawable) { + return ((BitmapDrawable) badged).getBitmap(); + } else { + return createIconBitmap(badged, context); + } + } else { + return icon; + } + } + + /** + * Creates a normalized bitmap suitable for the all apps view. The bitmap is also visually + * normalized with other icons and has enough spacing to add shadow. + */ + public static Bitmap createScaledBitmapWithoutShadow(Drawable icon, Context context) { + RectF iconBounds = new RectF(); + float scale = FeatureFlags.LAUNCHER3_DISABLE_ICON_NORMALIZATION ? + 1 : IconNormalizer.getInstance().getScale(icon, iconBounds); + scale = Math.min(scale, ShadowGenerator.getScaleForBounds(iconBounds)); + return createIconBitmap(icon, context, scale); + } + + /** + * Adds a shadow to the provided icon. It assumes that the icon has already been scaled using + * {@link #createScaledBitmapWithoutShadow(Drawable, Context)} + */ + public static Bitmap addShadowToIcon(Bitmap icon) { + return ShadowGenerator.getInstance().recreateIcon(icon); + } + + /** + * Adds the {@param badge} on top of {@param srcTgt} using the badge dimensions. + */ + @TargetApi(Build.VERSION_CODES.LOLLIPOP) + public static Bitmap badgeWithBitmap(Bitmap srcTgt, Bitmap badge, Context context) { + int badgeSize = context.getResources().getDimensionPixelSize(R.dimen.profile_badge_size); + synchronized (sCanvas) { + sCanvas.setBitmap(srcTgt); + sCanvas.drawBitmap(badge, new Rect(0, 0, badge.getWidth(), badge.getHeight()), + new Rect(srcTgt.getWidth() - badgeSize, + srcTgt.getHeight() - badgeSize, srcTgt.getWidth(), srcTgt.getHeight()), + new Paint(Paint.FILTER_BITMAP_FLAG)); + sCanvas.setBitmap(null); + } + return srcTgt; + } + + /** + * Returns a bitmap suitable for the all apps view. + */ + public static Bitmap createIconBitmap(Drawable icon, Context context) { + return createIconBitmap(icon, context, 1.0f /* scale */); + } + + /** + * @param scale the scale to apply before drawing {@param icon} on the canvas + */ + public static Bitmap createIconBitmap(Drawable icon, Context context, float scale) { + synchronized (sCanvas) { + final int iconBitmapSize = getIconBitmapSize(); + + int width = iconBitmapSize; + int height = iconBitmapSize; + + if (icon instanceof PaintDrawable) { + PaintDrawable painter = (PaintDrawable) icon; + painter.setIntrinsicWidth(width); + painter.setIntrinsicHeight(height); + } else if (icon instanceof BitmapDrawable) { + // Ensure the bitmap has a density. + BitmapDrawable bitmapDrawable = (BitmapDrawable) icon; + Bitmap bitmap = bitmapDrawable.getBitmap(); + if (bitmap != null && bitmap.getDensity() == Bitmap.DENSITY_NONE) { + bitmapDrawable.setTargetDensity(context.getResources().getDisplayMetrics()); + } + } + int sourceWidth = icon.getIntrinsicWidth(); + int sourceHeight = icon.getIntrinsicHeight(); + if (sourceWidth > 0 && sourceHeight > 0) { + // Scale the icon proportionally to the icon dimensions + final float ratio = (float) sourceWidth / sourceHeight; + if (sourceWidth > sourceHeight) { + height = (int) (width / ratio); + } else if (sourceHeight > sourceWidth) { + width = (int) (height * ratio); + } + } + + // no intrinsic size --> use default size + int textureWidth = iconBitmapSize; + int textureHeight = iconBitmapSize; + + final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight, + Bitmap.Config.ARGB_8888); + final Canvas canvas = sCanvas; + canvas.setBitmap(bitmap); + + final int left = (textureWidth-width) / 2; + final int top = (textureHeight-height) / 2; + + sOldBounds.set(icon.getBounds()); + icon.setBounds(left, top, left+width, top+height); + canvas.save(Canvas.MATRIX_SAVE_FLAG); + canvas.scale(scale, scale, textureWidth / 2, textureHeight / 2); + icon.draw(canvas); + canvas.restore(); + icon.setBounds(sOldBounds); + canvas.setBitmap(null); + + return bitmap; + } + } + + /** + * An extension of {@link BitmapDrawable} which returns the bitmap pixel size as intrinsic size. + * This allows the badging to be done based on the action bitmap size rather than + * the scaled bitmap size. + */ + private static class FixedSizeBitmapDrawable extends BitmapDrawable { + + public FixedSizeBitmapDrawable(Bitmap bitmap) { + super(null, bitmap); + } + + @Override + public int getIntrinsicHeight() { + return getBitmap().getWidth(); + } + + @Override + public int getIntrinsicWidth() { + return getBitmap().getWidth(); + } + } +} diff --git a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java index 3c7ba327b..38fd9510a 100644 --- a/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java +++ b/src/com/android/launcher3/shortcuts/DeepShortcutsContainer.java @@ -63,6 +63,7 @@ import com.android.launcher3.dragndrop.DragController; import com.android.launcher3.dragndrop.DragLayer; import com.android.launcher3.dragndrop.DragOptions; import com.android.launcher3.dragndrop.DragView; +import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.graphics.TriangleShape; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -391,7 +392,7 @@ public class DeepShortcutsContainer extends LinearLayout implements View.OnLongC private void showDragView(BubbleTextView originalIcon) { // TODO: implement support for Drawable DragViews so we don't have to create a bitmap here. - Bitmap b = Utilities.createIconBitmap(originalIcon.getIcon(), mLauncher); + Bitmap b = LauncherIcons.createIconBitmap(originalIcon.getIcon(), mLauncher); float scale = mLauncher.getDragLayer().getLocationInDragLayer(originalIcon, mTempXY); int dragLayerX = Math.round(mTempXY[0] - (b.getWidth() - scale * originalIcon.getWidth()) / 2); int dragLayerY = Math.round(mTempXY[1] - (b.getHeight() - scale * b.getHeight()) / 2 diff --git a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java index 2adb82e2d..fc474f527 100644 --- a/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java +++ b/src/com/android/launcher3/shortcuts/ShortcutDragPreviewProvider.java @@ -23,7 +23,7 @@ import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.view.View; -import com.android.launcher3.HolographicOutlineHelper; +import com.android.launcher3.graphics.HolographicOutlineHelper; import com.android.launcher3.Launcher; import com.android.launcher3.Utilities; import com.android.launcher3.graphics.DragPreviewProvider; @@ -44,7 +44,7 @@ public class ShortcutDragPreviewProvider extends DragPreviewProvider { public Bitmap createDragOutline(Canvas canvas) { Bitmap b = drawScaledPreview(canvas, Bitmap.Config.ALPHA_8); - HolographicOutlineHelper.obtain(mView.getContext()) + HolographicOutlineHelper.getInstance(mView.getContext()) .applyExpensiveOutlineWithBlur(b, canvas); canvas.setBitmap(null); return b; diff --git a/src/com/android/launcher3/util/CursorIconInfo.java b/src/com/android/launcher3/util/CursorIconInfo.java index 4fefa986e..6603ee765 100644 --- a/src/com/android/launcher3/util/CursorIconInfo.java +++ b/src/com/android/launcher3/util/CursorIconInfo.java @@ -25,6 +25,7 @@ import android.text.TextUtils; import com.android.launcher3.LauncherSettings; import com.android.launcher3.ShortcutInfo; import com.android.launcher3.Utilities; +import com.android.launcher3.graphics.LauncherIcons; /** * Utility class to load icon from a cursor. @@ -59,7 +60,7 @@ public class CursorIconInfo { info.iconResource = new ShortcutIconResource(); info.iconResource.packageName = packageName; info.iconResource.resourceName = resourceName; - icon = Utilities.createIconBitmap(packageName, resourceName, mContext); + icon = LauncherIcons.createIconBitmap(packageName, resourceName, mContext); } if (icon == null) { // Failed to load from resource, try loading from DB. @@ -72,7 +73,7 @@ public class CursorIconInfo { * Loads the fixed bitmap from the icon if available. */ public Bitmap loadIcon(Cursor c) { - return Utilities.createIconBitmap(c, iconIndex, mContext); + return LauncherIcons.createIconBitmap(c, iconIndex, mContext); } /** diff --git a/src/com/android/launcher3/util/IconNormalizer.java b/src/com/android/launcher3/util/IconNormalizer.java deleted file mode 100644 index 040a1b51a..000000000 --- a/src/com/android/launcher3/util/IconNormalizer.java +++ /dev/null @@ -1,250 +0,0 @@ -/* - * Copyright (C) 2015 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.launcher3.util; - -import android.graphics.Bitmap; -import android.graphics.Canvas; -import android.graphics.Color; -import android.graphics.RectF; -import android.graphics.drawable.Drawable; - -import com.android.launcher3.LauncherAppState; - -import java.nio.ByteBuffer; - -public class IconNormalizer { - - // Ratio of icon visible area to full icon size for a square shaped icon - private static final float MAX_SQUARE_AREA_FACTOR = 375.0f / 576; - // Ratio of icon visible area to full icon size for a circular shaped icon - private static final float MAX_CIRCLE_AREA_FACTOR = 380.0f / 576; - - private static final float CIRCLE_AREA_BY_RECT = (float) Math.PI / 4; - - // Slope used to calculate icon visible area to full icon size for any generic shaped icon. - private static final float LINEAR_SCALE_SLOPE = - (MAX_CIRCLE_AREA_FACTOR - MAX_SQUARE_AREA_FACTOR) / (1 - CIRCLE_AREA_BY_RECT); - - private static final int MIN_VISIBLE_ALPHA = 40; - - private static final Object LOCK = new Object(); - private static IconNormalizer sIconNormalizer; - - private final int mMaxSize; - private final Bitmap mBitmap; - private final Canvas mCanvas; - private final byte[] mPixels; - - // for each y, stores the position of the leftmost x and the rightmost x - private final float[] mLeftBorder; - private final float[] mRightBorder; - - private IconNormalizer() { - // Use twice the icon size as maximum size to avoid scaling down twice. - mMaxSize = LauncherAppState.getInstance().getInvariantDeviceProfile().iconBitmapSize * 2; - mBitmap = Bitmap.createBitmap(mMaxSize, mMaxSize, Bitmap.Config.ALPHA_8); - mCanvas = new Canvas(mBitmap); - mPixels = new byte[mMaxSize * mMaxSize]; - - mLeftBorder = new float[mMaxSize]; - mRightBorder = new float[mMaxSize]; - } - - /** - * Returns the amount by which the {@param d} should be scaled (in both dimensions) so that it - * matches the design guidelines for a launcher icon. - * - * We first calculate the convex hull of the visible portion of the icon. - * This hull then compared with the bounding rectangle of the hull to find how closely it - * resembles a circle and a square, by comparing the ratio of the areas. Note that this is not an - * ideal solution but it gives satisfactory result without affecting the performance. - * - * This closeness is used to determine the ratio of hull area to the full icon size. - * Refer {@link #MAX_CIRCLE_AREA_FACTOR} and {@link #MAX_SQUARE_AREA_FACTOR} - * - * @param outBounds optional rect to receive the fraction distance from each edge. - */ - public synchronized float getScale(Drawable d, RectF outBounds) { - int width = d.getIntrinsicWidth(); - int height = d.getIntrinsicHeight(); - if (width <= 0 || height <= 0) { - width = width <= 0 || width > mMaxSize ? mMaxSize : width; - height = height <= 0 || height > mMaxSize ? mMaxSize : height; - } else if (width > mMaxSize || height > mMaxSize) { - int max = Math.max(width, height); - width = mMaxSize * width / max; - height = mMaxSize * height / max; - } - - mBitmap.eraseColor(Color.TRANSPARENT); - d.setBounds(0, 0, width, height); - d.draw(mCanvas); - - ByteBuffer buffer = ByteBuffer.wrap(mPixels); - buffer.rewind(); - mBitmap.copyPixelsToBuffer(buffer); - - // Overall bounds of the visible icon. - int topY = -1; - int bottomY = -1; - int leftX = mMaxSize + 1; - int rightX = -1; - - // Create border by going through all pixels one row at a time and for each row find - // the first and the last non-transparent pixel. Set those values to mLeftBorder and - // mRightBorder and use -1 if there are no visible pixel in the row. - - // buffer position - int index = 0; - // buffer shift after every row, width of buffer = mMaxSize - int rowSizeDiff = mMaxSize - width; - // first and last position for any row. - int firstX, lastX; - - for (int y = 0; y < height; y++) { - firstX = lastX = -1; - for (int x = 0; x < width; x++) { - if ((mPixels[index] & 0xFF) > MIN_VISIBLE_ALPHA) { - if (firstX == -1) { - firstX = x; - } - lastX = x; - } - index++; - } - index += rowSizeDiff; - - mLeftBorder[y] = firstX; - mRightBorder[y] = lastX; - - // If there is at least one visible pixel, update the overall bounds. - if (firstX != -1) { - bottomY = y; - if (topY == -1) { - topY = y; - } - - leftX = Math.min(leftX, firstX); - rightX = Math.max(rightX, lastX); - } - } - - if (topY == -1 || rightX == -1) { - // No valid pixels found. Do not scale. - return 1; - } - - convertToConvexArray(mLeftBorder, 1, topY, bottomY); - convertToConvexArray(mRightBorder, -1, topY, bottomY); - - // Area of the convex hull - float area = 0; - for (int y = 0; y < height; y++) { - if (mLeftBorder[y] <= -1) { - continue; - } - area += mRightBorder[y] - mLeftBorder[y] + 1; - } - - // Area of the rectangle required to fit the convex hull - float rectArea = (bottomY + 1 - topY) * (rightX + 1 - leftX); - float hullByRect = area / rectArea; - - float scaleRequired; - if (hullByRect < CIRCLE_AREA_BY_RECT) { - scaleRequired = MAX_CIRCLE_AREA_FACTOR; - } else { - scaleRequired = MAX_SQUARE_AREA_FACTOR + LINEAR_SCALE_SLOPE * (1 - hullByRect); - } - - if (outBounds != null) { - outBounds.left = ((float) leftX) / width; - outBounds.right = 1 - ((float) rightX) / width; - - outBounds.top = ((float) topY) / height; - outBounds.bottom = 1 - ((float) bottomY) / height; - } - - float areaScale = area / (width * height); - // Use sqrt of the final ratio as the images is scaled across both width and height. - float scale = areaScale > scaleRequired ? (float) Math.sqrt(scaleRequired / areaScale) : 1; - return scale; - } - - /** - * Modifies {@param xCordinates} to represent a convex border. Fills in all missing values - * (except on either ends) with appropriate values. - * @param xCordinates map of x coordinate per y. - * @param direction 1 for left border and -1 for right border. - * @param topY the first Y position (inclusive) with a valid value. - * @param bottomY the last Y position (inclusive) with a valid value. - */ - private static void convertToConvexArray( - float[] xCordinates, int direction, int topY, int bottomY) { - int total = xCordinates.length; - // The tangent at each pixel. - float[] angles = new float[total - 1]; - - int first = topY; // First valid y coordinate - int last = -1; // Last valid y coordinate which didn't have a missing value - - float lastAngle = Float.MAX_VALUE; - - for (int i = topY + 1; i <= bottomY; i++) { - if (xCordinates[i] <= -1) { - continue; - } - int start; - - if (lastAngle == Float.MAX_VALUE) { - start = first; - } else { - float currentAngle = (xCordinates[i] - xCordinates[last]) / (i - last); - start = last; - // If this position creates a concave angle, keep moving up until we find a - // position which creates a convex angle. - if ((currentAngle - lastAngle) * direction < 0) { - while (start > first) { - start --; - currentAngle = (xCordinates[i] - xCordinates[start]) / (i - start); - if ((currentAngle - angles[start]) * direction >= 0) { - break; - } - } - } - } - - // Reset from last check - lastAngle = (xCordinates[i] - xCordinates[start]) / (i - start); - // Update all the points from start. - for (int j = start; j < i; j++) { - angles[j] = lastAngle; - xCordinates[j] = xCordinates[start] + lastAngle * (j - start); - } - last = i; - } - } - - public static IconNormalizer getInstance() { - synchronized (LOCK) { - if (sIconNormalizer == null) { - sIconNormalizer = new IconNormalizer(); - } - } - return sIconNormalizer; - } -} diff --git a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java index eaa0bb3d5..7c06701e8 100644 --- a/src/com/android/launcher3/widget/PendingItemPreviewProvider.java +++ b/src/com/android/launcher3/widget/PendingItemPreviewProvider.java @@ -21,7 +21,7 @@ import android.graphics.Canvas; import android.graphics.Rect; import android.view.View; -import com.android.launcher3.HolographicOutlineHelper; +import com.android.launcher3.graphics.HolographicOutlineHelper; import com.android.launcher3.Launcher; import com.android.launcher3.PendingAddItemInfo; import com.android.launcher3.Workspace; @@ -67,7 +67,7 @@ public class PendingItemPreviewProvider extends DragPreviewProvider { // Don't clip alpha values for the drag outline if we're using the default widget preview boolean clipAlpha = !(mAddInfo instanceof PendingAddWidgetInfo && (((PendingAddWidgetInfo) mAddInfo).previewImage == 0)); - HolographicOutlineHelper.obtain(mView.getContext()) + HolographicOutlineHelper.getInstance(mView.getContext()) .applyExpensiveOutlineWithBlur(b, canvas, clipAlpha); canvas.setBitmap(null); diff --git a/src/com/android/launcher3/widget/WidgetsContainerView.java b/src/com/android/launcher3/widget/WidgetsContainerView.java index 70b2945f5..656fba601 100644 --- a/src/com/android/launcher3/widget/WidgetsContainerView.java +++ b/src/com/android/launcher3/widget/WidgetsContainerView.java @@ -45,6 +45,7 @@ import com.android.launcher3.Utilities; import com.android.launcher3.WidgetPreviewLoader; import com.android.launcher3.Workspace; import com.android.launcher3.dragndrop.DragController; +import com.android.launcher3.graphics.LauncherIcons; import com.android.launcher3.model.WidgetsModel; import com.android.launcher3.userevent.nano.LauncherLogProto; import com.android.launcher3.userevent.nano.LauncherLogProto.Target; @@ -240,7 +241,7 @@ public class WidgetsContainerView extends BaseContainerView } else { PendingAddShortcutInfo createShortcutInfo = (PendingAddShortcutInfo) v.getTag(); Drawable icon = mIconCache.getFullResIcon(createShortcutInfo.activityInfo); - preview = Utilities.createIconBitmap(icon, mLauncher); + preview = LauncherIcons.createIconBitmap(icon, mLauncher); createItemInfo.spanX = createItemInfo.spanY = 1; scale = ((float) mLauncher.getDeviceProfile().iconSizePx) / preview.getWidth(); } -- cgit v1.2.3