Home | History | Annotate | Download | only in quicksearchbox
      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