1 /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- 2 * This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 package org.mozilla.gecko.icons.loader; 6 7 import android.content.Context; 8 import android.graphics.Bitmap; 9 import android.graphics.Canvas; 10 import android.graphics.Color; 11 import android.graphics.Paint; 12 import android.graphics.Rect; 13 import android.support.annotation.Nullable; 14 import android.support.annotation.VisibleForTesting; 15 import android.util.Log; 16 17 import org.mozilla.gecko.db.BrowserDB; 18 import org.mozilla.gecko.db.SuggestedSites; 19 import org.mozilla.gecko.home.ImageLoader; 20 import org.mozilla.gecko.icons.IconRequest; 21 import org.mozilla.gecko.icons.IconResponse; 22 23 import java.io.IOException; 24 25 public class SuggestedSiteLoader implements IconLoader { 26 public static final String SUGGESTED_SITE_TOUCHTILE = "suggestedsitetile:"; 27 28 private static final String LOGTAG = SuggestedSiteLoader.class.getSimpleName(); 29 30 @Nullable 31 @Override load(IconRequest request)32 public IconResponse load(IconRequest request) { 33 if (request.shouldSkipDisk()) { 34 return null; 35 } 36 37 final String iconUrl = request.getBestIcon().getUrl(); 38 39 if (iconUrl.startsWith(SUGGESTED_SITE_TOUCHTILE)) { 40 return buildIcon(request.getContext(), iconUrl.substring(SUGGESTED_SITE_TOUCHTILE.length()), request.getTargetSize()); 41 } 42 43 return null; 44 } 45 46 @VisibleForTesting loadBitmap(final Context context, final String iconURL)47 Bitmap loadBitmap(final Context context, final String iconURL) throws IOException { 48 return ImageLoader.with(context) 49 .load(iconURL) 50 .noFade() 51 .get(); 52 } 53 buildIcon(final Context context, final String siteURL, final int targetSize)54 private IconResponse buildIcon(final Context context, final String siteURL, final int targetSize) { 55 final SuggestedSites suggestedSites = BrowserDB.from(context).getSuggestedSites(); 56 57 if (suggestedSites == null) { 58 // See longer explanation in SuggestedSitePreparer: suggested sites aren't always loaded. 59 // If they aren't, SuggestedSitePreparer won't suggest any bundled icons so we should 60 // never try to load them anyway, but we should double check here to be completely safe. 61 return null; 62 } 63 64 final String iconLocation = suggestedSites.getImageUrlForUrl(siteURL); 65 final String backgroundColorString = suggestedSites.getBackgroundColorForUrl(siteURL); 66 67 if (iconLocation == null || backgroundColorString == null) { 68 // There's little we can do if loading a bundled resource fails: this failure could 69 // be caused by a distribution (as opposed to Gecko), so we should just shout loudly, 70 // as opposed to crashing: 71 Log.e(LOGTAG, "Unable to find tile data definitions for site:" + siteURL); 72 return null; 73 } 74 75 final int backgroundColor = Color.parseColor(backgroundColorString); 76 77 try { 78 final Bitmap foreground = loadBitmap(context, iconLocation); 79 80 // Our supplied tile icons are bigger than the max favicon size. They are also not square,, 81 // so we scale them down and fit them into a square (i.e. centering in their smaller dimension): 82 final Bitmap output = Bitmap.createBitmap(targetSize, targetSize, foreground.getConfig()); 83 final Canvas canvas = new Canvas(output); 84 canvas.drawColor(backgroundColor); 85 86 final Rect src = new Rect(0, 0, foreground.getWidth(), foreground.getHeight()); 87 88 // And we draw the icon in the middle of that square: 89 final float scaleFactor = targetSize * 1.0f / Math.max(foreground.getHeight(), foreground.getWidth()); 90 91 final int heightDelta = targetSize - (int) (scaleFactor * foreground.getHeight()); 92 final int widthDelta = targetSize - (int) (scaleFactor * foreground.getWidth()); 93 94 final Rect dst = new Rect(widthDelta / 2, heightDelta / 2, output.getWidth() - (widthDelta / 2), output.getHeight() - (heightDelta / 2)); 95 96 // Interpolate when painting: 97 final Paint paint = new Paint(); 98 paint.setFilterBitmap(true); 99 paint.setAntiAlias(true); 100 paint.setDither(true); 101 102 canvas.drawBitmap(foreground, src, dst, paint); 103 104 final IconResponse response = IconResponse.create(output); 105 response.updateColor(backgroundColor); 106 107 return response; 108 } catch (IOException e) { 109 // Same as above: if we can't load the data, shout - but don't crash: 110 Log.e(LOGTAG, "Unable to load tile data for site:" + siteURL + " at location:" + iconLocation); 111 } 112 113 return null; 114 } 115 } 116