summaryrefslogtreecommitdiffstats
path: root/mobile/android/base/java/org/mozilla/gecko/home/ImageLoader.java
blob: 2bbd82a8df775b945ea7b92a4fbdc0e4db5c0cb4 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
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");
        }
    }
}