1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5package org.chromium.chrome.browser;
6
7import android.app.ActivityManager;
8import android.content.Context;
9import android.content.Intent;
10import android.content.pm.PackageManager;
11import android.content.pm.ResolveInfo;
12import android.graphics.Bitmap;
13import android.graphics.Canvas;
14import android.graphics.Color;
15import android.graphics.Paint;
16import android.graphics.Path;
17import android.graphics.PorterDuff;
18import android.graphics.PorterDuffColorFilter;
19import android.graphics.PorterDuffXfermode;
20import android.graphics.Rect;
21import android.graphics.RectF;
22import android.graphics.drawable.BitmapDrawable;
23import android.graphics.drawable.Drawable;
24import android.net.Uri;
25import android.os.Build;
26import android.util.Log;
27import android.util.TypedValue;
28
29import org.chromium.chrome.R;
30
31import java.util.List;
32
33/**
34 * Util class for bookmarks.
35 */
36public class BookmarkUtils {
37
38    // There is no public string defining this intent so if Home changes the value, we
39    // have to update this string.
40    private static final String INSTALL_SHORTCUT = "com.android.launcher.action.INSTALL_SHORTCUT";
41    private static final int DEFAULT_RGB_VALUE = 145;
42    private static final String TAG = "BookmarkUtils";
43    public static final String REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB =
44            "REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB";
45    private static final int INSET_DIMENSION_FOR_TOUCHICON = 1;
46    private static final int TOUCHICON_BORDER_RADII = 10;
47
48    /**
49     * Creates an intent that will add a shortcut to the home screen.
50     * @param context Context used to create the intent.
51     * @param shortcutIntent Intent to fire when the shortcut is activated.
52     * @param title Title of the bookmark.
53     * @param favicon Bookmark favicon.
54     * @param rValue Red component of the dominant favicon color.
55     * @param gValue Green component of the dominant favicon color.
56     * @param bValue Blue component of the dominant favicon color.
57     * @return Intent for the shortcut.
58     */
59    public static Intent createAddToHomeIntent(Context context, Intent shortcutIntent, String title,
60            Bitmap favicon, int rValue, int gValue, int bValue) {
61        Intent i = new Intent(INSTALL_SHORTCUT);
62        i.putExtra(Intent.EXTRA_SHORTCUT_INTENT, shortcutIntent);
63        i.putExtra(Intent.EXTRA_SHORTCUT_NAME, title);
64        i.putExtra(Intent.EXTRA_SHORTCUT_ICON, createIcon(context, favicon, rValue,
65                gValue, bValue));
66        return i;
67    }
68
69    /**
70     * Creates an intent that will add a shortcut to the home screen.
71     * @param context Context used to create the intent.
72     * @param url Url of the bookmark.
73     * @param title Title of the bookmark.
74     * @param favicon Bookmark favicon.
75     * @param rValue Red component of the dominant favicon color.
76     * @param gValue Green component of the dominant favicon color.
77     * @param bValue Blue component of the dominant favicon color.
78     * @return Intent for the shortcut.
79     */
80    public static Intent createAddToHomeIntent(Context context, String url, String title,
81            Bitmap favicon, int rValue, int gValue, int bValue) {
82        Intent shortcutIntent = createShortcutIntent(context, url);
83        return createAddToHomeIntent(
84                context, shortcutIntent, title, favicon, rValue, gValue, bValue);
85    }
86
87    /**
88     * Shortcut intent for icon on homescreen.
89     * @param context Context used to create the intent.
90     * @param url Url of the bookmark.
91     * @return Intent for onclick action of the shortcut.
92     */
93    public static Intent createShortcutIntent(Context context, String url) {
94        Intent shortcutIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
95        shortcutIntent.putExtra(REUSE_URL_MATCHING_TAB_ELSE_NEW_TAB, true);
96        return shortcutIntent;
97    }
98
99    /**
100     * Utility method to check if a shortcut can be added to the homescreen.
101     * @param context Context used to get the package manager.
102     * @return if a shortcut can be added to the homescreen under the current profile.
103     */
104    public static boolean isAddToHomeIntentSupported(Context context) {
105        PackageManager pm = context.getPackageManager();
106        Intent i = new Intent(INSTALL_SHORTCUT);
107        List<ResolveInfo> receivers = pm.queryBroadcastReceivers(
108                i, PackageManager.GET_INTENT_FILTERS);
109        return !receivers.isEmpty();
110    }
111
112    /**
113     * Creates an icon to be associated with this bookmark. If available, the touch icon
114     * will be used, else we draw our own.
115     * @param context Context used to create the intent.
116     * @param favicon Bookmark favicon bitmap.
117     * @param rValue Red component of the dominant favicon color.
118     * @param gValue Green component of the dominant favicon color.
119     * @param bValue Blue component of the dominant favicon color.
120     * @return Bitmap Either the touch-icon or the newly created favicon.
121     */
122    private static Bitmap createIcon(Context context, Bitmap favicon, int rValue,
123            int gValue, int bValue) {
124        Bitmap bitmap = null;
125        ActivityManager am = (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
126        final int iconSize = am.getLauncherLargeIconSize();
127        final int iconDensity = am.getLauncherLargeIconDensity();
128        try {
129            bitmap = Bitmap.createBitmap(iconSize, iconSize, Bitmap.Config.ARGB_8888);
130            Canvas canvas = new Canvas(bitmap);
131            if (favicon == null) {
132                favicon = getBitmapFromResourceId(context, R.drawable.globe_favicon, iconDensity);
133                rValue = gValue = bValue = DEFAULT_RGB_VALUE;
134            }
135            final int smallestSide = iconSize;
136            if (favicon.getWidth() >= smallestSide / 2 && favicon.getHeight() >= smallestSide / 2) {
137                drawTouchIconToCanvas(context, favicon, canvas);
138            } else {
139                drawWidgetBackgroundToCanvas(context, canvas, iconDensity,
140                        Color.rgb(rValue, gValue, bValue));
141                drawFaviconToCanvas(context, favicon, canvas);
142            }
143            canvas.setBitmap(null);
144        } catch (OutOfMemoryError e) {
145            Log.w(TAG, "OutOfMemoryError while trying to draw bitmap on canvas.");
146        }
147        return bitmap;
148    }
149
150    private static Bitmap getBitmapFromResourceId(Context context, int id, int density) {
151        Drawable drawable = null;
152        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH_MR1) {
153            drawable = context.getResources().getDrawableForDensity(id, density);
154        } else {
155            drawable = context.getResources().getDrawable(id);
156        }
157
158        if (drawable instanceof BitmapDrawable) {
159            BitmapDrawable bd = (BitmapDrawable) drawable;
160            return bd.getBitmap();
161        }
162        assert false : "The drawable was not a bitmap drawable as expected";
163        return null;
164    }
165
166    /**
167     * Use touch-icon or higher-resolution favicon and round the corners.
168     * @param context    Context used to get resources.
169     * @param touchIcon  Touch icon bitmap.
170     * @param canvas     Canvas that holds the touch icon.
171     */
172    private static void drawTouchIconToCanvas(
173            Context context, Bitmap touchIcon, Canvas canvas) {
174        Rect iconBounds = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
175        Rect src = new Rect(0, 0, touchIcon.getWidth(), touchIcon.getHeight());
176        Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
177        paint.setFilterBitmap(true);
178        canvas.drawBitmap(touchIcon, src, iconBounds, paint);
179        // Convert dp to px.
180        int borderRadii = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
181                TOUCHICON_BORDER_RADII, context.getResources().getDisplayMetrics());
182        Path path = new Path();
183        path.setFillType(Path.FillType.INVERSE_WINDING);
184        RectF rect = new RectF(iconBounds);
185        rect.inset(INSET_DIMENSION_FOR_TOUCHICON, INSET_DIMENSION_FOR_TOUCHICON);
186        path.addRoundRect(rect, borderRadii, borderRadii, Path.Direction.CW);
187        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
188        canvas.drawPath(path, paint);
189    }
190
191    /**
192     * Draw the favicon with dominant color.
193     * @param context Context used to create the intent.
194     * @param favicon favicon bitmap.
195     * @param canvas Canvas that holds the favicon.
196     */
197    private static void drawFaviconToCanvas(Context context, Bitmap favicon, Canvas canvas) {
198        Rect iconBounds = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
199        int faviconSize = iconBounds.width() / 3;
200        Bitmap scaledFavicon = Bitmap.createScaledBitmap(favicon, faviconSize, faviconSize, true);
201        canvas.drawBitmap(scaledFavicon,
202                iconBounds.exactCenterX() - scaledFavicon.getWidth() / 2.0f,
203                iconBounds.exactCenterY() - scaledFavicon.getHeight() / 2.0f, null);
204    }
205
206    /**
207     * Draw document icon to canvas.
208     * @param context     Context used to get bitmap resources.
209     * @param canvas      Canvas that holds the document icon.
210     * @param iconDensity Density information to get bitmap resources.
211     * @param color       Color for the document icon's folding and the bottom strip.
212     */
213    private static void drawWidgetBackgroundToCanvas(
214            Context context, Canvas canvas, int iconDensity, int color) {
215        Rect iconBounds = new Rect(0, 0, canvas.getWidth(), canvas.getHeight());
216        Bitmap bookmarkWidgetBg =
217                getBitmapFromResourceId(context, R.mipmap.bookmark_widget_bg, iconDensity);
218        Bitmap bookmarkWidgetBgHighlights = getBitmapFromResourceId(context,
219                R.mipmap.bookmark_widget_bg_highlights, iconDensity);
220        if (bookmarkWidgetBg == null || bookmarkWidgetBgHighlights == null) {
221            Log.w(TAG, "Can't load R.mipmap.bookmark_widget_bg or " +
222                    "R.mipmap.bookmark_widget_bg_highlights.");
223            return;
224        }
225        Paint paint = new Paint(Paint.FILTER_BITMAP_FLAG);
226        canvas.drawBitmap(bookmarkWidgetBg, null, iconBounds, paint);
227
228        // The following color filter will convert bookmark_widget_bg_highlights' white color to
229        // the 'color' variable when it is painted to 'canvas'.
230        paint.setColorFilter(new PorterDuffColorFilter(color, PorterDuff.Mode.SRC_IN));
231        canvas.drawBitmap(bookmarkWidgetBgHighlights, null, iconBounds, paint);
232    }
233}
234