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