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