/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.home; import android.content.Context; import android.net.Uri; import android.util.DisplayMetrics; import android.util.Log; import com.squareup.picasso.LruCache; import com.squareup.picasso.Picasso; import com.squareup.picasso.Downloader.Response; import com.squareup.picasso.UrlConnectionDownloader; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.util.EnumSet; import java.util.Set; import org.mozilla.gecko.distribution.Distribution; public class ImageLoader { private static final String LOGTAG = "GeckoImageLoader"; private static final String DISTRIBUTION_SCHEME = "gecko.distribution"; private static final String SUGGESTED_SITES_AUTHORITY = "suggestedsites"; // The order of density factors to try when looking for an image resource // in the distribution directory. It looks for an exact match first (1.0) then // tries to find images with higher density (2.0 and 1.5). If no image is found, // try a lower density (0.5). See loadDistributionImage(). private static final float[] densityFactors = new float[] { 1.0f, 2.0f, 1.5f, 0.5f }; private static enum Density { MDPI, HDPI, XHDPI, XXHDPI; @Override public String toString() { return super.toString().toLowerCase(); } } // Picasso instance and LruCache lrucache are protected by synchronization. private static Picasso instance; private static LruCache lrucache; public static synchronized Picasso with(Context context) { if (instance == null) { lrucache = new LruCache(context); Picasso.Builder builder = new Picasso.Builder(context).memoryCache(lrucache); final Distribution distribution = Distribution.getInstance(context.getApplicationContext()); builder.downloader(new ImageDownloader(context, distribution)); instance = builder.build(); } return instance; } public static synchronized void clearLruCache() { if (lrucache != null) { lrucache.evictAll(); } } /** * Custom Downloader built on top of Picasso's UrlConnectionDownloader * that supports loading images from custom URIs. */ public static class ImageDownloader extends UrlConnectionDownloader { private final Context context; private final Distribution distribution; public ImageDownloader(Context context, Distribution distribution) { super(context); this.context = context; this.distribution = distribution; } private Density getDensity(float factor) { final DisplayMetrics dm = context.getResources().getDisplayMetrics(); final float densityDpi = dm.densityDpi * factor; if (densityDpi >= DisplayMetrics.DENSITY_XXHIGH) { return Density.XXHDPI; } else if (densityDpi >= DisplayMetrics.DENSITY_XHIGH) { return Density.XHDPI; } else if (densityDpi >= DisplayMetrics.DENSITY_HIGH) { return Density.HDPI; } // Fallback to mdpi, no need to handle ldpi. return Density.MDPI; } @Override public Response load(Uri uri, boolean localCacheOnly) throws IOException { final String scheme = uri.getScheme(); if (DISTRIBUTION_SCHEME.equals(scheme)) { return loadDistributionImage(uri); } return super.load(uri, localCacheOnly); } private static String getPathForDensity(String basePath, Density density, String filename) { final File dir = new File(basePath, density.toString()); return String.format("%s/%s.png", dir.toString(), filename); } /** * Handle distribution URIs in Picasso. The expected format is: * * gecko.distribution:/// * * Which will look for the following file in the distribution: * * ///.png */ private Response loadDistributionImage(Uri uri) throws IOException { // Eliminate the leading '//' final String ssp = uri.getSchemeSpecificPart().substring(2); final String filename; final String basePath; final int slashIndex = ssp.lastIndexOf('/'); if (slashIndex == -1) { filename = ssp; basePath = ""; } else { filename = ssp.substring(slashIndex + 1); basePath = ssp.substring(0, slashIndex); } Set triedDensities = EnumSet.noneOf(Density.class); for (int i = 0; i < densityFactors.length; i++) { final Density density = getDensity(densityFactors[i]); if (!triedDensities.add(density)) { continue; } final String path = getPathForDensity(basePath, density, filename); Log.d(LOGTAG, "Trying to load image from distribution " + path); final File f = distribution.getDistributionFile(path); if (f != null) { return new Response(new FileInputStream(f), true); } } throw new ResponseException("Couldn't find suggested site image in distribution"); } } }