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.drawable.Drawable;
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.net.Uri;
3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.AsyncTask;
3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.Log;
3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.util.TypedValue;
3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.widget.ImageView;
3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.util.AccountImageHelper;
3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.util.ByteArrayPool;
3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.util.CachedInputStream;
3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport com.android.tv.settings.util.UriUtils;
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.FileNotFoundException;
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.IOException;
4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.io.InputStream;
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.lang.ref.WeakReference;
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.net.SocketTimeoutException;
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 Laneclass DrawableLoader extends AsyncTask<BitmapWorkerOptions, Void, Drawable> {
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static final String TAG = "DrawableLoader";
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    private int mOriginalWidth;
6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private int mOriginalHeight;
696e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler    private final RecycleBitmapPool mRecycledBitmaps;
7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
716e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler    private final RefcountObject.RefcountListener mRefcountListener =
7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            new RefcountObject.RefcountListener() {
7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public void onRefcountZero(RefcountObject object) {
7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mRecycledBitmaps.addRecycledBitmap((Bitmap) object.getObject());
7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    };
7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    DrawableLoader(ImageView imageView, RecycleBitmapPool recycledBitmapPool) {
816e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler        mImageView = new WeakReference<>(imageView);
8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mRecycledBitmaps = recycledBitmapPool;
8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public int getOriginalWidth() {
8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mOriginalWidth;
8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public int getOriginalHeight() {
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mOriginalHeight;
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected Drawable doInBackground(BitmapWorkerOptions... params) {
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return retrieveDrawable(params[0]);
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected Drawable retrieveDrawable(BitmapWorkerOptions workerOptions) {
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (workerOptions.getIconResource() != null) {
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return getBitmapFromResource(workerOptions.getIconResource(), workerOptions);
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (workerOptions.getResourceUri() != null) {
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (UriUtils.isAndroidResourceUri(workerOptions.getResourceUri())
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        || UriUtils.isShortcutIconResourceUri(workerOptions.getResourceUri())) {
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    // Make an icon resource from this.
10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return getBitmapFromResource(
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            UriUtils.getIconResource(workerOptions.getResourceUri()),
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            workerOptions);
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else if (UriUtils.isWebUri(workerOptions.getResourceUri())) {
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return getBitmapFromHttp(workerOptions);
11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else if (UriUtils.isContentUri(workerOptions.getResourceUri())) {
11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return getBitmapFromContent(workerOptions);
11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else if (UriUtils.isAccountImageUri(workerOptions.getResourceUri())) {
11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return getAccountImage(workerOptions);
11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    Log.e(TAG, "Error loading bitmap - unknown resource URI! "
11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            + workerOptions.getResourceUri());
11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.e(TAG, "Error loading bitmap - no source!");
12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (IOException e) {
12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.e(TAG, "Error loading url " + workerOptions.getResourceUri(), e);
12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (RuntimeException e) {
12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.e(TAG, "Critical Error loading url " + workerOptions.getResourceUri(), e);
12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected void onPostExecute(Drawable bitmap) {
1366e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler        final ImageView imageView = mImageView.get();
1376e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler        if (imageView != null) {
1386e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler            imageView.setImageDrawable(bitmap);
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    protected void onCancelled(Drawable result) {
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (result instanceof RefcountBitmapDrawable) {
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // Remove the extra refcount created by us,  DrawableDownloader LruCache
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // still holds one to the bitmap
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            RefcountBitmapDrawable d = (RefcountBitmapDrawable) result;
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            d.getRefcountObject().releaseRef();
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Drawable getBitmapFromResource(ShortcutIconResource iconResource,
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            BitmapWorkerOptions outputOptions) throws IOException {
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (DEBUG) {
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.d(TAG, "Loading " + iconResource.toString());
15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Object drawable = loadDrawable(outputOptions.getContext(), iconResource);
15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (drawable instanceof InputStream) {
16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                // Most of these are bitmaps, so resize properly.
16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return decodeBitmap((InputStream)drawable, outputOptions);
16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else if (drawable instanceof Drawable){
16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Drawable d = (Drawable) drawable;
16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mOriginalWidth = d.getIntrinsicWidth();
16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                mOriginalHeight = d.getIntrinsicHeight();
16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return d;
16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.w(TAG, "getBitmapFromResource failed, unrecognized resource: " + drawable);
16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return null;
17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (NameNotFoundException e) {
17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.w(TAG, "Could not load package: " + iconResource.packageName + "! NameNotFound");
17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (NotFoundException e) {
17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.w(TAG, "Could not load resource: " + iconResource.resourceName + "! NotFound");
17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
17965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
18065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Drawable decodeBitmap(InputStream in, BitmapWorkerOptions options)
18165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throws IOException {
18265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        CachedInputStream bufferedStream = null;
18365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        BitmapFactory.Options bitmapOptions = null;
18465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
18565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream = new CachedInputStream(in);
18665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // Let the bufferedStream be able to mark unlimited bytes up to full stream length.
18765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // The value that BitmapFactory uses (1024) is too small for detecting bounds
18865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream.setOverrideMarkLimit(Integer.MAX_VALUE);
18965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bitmapOptions = new BitmapFactory.Options();
19065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bitmapOptions.inJustDecodeBounds = true;
19165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (options.getBitmapConfig() != null) {
19265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmapOptions.inPreferredConfig = options.getBitmapConfig();
19365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
19465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bitmapOptions.inTempStorage = ByteArrayPool.get16KBPool().allocateChunk();
19565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream.mark(Integer.MAX_VALUE);
19665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            BitmapFactory.decodeStream(bufferedStream, null, bitmapOptions);
19765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
19865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mOriginalWidth = bitmapOptions.outWidth;
19965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            mOriginalHeight = bitmapOptions.outHeight;
20065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int heightScale = 1;
20165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int height = options.getHeight();
20265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (height > 0) {
20365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                heightScale = bitmapOptions.outHeight / height;
20465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
20565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
20665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int widthScale = 1;
20765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int width = options.getWidth();
20865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (width > 0) {
20965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                widthScale = bitmapOptions.outWidth / width;
21065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
21165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
21265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int scale = heightScale > widthScale ? heightScale : widthScale;
21365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (scale <= 1) {
21465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                scale = 1;
21565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } else {
21665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                int shift = 0;
21765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                do {
21865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    scale >>= 1;
21965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    shift++;
22065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } while (scale != 0);
22165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                scale = 1 << (shift - 1);
22265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
22365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
22465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (DEBUG) {
22565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.d("BitmapWorkerTask", "Source bitmap: (" + bitmapOptions.outWidth + "x"
22665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        + bitmapOptions.outHeight + ").  Max size: (" + options.getWidth() + "x"
22765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        + options.getHeight() + ").  Chosen scale: " + scale + " -> " + scale);
22865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
22965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
23065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // Reset buffer to original position and disable the overrideMarkLimit
23165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream.reset();
23265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            bufferedStream.setOverrideMarkLimit(0);
2336e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler            Bitmap bitmap;
23465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            try {
23565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmapOptions.inJustDecodeBounds = false;
23665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmapOptions.inSampleSize = scale;
23765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmapOptions.inMutable = true;
23865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmapOptions.inBitmap = mRecycledBitmaps.getRecycledBitmap(
23965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        mOriginalWidth / scale, mOriginalHeight / scale);
24065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmap = BitmapFactory.decodeStream(bufferedStream, null, bitmapOptions);
24165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } catch (RuntimeException ex) {
24265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.e(TAG, "RuntimeException" + ex + ", trying decodeStream again");
24365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bufferedStream.reset();
24465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bufferedStream.setOverrideMarkLimit(0);
24565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmapOptions.inBitmap = null;
24665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bitmap = BitmapFactory.decodeStream(bufferedStream, null, bitmapOptions);
24765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
24865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (bitmap == null) {
24965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.d(TAG, "bitmap was null");
25065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return null;
25165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
2526e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler            RefcountObject<Bitmap> object = new RefcountObject<>(bitmap);
25365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            object.addRef();
25465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            object.setRefcountListener(mRefcountListener);
25565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            RefcountBitmapDrawable d = new RefcountBitmapDrawable(
25665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    options.getContext().getResources(), object);
25765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return d;
25865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } finally {
25965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.w(TAG, "couldn't load bitmap, releasing resources");
26065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (bitmapOptions != null) {
26165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                ByteArrayPool.get16KBPool().releaseChunk(bitmapOptions.inTempStorage);
26265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
26365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (bufferedStream != null) {
26465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                bufferedStream.close();
26565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
26665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
26765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
26865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
26965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Drawable getBitmapFromHttp(BitmapWorkerOptions options) throws IOException {
27065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        URL url = new URL(options.getResourceUri().toString());
27165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (DEBUG) {
27265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.d(TAG, "Loading " + url);
27365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
27465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        try {
27565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // TODO use volley for better disk cache
27665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            URLConnection connection = url.openConnection();
27765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            connection.setConnectTimeout(SOCKET_TIMEOUT);
27865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            connection.setReadTimeout(READ_TIMEOUT);
27965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            InputStream in = connection.getInputStream();
28065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return decodeBitmap(in, options);
28165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } catch (SocketTimeoutException e) {
28265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.e(TAG, "loading " + url + " timed out");
28365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
28465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
28565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
28665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
28765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Drawable getBitmapFromContent(BitmapWorkerOptions options)
28865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throws IOException {
28965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Uri resourceUri = options.getResourceUri();
29065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (resourceUri != null) {
29165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            try {
29265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                InputStream bitmapStream =
29365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        options.getContext().getContentResolver().openInputStream(resourceUri);
29465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
29565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (bitmapStream != null) {
29665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return decodeBitmap(bitmapStream, options);
29765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                } else {
29865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    Log.w(TAG, "Content provider returned a null InputStream when trying to " +
29965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            "open resource.");
30065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return null;
30165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
30265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            } catch (FileNotFoundException e) {
30365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                Log.e(TAG, "FileNotFoundException during openInputStream for uri: "
30465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                        + resourceUri.toString());
30565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return null;
30665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
30765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        } else {
30865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.w(TAG, "Get null resourceUri from BitmapWorkerOptions.");
30965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
31065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
31165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
31265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
31365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
31465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * load drawable for non-bitmap resource or InputStream for bitmap resource without
31565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * caching Bitmap in Resources.  So that caller can maintain a different caching
31665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * storage with less memory used.
31765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @return  either {@link Drawable} for xml and ColorDrawable <br>
31865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     *          or {@link InputStream} for Bitmap resource
31965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
32065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private static Object loadDrawable(Context context, ShortcutIconResource r)
32165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throws NameNotFoundException {
3226e995161147d9110d77ae1fe38b697e52891d3f2Tony Mantler        Resources resources = context.getPackageManager().getResourcesForApplication(r.packageName);
32365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (resources == null) {
32465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
32565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
32665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final int id = resources.getIdentifier(r.resourceName, null, null);
32765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (id == 0) {
32865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Log.e(TAG, "Couldn't get resource " + r.resourceName + " in resources of "
32965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    + r.packageName);
33065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return null;
33165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
33265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        TypedValue value = new TypedValue();
33365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        resources.getValue(id, value, true);
33465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if ((value.type == TypedValue.TYPE_STRING && value.string.toString().endsWith(".xml")) || (
33565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                value.type >= TypedValue.TYPE_FIRST_COLOR_INT
33665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                && value.type <= TypedValue.TYPE_LAST_COLOR_INT)) {
33765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return resources.getDrawable(id);
33865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
33965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return resources.openRawResource(id, value);
34065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
34165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
34265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static Drawable getDrawable(Context context, ShortcutIconResource iconResource)
34365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throws NameNotFoundException {
34465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Resources resources =
34565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                context.getPackageManager().getResourcesForApplication(iconResource.packageName);
34665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        int id = resources.getIdentifier(iconResource.resourceName, null, null);
34765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (id == 0) {
34865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            throw new NameNotFoundException();
34965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
35065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return resources.getDrawable(id);
35165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
35265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
35365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private Drawable getAccountImage(BitmapWorkerOptions options) {
35465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        String accountName = UriUtils.getAccountName(options.getResourceUri());
35565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        Context context = options.getContext();
35665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
35765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (accountName != null && context != null) {
35865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            Account thisAccount = null;
35965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            for (Account account : AccountManager.get(context).
36065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    getAccountsByType(GOOGLE_ACCOUNT_TYPE)) {
36165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (account.name.equals(accountName)) {
36265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    thisAccount = account;
36365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    break;
36465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
36565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
36665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (thisAccount != null) {
36765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                String picUriString = AccountImageHelper.getAccountPictureUri(context, thisAccount);
36865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                if (picUriString != null) {
36965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    BitmapWorkerOptions.Builder optionBuilder =
37065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            new BitmapWorkerOptions.Builder(context)
37165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            .width(options.getWidth())
37265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    .height(options.getHeight())
37365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    .cacheFlag(options.getCacheFlag())
37465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    .bitmapConfig(options.getBitmapConfig())
37565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                                    .resource(Uri.parse(picUriString));
37665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                    return DrawableDownloader.getInstance(context)
37765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                            .loadBitmapBlocking(optionBuilder.build());
37865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                }
37965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return null;
38065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
38165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
38265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return null;
38365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
38465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
385