PackageIconLoader.java revision 2353e9c002c45c0adc8a979a49ff317cae5838b8
1/*
2 * Copyright (C) 2009 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.quicksearchbox;
18
19import com.android.quicksearchbox.util.Util;
20
21import android.content.ContentResolver;
22import android.content.Context;
23import android.content.pm.PackageManager;
24import android.content.pm.PackageManager.NameNotFoundException;
25import android.content.res.Resources;
26import android.graphics.drawable.Drawable;
27import android.net.Uri;
28import android.text.TextUtils;
29import android.util.Log;
30
31import java.io.FileNotFoundException;
32import java.io.IOException;
33import java.io.InputStream;
34import java.util.List;
35
36/**
37 * Loads icons from other packages.
38 *
39 * Code partly stolen from {@link ContentResolver} and android.app.SuggestionsAdapter.
40  */
41public class PackageIconLoader implements IconLoader {
42
43    private static final boolean DBG = false;
44    private static final String TAG = "QSB.PackageIconLoader";
45
46    private final Context mPackageContext;
47
48    /**
49     * Creates a new icon loader.
50     *
51     * @param packageName The name of the package from which the icons will be loaded.
52     *        Resource IDs without an explicit package will be resolved against the package
53     *        of this context.
54     * @throws PackageManager.NameNotFoundException If the package is not found.
55     */
56    public PackageIconLoader(Context context, String packageName)
57            throws PackageManager.NameNotFoundException, SecurityException {
58        mPackageContext = context.createPackageContext(packageName, Context.CONTEXT_RESTRICTED);
59    }
60
61    public Drawable getIcon(String drawableId) {
62        if (DBG) Log.d(TAG, "getIcon(" + drawableId + ")");
63        if (TextUtils.isEmpty(drawableId) || "0".equals(drawableId)) {
64            return null;
65        }
66        try {
67            // First, see if it's just an integer
68            int resourceId = Integer.parseInt(drawableId);
69            // If so, find it by resource ID
70            return mPackageContext.getResources().getDrawable(resourceId);
71        } catch (NumberFormatException nfe) {
72            // It's not an integer, use it as a URI
73            Uri uri = Uri.parse(drawableId);
74            return getDrawable(uri);
75        } catch (Resources.NotFoundException nfe) {
76            // It was an integer, but it couldn't be found, bail out
77            Log.w(TAG, "Icon resource not found: " + drawableId);
78            return null;
79        }
80    }
81
82    public Uri getIconUri(String drawableId) {
83        if (TextUtils.isEmpty(drawableId) || "0".equals(drawableId)) {
84            return null;
85        }
86        try {
87            int resourceId = Integer.parseInt(drawableId);
88            return Util.getResourceUri(mPackageContext, resourceId);
89        } catch (NumberFormatException nfe) {
90            return Uri.parse(drawableId);
91        }
92    }
93
94    /**
95     * Gets a drawable by URI.
96     *
97     * @return A drawable, or {@code null} if the drawable could not be loaded.
98     */
99    private Drawable getDrawable(Uri uri) {
100        try {
101            String scheme = uri.getScheme();
102            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
103                // Load drawables through Resources, to get the source density information
104                OpenResourceIdResult r = getResourceId(uri);
105                try {
106                    return r.r.getDrawable(r.id);
107                } catch (Resources.NotFoundException ex) {
108                    throw new FileNotFoundException("Resource does not exist: " + uri);
109                }
110            } else {
111                // Let the ContentResolver handle content and file URIs.
112                InputStream stream = mPackageContext.getContentResolver().openInputStream(uri);
113                if (stream == null) {
114                    throw new FileNotFoundException("Failed to open " + uri);
115                }
116                try {
117                    return Drawable.createFromStream(stream, null);
118                } finally {
119                    try {
120                        stream.close();
121                    } catch (IOException ex) {
122                        Log.e(TAG, "Error closing icon stream for " + uri, ex);
123                    }
124                }
125            }
126        } catch (FileNotFoundException fnfe) {
127            Log.w(TAG, "Icon not found: " + uri + ", " + fnfe.getMessage());
128            return null;
129        }
130    }
131
132    /**
133     * A resource identified by the {@link Resources} that contains it, and a resource id.
134     */
135    public class OpenResourceIdResult {
136        public Resources r;
137        public int id;
138    }
139
140    /**
141     * Resolves an android.resource URI to a {@link Resources} and a resource id.
142     */
143    public OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
144        String authority = uri.getAuthority();
145        Resources r;
146        if (TextUtils.isEmpty(authority)) {
147            throw new FileNotFoundException("No authority: " + uri);
148        } else {
149            try {
150                r = mPackageContext.getPackageManager().getResourcesForApplication(authority);
151            } catch (NameNotFoundException ex) {
152                throw new FileNotFoundException("Failed to get resources: " + ex);
153            }
154        }
155        List<String> path = uri.getPathSegments();
156        if (path == null) {
157            throw new FileNotFoundException("No path: " + uri);
158        }
159        int len = path.size();
160        int id;
161        if (len == 1) {
162            try {
163                id = Integer.parseInt(path.get(0));
164            } catch (NumberFormatException e) {
165                throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
166            }
167        } else if (len == 2) {
168            id = r.getIdentifier(path.get(1), path.get(0), authority);
169        } else {
170            throw new FileNotFoundException("More than two path segments: " + uri);
171        }
172        if (id == 0) {
173            throw new FileNotFoundException("No resource found for: " + uri);
174        }
175        OpenResourceIdResult res = new OpenResourceIdResult();
176        res.r = r;
177        res.id = id;
178        return res;
179    }
180}
181