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