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