165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/*
265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project
365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License");
565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License.
665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at
765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *      http://www.apache.org/licenses/LICENSE-2.0
965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software
1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS,
1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and
1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License.
1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.widget;
1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.accounts.Account;
2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.accounts.AccountManager;
2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Context;
2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.Intent.ShortcutIconResource;
2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.pm.PackageManager.NameNotFoundException;
2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.Resources;
2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.content.res.Resources.NotFoundException;
2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.Bitmap;
2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.BitmapFactory;
2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.Canvas;
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.drawable.Drawable;
3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.Uri;
3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.AsyncTask;
3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Log;
3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.TypedValue;
3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.ImageView;
3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.util.AccountImageHelper;
3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.util.ByteArrayPool;
3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.util.CachedInputStream;
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.util.UriUtils;
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.BufferedInputStream;
4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.IOException;
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.InputStream;
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.lang.ref.WeakReference;
4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.net.URL;
4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.net.URLConnection;
4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/**
4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * AsyncTask which loads a bitmap.
5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * <p>
5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * The source of this can be another package (via a resource), a URI (content provider), or
5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * a file path.
5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * @see BitmapWorkerOptions
5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class BitmapWorkerTask extends AsyncTask<BitmapWorkerOptions, Void, Bitmap> {
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String TAG = "BitmapWorker";
5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String GOOGLE_ACCOUNT_TYPE = "com.google";
6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final boolean DEBUG = false;
6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int SOCKET_TIMEOUT = 10000;
6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final int READ_TIMEOUT = 10000;
6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
666e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler    private final WeakReference<ImageView> mImageView;
6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    // a flag for if the bitmap is scaled from original source
6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected boolean mScaled;
6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public BitmapWorkerTask(ImageView imageView) {
716e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler        mImageView = new WeakReference<>(imageView);
7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mScaled = false;
7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected Bitmap doInBackground(BitmapWorkerOptions... params) {
7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return retrieveBitmap(params[0]);
7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected Bitmap retrieveBitmap(BitmapWorkerOptions workerOptions) {
8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (workerOptions.getIconResource() != null) {
8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return getBitmapFromResource(workerOptions.getContext(),
8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        workerOptions.getIconResource(),
8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        workerOptions);
8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (workerOptions.getResourceUri() != null) {
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (UriUtils.isAndroidResourceUri(workerOptions.getResourceUri())
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        || UriUtils.isShortcutIconResourceUri(workerOptions.getResourceUri())) {
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    // Make an icon resource from this.
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return getBitmapFromResource(workerOptions.getContext(),
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            UriUtils.getIconResource(workerOptions.getResourceUri()),
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            workerOptions);
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else if (UriUtils.isWebUri(workerOptions.getResourceUri())) {
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        return getBitmapFromHttp(workerOptions);
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else if (UriUtils.isContentUri(workerOptions.getResourceUri())) {
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return getBitmapFromContent(workerOptions);
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else if (UriUtils.isAccountImageUri(workerOptions.getResourceUri())) {
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return getAccountImage(workerOptions);
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    Log.e(TAG, "Error loading bitmap - unknown resource URI! "
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            + workerOptions.getResourceUri());
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.e(TAG, "Error loading bitmap - no source!");
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (IOException e) {
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.e(TAG, "Error loading url " + workerOptions.getResourceUri(), e);
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (RuntimeException e) {
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.e(TAG, "Critical Error loading url " + workerOptions.getResourceUri(), e);
11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected void onPostExecute(Bitmap bitmap) {
1206e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler        final ImageView imageView = mImageView.get();
1216e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler        if (imageView != null) {
1226e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler            imageView.setImageBitmap(bitmap);
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Bitmap getBitmapFromResource(Context context, ShortcutIconResource iconResource,
12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            BitmapWorkerOptions outputOptions) throws IOException {
12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (DEBUG) {
12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.d(TAG, "Loading " + iconResource.toString());
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Object drawable = loadDrawable(context, iconResource);
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (drawable instanceof InputStream) {
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                // Most of these are bitmaps, so resize properly.
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return decodeBitmap((InputStream)drawable, outputOptions);
13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (drawable instanceof Drawable){
13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return createIconBitmap((Drawable) drawable, outputOptions);
13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.w(TAG, "getBitmapFromResource failed, unrecognized resource: " + drawable);
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return null;
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (NameNotFoundException e) {
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.w(TAG, "Could not load package: " + iconResource.packageName + "! NameNotFound");
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (NotFoundException e) {
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.w(TAG, "Could not load resource: " + iconResource.resourceName + "! NotFound");
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public final boolean isScaled() {
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mScaled;
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Scales the bitmap if either of the dimensions is out of range.
15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Bitmap scaleBitmapIfNecessary(BitmapWorkerOptions outputOptions, Bitmap bitmap) {
15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (bitmap == null) {
16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        float heightScale = 1f;
16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        {
16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (bitmap.getHeight() > outputOptions.getHeight()) {
16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                heightScale = (float) outputOptions.getHeight() / (float) bitmap.getHeight();
16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        float widthScale = 1f;
17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        {
17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (bitmap.getWidth() > outputOptions.getWidth()) {
17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                widthScale = (float) outputOptions.getWidth() / (float) bitmap.getWidth();
17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        float scale = heightScale < widthScale ? heightScale : widthScale;
17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (scale >= 1.0f) {
17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return bitmap;
17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int width = (int) (bitmap.getWidth() * scale);
18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int height = (int) (bitmap.getHeight() * scale);
18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (DEBUG) {
18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.d(TAG, "Scaling bitmap " + ((outputOptions.getResourceUri() != null)
18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                        ? outputOptions.getResourceUri().toString()
18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                        : outputOptions.getIconResource().toString()) + " from "
18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                + bitmap.getWidth() + "x" + bitmap.getHeight() + " to " + width
18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                + "x" + height + " scale " + scale);
18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Bitmap newBitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mScaled = true;
19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return newBitmap;
19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Bitmap decodeBitmap(InputStream in, BitmapWorkerOptions options)
19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throws IOException {
19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        CachedInputStream bufferedStream = null;
19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        BitmapFactory.Options bitmapOptions = null;
19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream = new CachedInputStream(in);
20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // Let the bufferedStream be able to mark unlimited bytes up to full stream length.
20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // The value that BitmapFactory uses (1024) is too small for detecting bounds
20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream.setOverrideMarkLimit(Integer.MAX_VALUE);
20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bitmapOptions = new BitmapFactory.Options();
20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bitmapOptions.inJustDecodeBounds = true;
20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (options.getBitmapConfig() != null) {
20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmapOptions.inPreferredConfig = options.getBitmapConfig();
20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bitmapOptions.inTempStorage = ByteArrayPool.get16KBPool().allocateChunk();
21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream.mark(Integer.MAX_VALUE);
21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            BitmapFactory.decodeStream(bufferedStream, null, bitmapOptions);
21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float heightScale = 1f;
21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            {
21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                int height = options.getHeight();
21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (height > 0) {
21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    heightScale = (float) bitmapOptions.outHeight / height;
21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float widthScale = 1f;
22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            {
22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                int width = options.getWidth();
22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (width > 0) {
22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    widthScale = (float) bitmapOptions.outWidth / width;
22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            float scale = heightScale > widthScale ? heightScale : widthScale;
23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (DEBUG) {
23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.d("BitmapWorkerTask", "Source bitmap: (" + bitmapOptions.outWidth + "x"
23365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        + bitmapOptions.outHeight + ").  Max size: (" + options.getWidth() + "x"
23465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        + options.getHeight() + ").  Chosen scale: " + scale + " -> "
23565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        + (int) scale);
23665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bitmapOptions.inJustDecodeBounds = false;
23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (scale >= 2) {
24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmapOptions.inSampleSize = (int) scale;
24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // Reset buffer to original position and disable the overrideMarkLimit
24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream.reset();
24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream.setOverrideMarkLimit(0);
24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return scaleBitmapIfNecessary(options,
24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    BitmapFactory.decodeStream(bufferedStream, null,
24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            bitmapOptions));
24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } finally {
25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (bitmapOptions != null) {
25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                ByteArrayPool.get16KBPool().releaseChunk(bitmapOptions.inTempStorage);
25265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (bufferedStream != null) {
25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bufferedStream.close();
25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Bitmap getBitmapFromHttp(BitmapWorkerOptions options) throws IOException {
26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        URL url = new URL(options.getResourceUri().toString());
26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (DEBUG) {
26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.d(TAG, "Loading " + url);
26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            URLConnection connection = url.openConnection();
26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            connection.setConnectTimeout(SOCKET_TIMEOUT);
26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            connection.setReadTimeout(READ_TIMEOUT);
26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            InputStream in = new BufferedInputStream(connection.getInputStream());
26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return decodeBitmap(in, options);
27065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } finally {
27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (DEBUG) {
27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.d(TAG, "loading done "+url);
27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Bitmap getBitmapFromContent(BitmapWorkerOptions options) throws IOException {
27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        InputStream bitmapStream =
27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                options.getContext().getContentResolver().openInputStream(options.getResourceUri());
28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (bitmapStream != null) {
28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return decodeBitmap(bitmapStream, options);
28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.w(TAG, "Content provider returned a null InputStream when trying to " +
28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    "open resource.");
28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
28665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * load drawable for non-bitmap resource or InputStream for bitmap resource without
29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * caching Bitmap in Resources.  So that caller can maintain a different caching
29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * storage with less memory used.
29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @return  either {@link Drawable} for xml and ColorDrawable <br>
29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     *          or {@link InputStream} for Bitmap resource
29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static Object loadDrawable(Context context, ShortcutIconResource r)
29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throws NameNotFoundException {
29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Resources resources = context.getPackageManager()
29965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                .getResourcesForApplication(r.packageName);
30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (resources == null) {
30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final int id = resources.getIdentifier(r.resourceName, null, null);
30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (id == 0) {
30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.e(TAG, "Couldn't get resource " + r.resourceName + " in resources of "
30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    + r.packageName);
30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
30965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        TypedValue value = new TypedValue();
31065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        resources.getValue(id, value, true);
31165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if ((value.type == TypedValue.TYPE_STRING && value.string.toString().endsWith(".xml")) || (
31265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                value.type >= TypedValue.TYPE_FIRST_COLOR_INT
31365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                && value.type <= TypedValue.TYPE_LAST_COLOR_INT)) {
31465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return resources.getDrawable(id);
31565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
31665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return resources.openRawResource(id, value);
31765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
31865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
31965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static Bitmap createIconBitmap(Drawable drawable, BitmapWorkerOptions workerOptions) {
32065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        // Some drawables have an intrinsic width and height of -1. In that case
32165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        // size it to our output.
32265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int width = drawable.getIntrinsicWidth();
32365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (width == -1) {
32465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            width = workerOptions.getWidth();
32565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
32665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int height = drawable.getIntrinsicHeight();
32765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (height == -1) {
32865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            height = workerOptions.getHeight();
32965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
33065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
33165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Canvas canvas = new Canvas(bitmap);
33265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        drawable.setBounds(0, 0, drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight());
33365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        drawable.draw(canvas);
33465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return bitmap;
33565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
33665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
33765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static Drawable getDrawable(Context context, ShortcutIconResource iconResource)
33865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throws NameNotFoundException {
33965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Resources resources =
34065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                context.getPackageManager().getResourcesForApplication(iconResource.packageName);
34165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int id = resources.getIdentifier(iconResource.resourceName, null, null);
34265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (id == 0) {
34365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throw new NameNotFoundException();
34465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
34565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return resources.getDrawable(id);
34665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
34765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
34865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Bitmap getAccountImage(BitmapWorkerOptions options) {
34965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String accountName = UriUtils.getAccountName(options.getResourceUri());
35065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Context context = options.getContext();
35165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
35265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (accountName != null && context != null) {
35365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Account thisAccount = null;
35465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            for (Account account : AccountManager.get(context).
35565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    getAccountsByType(GOOGLE_ACCOUNT_TYPE)) {
35665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (account.name.equals(accountName)) {
35765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    thisAccount = account;
35865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    break;
35965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
36065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
36165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (thisAccount != null) {
36265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                String picUriString = AccountImageHelper.getAccountPictureUri(context, thisAccount);
36365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (picUriString != null) {
36465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    BitmapWorkerOptions.Builder optionBuilder =
36565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            new BitmapWorkerOptions.Builder(context)
36665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            .width(options.getWidth())
36765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    .height(options.getHeight())
36865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    .cacheFlag(options.getCacheFlag())
36965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    .bitmapConfig(options.getBitmapConfig())
37065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    .resource(Uri.parse(picUriString));
37165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return BitmapDownloader.getInstance(context)
37265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            .loadBitmapBlocking(optionBuilder.build());
37365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
37465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return null;
37565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
37665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
37765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
37865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
37965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
380