/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ package org.mozilla.gecko.util; import android.content.Context; import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.net.Uri; import android.support.annotation.DrawableRes; import android.support.annotation.NonNull; import android.support.v7.widget.AppCompatDrawableManager; import android.text.TextUtils; import android.util.Log; import org.mozilla.gecko.util.GeckoJarReader; import org.mozilla.gecko.util.ThreadUtils; import org.mozilla.gecko.util.UIAsyncTask; import java.io.InputStream; import java.net.URL; import static org.mozilla.gecko.gfx.BitmapUtils.getBitmapFromDataURI; import static org.mozilla.gecko.gfx.BitmapUtils.getResource; public class ResourceDrawableUtils { private static final String LOGTAG = "ResourceDrawableUtils"; public static Drawable getDrawable(@NonNull final Context context, @DrawableRes final int drawableID) { // TODO: upgrade this call to use AppCompatResources when upgrading to support library >= 24.2 // https://developer.android.com/reference/android/support/v7/content/res/AppCompatResources.html#getDrawable(android.content.Context,%20int) return AppCompatDrawableManager.get().getDrawable(context, drawableID); } public interface BitmapLoader { public void onBitmapFound(Drawable d); } public static void runOnBitmapFoundOnUiThread(final BitmapLoader loader, final Drawable d) { if (ThreadUtils.isOnUiThread()) { loader.onBitmapFound(d); return; } ThreadUtils.postToUiThread(new Runnable() { @Override public void run() { loader.onBitmapFound(d); } }); } /** * Attempts to find a drawable associated with a given string, using its URI scheme to determine * how to load the drawable. The BitmapLoader's `onBitmapFound` method is always called, and * will be called with `null` if no drawable is found. * * The BitmapLoader `onBitmapFound` method always runs on the UI thread. */ public static void getDrawable(final Context context, final String data, final BitmapLoader loader) { if (TextUtils.isEmpty(data)) { runOnBitmapFoundOnUiThread(loader, null); return; } if (data.startsWith("data")) { final BitmapDrawable d = new BitmapDrawable(context.getResources(), getBitmapFromDataURI(data)); runOnBitmapFoundOnUiThread(loader, d); return; } if (data.startsWith("jar:") || data.startsWith("file://")) { (new UIAsyncTask.WithoutParams(ThreadUtils.getBackgroundHandler()) { @Override public Drawable doInBackground() { try { if (data.startsWith("jar:jar")) { return GeckoJarReader.getBitmapDrawable( context, context.getResources(), data); } // Don't attempt to validate the JAR signature when loading an add-on icon if (data.startsWith("jar:file")) { return GeckoJarReader.getBitmapDrawable( context, context.getResources(), Uri.decode(data)); } final URL url = new URL(data); final InputStream is = (InputStream) url.getContent(); try { return Drawable.createFromStream(is, "src"); } finally { is.close(); } } catch (Exception e) { Log.w(LOGTAG, "Unable to set icon", e); } return null; } @Override public void onPostExecute(Drawable drawable) { loader.onBitmapFound(drawable); } }).execute(); return; } if (data.startsWith("-moz-icon://")) { final Uri imageUri = Uri.parse(data); final String ssp = imageUri.getSchemeSpecificPart(); final String resource = ssp.substring(ssp.lastIndexOf('/') + 1); try { final Drawable d = context.getPackageManager().getApplicationIcon(resource); runOnBitmapFoundOnUiThread(loader, d); } catch (Exception ex) { } return; } if (data.startsWith("drawable://")) { final Uri imageUri = Uri.parse(data); final int id = getResource(context, imageUri); final Drawable d = getDrawable(context, id); runOnBitmapFoundOnUiThread(loader, d); return; } runOnBitmapFoundOnUiThread(loader, null); } }