13e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/*
23e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Copyright (C) 2009 The Android Open Source Project
33e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
43e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Licensed under the Apache License, Version 2.0 (the "License");
53e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * you may not use this file except in compliance with the License.
63e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * You may obtain a copy of the License at
73e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
83e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *      http://www.apache.org/licenses/LICENSE-2.0
93e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Unless required by applicable law or agreed to in writing, software
113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * distributed under the License is distributed on an "AS IS" BASIS,
123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * See the License for the specific language governing permissions and
143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * limitations under the License.
153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert */
163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpackage com.android.quicksearchbox;
183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
19e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwoodimport com.android.quicksearchbox.util.CachedLater;
20516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwoodimport com.android.quicksearchbox.util.NamedTask;
21516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwoodimport com.android.quicksearchbox.util.NamedTaskExecutor;
22e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwoodimport com.android.quicksearchbox.util.Now;
23e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwoodimport com.android.quicksearchbox.util.NowOrLater;
242353e9c002c45c0adc8a979a49ff317cae5838b8Bjorn Bringertimport com.android.quicksearchbox.util.Util;
252353e9c002c45c0adc8a979a49ff317cae5838b8Bjorn Bringert
263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.ContentResolver;
273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.Context;
28e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringertimport android.content.pm.PackageManager;
293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.pm.PackageManager.NameNotFoundException;
303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.content.res.Resources;
313e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.graphics.drawable.Drawable;
323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.net.Uri;
33e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwoodimport android.os.Handler;
343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.text.TextUtils;
353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport android.util.Log;
363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport java.io.FileNotFoundException;
383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport java.io.IOException;
393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport java.io.InputStream;
403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertimport java.util.List;
413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert/**
433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert * Loads icons from other packages.
443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert *
45fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert * Code partly stolen from {@link ContentResolver} and android.app.SuggestionsAdapter.
46fde948e69f59589cf0d217ea414af7947de600bbBjorn Bringert  */
473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringertpublic class PackageIconLoader implements IconLoader {
483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private static final boolean DBG = false;
503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private static final String TAG = "QSB.PackageIconLoader";
513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
5225f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert    private final Context mContext;
5325f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert
5425f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert    private final String mPackageName;
5525f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert
5625f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert    private Context mPackageContext;
573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
58e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood    private final Handler mUiThread;
59e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood
60516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood    private final NamedTaskExecutor mIconLoaderExecutor;
61e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood
623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Creates a new icon loader.
643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
6525f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert     * @param context The QSB application context.
66e1fc4581ebab65220a82bcdb3272b4eb85453837Bjorn Bringert     * @param packageName The name of the package from which the icons will be loaded.
673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        Resource IDs without an explicit package will be resolved against the package
683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *        of this context.
693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
70516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood    public PackageIconLoader(Context context, String packageName, Handler uiThread,
71516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood            NamedTaskExecutor iconLoaderExecutor) {
7225f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert        mContext = context;
7325f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert        mPackageName = packageName;
74e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        mUiThread = uiThread;
75516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood        mIconLoaderExecutor = iconLoaderExecutor;
7625f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert    }
7725f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert
7825f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert    private boolean ensurePackageContext() {
7925f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert        if (mPackageContext == null) {
8025f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert            try {
8125f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert                mPackageContext = mContext.createPackageContext(mPackageName,
8225f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert                        Context.CONTEXT_RESTRICTED);
8325f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert            } catch (PackageManager.NameNotFoundException ex) {
8425f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert                // This should only happen if the app has just be uninstalled
8525f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert                Log.e(TAG, "Application not found " + mPackageName);
8625f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert                return false;
8725f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert            }
8825f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert        }
8925f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert        return true;
903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
92e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood    public NowOrLater<Drawable> getIcon(final String drawableId) {
933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (DBG) Log.d(TAG, "getIcon(" + drawableId + ")");
943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (TextUtils.isEmpty(drawableId) || "0".equals(drawableId)) {
95e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            return new Now<Drawable>(null);
963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
97e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        if (!ensurePackageContext()) {
98e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            return new Now<Drawable>(null);
99e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        }
100e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        NowOrLater<Drawable> drawable;
1013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        try {
1023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // First, see if it's just an integer
1033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            int resourceId = Integer.parseInt(drawableId);
1043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // If so, find it by resource ID
105e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            Drawable icon = mPackageContext.getResources().getDrawable(resourceId);
106e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            drawable = new Now<Drawable>(icon);
1073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } catch (NumberFormatException nfe) {
1083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // It's not an integer, use it as a URI
1093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Uri uri = Uri.parse(drawableId);
110516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(uri.getScheme())) {
111e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                // load all resources synchronously, to reduce UI flickering
112e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                drawable = new Now<Drawable>(getDrawable(uri));
113e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            } else {
114e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                drawable = new IconLaterTask(uri);
115e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            }
1163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } catch (Resources.NotFoundException nfe) {
1173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            // It was an integer, but it couldn't be found, bail out
1183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.w(TAG, "Icon resource not found: " + drawableId);
119e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            drawable = new Now<Drawable>(null);
1203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
121e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        return drawable;
1223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1233e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1243e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    public Uri getIconUri(String drawableId) {
1253e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (TextUtils.isEmpty(drawableId) || "0".equals(drawableId)) {
1263e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return null;
1273e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
12825f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert        if (!ensurePackageContext()) return null;
1293e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        try {
1303e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            int resourceId = Integer.parseInt(drawableId);
1312353e9c002c45c0adc8a979a49ff317cae5838b8Bjorn Bringert            return Util.getResourceUri(mPackageContext, resourceId);
1323e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } catch (NumberFormatException nfe) {
1333e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return Uri.parse(drawableId);
1343e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1353e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1363e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1373e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
1383e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Gets a drawable by URI.
1393e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     *
1403e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * @return A drawable, or {@code null} if the drawable could not be loaded.
1413e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
1423e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    private Drawable getDrawable(Uri uri) {
1433e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        try {
1443e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            String scheme = uri.getScheme();
1453e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            if (ContentResolver.SCHEME_ANDROID_RESOURCE.equals(scheme)) {
1463e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                // Load drawables through Resources, to get the source density information
1473e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                OpenResourceIdResult r = getResourceId(uri);
1483e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                try {
1493e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    return r.r.getDrawable(r.id);
1503e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                } catch (Resources.NotFoundException ex) {
1513e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    throw new FileNotFoundException("Resource does not exist: " + uri);
1523e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                }
1533e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            } else {
1543e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                // Let the ContentResolver handle content and file URIs.
1553e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                InputStream stream = mPackageContext.getContentResolver().openInputStream(uri);
1563e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                if (stream == null) {
1573e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    throw new FileNotFoundException("Failed to open " + uri);
1583e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                }
1593e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                try {
1603e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    return Drawable.createFromStream(stream, null);
1613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                } finally {
1623e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    try {
1633e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                        stream.close();
1643e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    } catch (IOException ex) {
1653e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                        Log.e(TAG, "Error closing icon stream for " + uri, ex);
1663e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                    }
1673e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                }
1683e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
1693e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } catch (FileNotFoundException fnfe) {
1703e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            Log.w(TAG, "Icon not found: " + uri + ", " + fnfe.getMessage());
1713e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            return null;
1723e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1733e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1743e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1753e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
1763e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * A resource identified by the {@link Resources} that contains it, and a resource id.
1773e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
17825f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert    private class OpenResourceIdResult {
1793e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public Resources r;
1803e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        public int id;
1813e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
1823e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert
1833e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    /**
1843e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     * Resolves an android.resource URI to a {@link Resources} and a resource id.
1853e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert     */
18625f44e14e0638b84b0a3f40f3f1a663cc3e999cbBjorn Bringert    private OpenResourceIdResult getResourceId(Uri uri) throws FileNotFoundException {
1873e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        String authority = uri.getAuthority();
1883e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        Resources r;
1893e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (TextUtils.isEmpty(authority)) {
1903e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new FileNotFoundException("No authority: " + uri);
1913e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {
1923e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            try {
1933e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                r = mPackageContext.getPackageManager().getResourcesForApplication(authority);
1943e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            } catch (NameNotFoundException ex) {
1952353e9c002c45c0adc8a979a49ff317cae5838b8Bjorn Bringert                throw new FileNotFoundException("Failed to get resources: " + ex);
1963e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
1973e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
1983e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        List<String> path = uri.getPathSegments();
1993e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (path == null) {
2003e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new FileNotFoundException("No path: " + uri);
2013e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2023e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        int len = path.size();
2033e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        int id;
2043e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (len == 1) {
2053e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            try {
2063e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                id = Integer.parseInt(path.get(0));
2073e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            } catch (NumberFormatException e) {
2083e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert                throw new FileNotFoundException("Single path segment is not a resource ID: " + uri);
2093e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            }
2103e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else if (len == 2) {
2113e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            id = r.getIdentifier(path.get(1), path.get(0), authority);
2123e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        } else {
2133e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new FileNotFoundException("More than two path segments: " + uri);
2143e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2153e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        if (id == 0) {
2163e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert            throw new FileNotFoundException("No resource found for: " + uri);
2173e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        }
2183e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        OpenResourceIdResult res = new OpenResourceIdResult();
2193e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        res.r = r;
2203e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        res.id = id;
2213e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert        return res;
2223e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert    }
223e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood
224516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood    private class IconLaterTask extends CachedLater<Drawable> implements NamedTask {
225e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        private final Uri mUri;
226e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood
227e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        public IconLaterTask(Uri iconUri) {
228e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            mUri = iconUri;
229e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        }
230e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood
231e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        @Override
232e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        protected void create() {
233516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood            mIconLoaderExecutor.execute(this);
234e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        }
235e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood
236e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        @Override
237e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        public void run() {
238e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            final Drawable icon = getIcon();
239e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            mUiThread.post(new Runnable(){
240e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                public void run() {
241e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                    store(icon);
242e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                }});
243e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        }
244e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood
245516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood        @Override
246516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood        public String getName() {
247516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood            return mPackageName;
248516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood        }
249516781305d7427e79928c95c6ea2b7689a4bf6ceMathew Inwood
250e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        private Drawable getIcon() {
251e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            try {
252e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                return getDrawable(mUri);
253e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            } catch (Throwable t) {
254e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                // we're making a call into another package, which could throw any exception.
255e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                // Make sure it doesn't crash QSB
256e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                Log.e(TAG, "Failed to load icon " + mUri, t);
257e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood                return null;
258e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood            }
259e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood        }
260e29d52aa72c96c3147fa91d83aeb8dafc6d1f578Mathew Inwood    }
2613e44ff1f2a204db3f479698cf0b3eab3d451dec2Bjorn Bringert}
262