1/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.policy.impl;
18
19import android.graphics.drawable.BitmapDrawable;
20import android.graphics.drawable.Drawable;
21import android.graphics.drawable.PaintDrawable;
22import android.graphics.drawable.StateListDrawable;
23import android.graphics.Bitmap;
24import android.graphics.BlurMaskFilter;
25import android.graphics.Canvas;
26import android.graphics.ColorMatrix;
27import android.graphics.ColorMatrixColorFilter;
28import android.graphics.Paint;
29import android.graphics.PaintFlagsDrawFilter;
30import android.graphics.PixelFormat;
31import android.graphics.PorterDuff;
32import android.graphics.Rect;
33import android.graphics.RectF;
34import android.graphics.TableMaskFilter;
35import android.graphics.Typeface;
36import android.text.Layout.Alignment;
37import android.text.StaticLayout;
38import android.text.TextPaint;
39import android.util.DisplayMetrics;
40import android.util.Log;
41import android.util.TypedValue;
42import android.view.ContextThemeWrapper;
43import android.content.res.Resources;
44import android.content.Context;
45
46/**
47 * Various utilities shared amongst the Launcher's classes.
48 */
49final class IconUtilities {
50    private static final String TAG = "IconUtilities";
51
52    private static final int sColors[] = { 0xffff0000, 0xff00ff00, 0xff0000ff };
53
54    private int mIconWidth = -1;
55    private int mIconHeight = -1;
56    private int mIconTextureWidth = -1;
57    private int mIconTextureHeight = -1;
58
59    private final Paint mPaint = new Paint();
60    private final Paint mBlurPaint = new Paint();
61    private final Paint mGlowColorPressedPaint = new Paint();
62    private final Paint mGlowColorFocusedPaint = new Paint();
63    private final Rect mOldBounds = new Rect();
64    private final Canvas mCanvas = new Canvas();
65    private final DisplayMetrics mDisplayMetrics;
66
67    private int mColorIndex = 0;
68
69    public IconUtilities(Context context) {
70        final Resources resources = context.getResources();
71        DisplayMetrics metrics = mDisplayMetrics = resources.getDisplayMetrics();
72        final float density = metrics.density;
73        final float blurPx = 5 * density;
74
75        mIconWidth = mIconHeight = (int) resources.getDimension(android.R.dimen.app_icon_size);
76        mIconTextureWidth = mIconTextureHeight = mIconWidth + (int)(blurPx*2);
77
78        mBlurPaint.setMaskFilter(new BlurMaskFilter(blurPx, BlurMaskFilter.Blur.NORMAL));
79
80        TypedValue value = new TypedValue();
81        mGlowColorPressedPaint.setColor(context.getTheme().resolveAttribute(
82                android.R.attr.colorPressedHighlight, value, true) ? value.data : 0xffffc300);
83        mGlowColorPressedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
84        mGlowColorFocusedPaint.setColor(context.getTheme().resolveAttribute(
85                android.R.attr.colorFocusedHighlight, value, true) ? value.data : 0xffff8e00);
86        mGlowColorFocusedPaint.setMaskFilter(TableMaskFilter.CreateClipTable(0, 30));
87
88        ColorMatrix cm = new ColorMatrix();
89        cm.setSaturation(0.2f);
90
91        mCanvas.setDrawFilter(new PaintFlagsDrawFilter(Paint.DITHER_FLAG,
92                Paint.FILTER_BITMAP_FLAG));
93    }
94
95    public Drawable createIconDrawable(Drawable src) {
96        Bitmap scaled = createIconBitmap(src);
97
98        StateListDrawable result = new StateListDrawable();
99
100        result.addState(new int[] { android.R.attr.state_focused },
101                new BitmapDrawable(createSelectedBitmap(scaled, false)));
102        result.addState(new int[] { android.R.attr.state_pressed },
103                new BitmapDrawable(createSelectedBitmap(scaled, true)));
104        result.addState(new int[0], new BitmapDrawable(scaled));
105
106        result.setBounds(0, 0, mIconTextureWidth, mIconTextureHeight);
107        return result;
108    }
109
110    /**
111     * Returns a bitmap suitable for the all apps view.  The bitmap will be a power
112     * of two sized ARGB_8888 bitmap that can be used as a gl texture.
113     */
114    private Bitmap createIconBitmap(Drawable icon) {
115        int width = mIconWidth;
116        int height = mIconHeight;
117
118        if (icon instanceof PaintDrawable) {
119            PaintDrawable painter = (PaintDrawable) icon;
120            painter.setIntrinsicWidth(width);
121            painter.setIntrinsicHeight(height);
122        } else if (icon instanceof BitmapDrawable) {
123            // Ensure the bitmap has a density.
124            BitmapDrawable bitmapDrawable = (BitmapDrawable) icon;
125            Bitmap bitmap = bitmapDrawable.getBitmap();
126            if (bitmap.getDensity() == Bitmap.DENSITY_NONE) {
127                bitmapDrawable.setTargetDensity(mDisplayMetrics);
128            }
129        }
130        int sourceWidth = icon.getIntrinsicWidth();
131        int sourceHeight = icon.getIntrinsicHeight();
132
133        if (sourceWidth > 0 && sourceHeight > 0) {
134            // There are intrinsic sizes.
135            if (width < sourceWidth || height < sourceHeight) {
136                // It's too big, scale it down.
137                final float ratio = (float) sourceWidth / sourceHeight;
138                if (sourceWidth > sourceHeight) {
139                    height = (int) (width / ratio);
140                } else if (sourceHeight > sourceWidth) {
141                    width = (int) (height * ratio);
142                }
143            } else if (sourceWidth < width && sourceHeight < height) {
144                // It's small, use the size they gave us.
145                width = sourceWidth;
146                height = sourceHeight;
147            }
148        }
149
150        // no intrinsic size --> use default size
151        int textureWidth = mIconTextureWidth;
152        int textureHeight = mIconTextureHeight;
153
154        final Bitmap bitmap = Bitmap.createBitmap(textureWidth, textureHeight,
155                Bitmap.Config.ARGB_8888);
156        final Canvas canvas = mCanvas;
157        canvas.setBitmap(bitmap);
158
159        final int left = (textureWidth-width) / 2;
160        final int top = (textureHeight-height) / 2;
161
162        if (false) {
163            // draw a big box for the icon for debugging
164            canvas.drawColor(sColors[mColorIndex]);
165            if (++mColorIndex >= sColors.length) mColorIndex = 0;
166            Paint debugPaint = new Paint();
167            debugPaint.setColor(0xffcccc00);
168            canvas.drawRect(left, top, left+width, top+height, debugPaint);
169        }
170
171        mOldBounds.set(icon.getBounds());
172        icon.setBounds(left, top, left+width, top+height);
173        icon.draw(canvas);
174        icon.setBounds(mOldBounds);
175
176        return bitmap;
177    }
178
179    private Bitmap createSelectedBitmap(Bitmap src, boolean pressed) {
180        final Bitmap result = Bitmap.createBitmap(mIconTextureWidth, mIconTextureHeight,
181                Bitmap.Config.ARGB_8888);
182        final Canvas dest = new Canvas(result);
183
184        dest.drawColor(0, PorterDuff.Mode.CLEAR);
185
186        int[] xy = new int[2];
187        Bitmap mask = src.extractAlpha(mBlurPaint, xy);
188
189        dest.drawBitmap(mask, xy[0], xy[1],
190                pressed ? mGlowColorPressedPaint : mGlowColorFocusedPaint);
191
192        mask.recycle();
193
194        dest.drawBitmap(src, 0, 0, mPaint);
195        dest.setBitmap(null);
196
197        return result;
198    }
199}
200