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