diff options
Diffstat (limited to 'mobile/android/base/java/org/mozilla/gecko/home/ImageLoader.java')
-rw-r--r-- | mobile/android/base/java/org/mozilla/gecko/home/ImageLoader.java | 164 |
1 files changed, 164 insertions, 0 deletions
diff --git a/mobile/android/base/java/org/mozilla/gecko/home/ImageLoader.java b/mobile/android/base/java/org/mozilla/gecko/home/ImageLoader.java new file mode 100644 index 000000000..2bbd82a8d --- /dev/null +++ b/mobile/android/base/java/org/mozilla/gecko/home/ImageLoader.java @@ -0,0 +1,164 @@ +/* -*- 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://<basepath>/<imagename> + * + * Which will look for the following file in the distribution: + * + * <distribution-root-dir>/<basepath>/<device-density>/<imagename>.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<Density> 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"); + } + } +} |