package gws.grottworkshop.gwsholmeswatson.view; import gws.grottworkshop.gwsholmeswatson.GWSHolmesWatson; import android.annotation.SuppressLint; import android.content.Context; import android.content.res.TypedArray; import android.graphics.Bitmap; import android.graphics.drawable.AnimationDrawable; import android.graphics.drawable.Drawable; import android.os.Message; import android.util.AttributeSet; import android.view.Gravity; import android.widget.ProgressBar; import android.widget.ViewSwitcher; // TODO: Auto-generated Javadoc /** * The Class RemoteImageView. */ public class RemoteImageView extends ViewSwitcher { /** The Constant DEFAULT_ERROR_DRAWABLE_RES_ID. */ public static final int DEFAULT_ERROR_DRAWABLE_RES_ID = android.R.drawable.ic_dialog_alert; /** The Constant ATTR_AUTO_LOAD. */ private static final String ATTR_AUTO_LOAD = "autoLoad"; /** The Constant ATTR_IMAGE_URL. */ private static final String ATTR_IMAGE_URL = "imageUrl"; /** The Constant ATTR_ERROR_DRAWABLE. */ private static final String ATTR_ERROR_DRAWABLE = "errorDrawable"; /** The Constant ANDROID_VIEW_ATTRS. */ private static final int[] ANDROID_VIEW_ATTRS = { android.R.attr.indeterminateDrawable }; /** The Constant ATTR_INDET_DRAWABLE. */ private static final int ATTR_INDET_DRAWABLE = 0; /** The image url. */ private String imageUrl; /** The is loaded. */ private boolean autoLoad, isLoaded; /** The loading spinner. */ private ProgressBar loadingSpinner; /** The image view. */ private GWSGestureCacheableImageView imageView; /** The error drawable. */ private Drawable progressDrawable, errorDrawable; /** The image loader. */ private RemoteImageLoader imageLoader; /** The shared image loader. */ private static RemoteImageLoader sharedImageLoader; /** * Use this method to inject an image loader that will be shared across all instances of this * class. If the shared reference is null, a new {@link RemoteImageLoader} will be instantiated * for every instance of this class. * * @param imageLoader * the shared image loader */ public static void setSharedImageLoader(RemoteImageLoader imageLoader) { sharedImageLoader = imageLoader; } /** * Instantiates a new remote image view. * * @param context the view's current context * @param imageUrl the URL of the image to download and show * @param autoLoad Whether the download should start immediately after creating the view. If set to * false, use {@link #loadImage()} to manually trigger the image download. */ public RemoteImageView(Context context, String imageUrl, boolean autoLoad) { super(context); initialize(context, imageUrl, null, null, autoLoad, null); } /** * Instantiates a new remote image view. * * @param context the view's current context * @param imageUrl the URL of the image to download and show * @param progressDrawable the drawable to be used for the {@link ProgressBar} which is displayed while the * image is loading * @param errorDrawable the drawable to be used if a download error occurs * @param autoLoad Whether the download should start immediately after creating the view. If set to * false, use {@link #loadImage()} to manually trigger the image download. */ public RemoteImageView(Context context, String imageUrl, Drawable progressDrawable, Drawable errorDrawable, boolean autoLoad) { super(context); initialize(context, imageUrl, progressDrawable, errorDrawable, autoLoad, null); } /** * Instantiates a new remote image view. * * @param context the context * @param attributes the attributes */ public RemoteImageView(Context context, AttributeSet attributes) { super(context, attributes); // Read all Android specific view attributes into a typed array first. // These are attributes that are specific to RemoteImageView, but which are not in the // ignition XML namespace. TypedArray imageViewAttrs = context.getTheme().obtainStyledAttributes(attributes, ANDROID_VIEW_ATTRS, 0, 0); int progressDrawableId = imageViewAttrs.getResourceId(ATTR_INDET_DRAWABLE, 0); imageViewAttrs.recycle(); int errorDrawableId = attributes.getAttributeResourceValue(GWSHolmesWatson.XMLNS, ATTR_ERROR_DRAWABLE, DEFAULT_ERROR_DRAWABLE_RES_ID); Drawable errorDrawable = context.getResources().getDrawable(errorDrawableId); Drawable progressDrawable = null; if (progressDrawableId > 0) { progressDrawable = context.getResources().getDrawable(progressDrawableId); } String imageUrl = attributes.getAttributeValue(GWSHolmesWatson.XMLNS, ATTR_IMAGE_URL); boolean autoLoad = attributes .getAttributeBooleanValue(GWSHolmesWatson.XMLNS, ATTR_AUTO_LOAD, true); initialize(context, imageUrl, progressDrawable, errorDrawable, autoLoad, attributes); } /** * Initialize. * * @param context the context * @param imageUrl the image url * @param progressDrawable the progress drawable * @param errorDrawable the error drawable * @param autoLoad the auto load * @param attributes the attributes */ private void initialize(Context context, String imageUrl, Drawable progressDrawable, Drawable errorDrawable, boolean autoLoad, AttributeSet attributes) { this.imageUrl = imageUrl; this.autoLoad = autoLoad; this.progressDrawable = progressDrawable; this.errorDrawable = errorDrawable; if (sharedImageLoader == null) { this.imageLoader = new RemoteImageLoader(context); } else { this.imageLoader = sharedImageLoader; } // ScaleAnimation anim = new ScaleAnimation(0.0f, 1.0f, 0.0f, 1.0f, // 125.0f, preferredItemHeight / 2.0f); // anim.setDuration(500L); // AlphaAnimation anim = new AlphaAnimation(0.0f, 1.0f); // anim.setDuration(500L); // setInAnimation(anim); addLoadingSpinnerView(context); addImageView(context, attributes); if (autoLoad && imageUrl != null) { loadImage(); } else { // if we don't have anything to load yet, don't show the progress element setDisplayedChild(1); } } /** * Adds the loading spinner view. * * @param context the context */ private void addLoadingSpinnerView(Context context) { loadingSpinner = new ProgressBar(context); loadingSpinner.setIndeterminate(true); if (this.progressDrawable == null) { this.progressDrawable = loadingSpinner.getIndeterminateDrawable(); } else { loadingSpinner.setIndeterminateDrawable(progressDrawable); if (progressDrawable instanceof AnimationDrawable) { ((AnimationDrawable) progressDrawable).start(); } } LayoutParams lp = new LayoutParams(progressDrawable.getIntrinsicWidth(), progressDrawable.getIntrinsicHeight()); lp.gravity = Gravity.CENTER; addView(loadingSpinner, 0, lp); } /** * Adds the image view. * * @param context the context * @param attributes the attributes */ private void addImageView(Context context, AttributeSet attributes) { if (attributes != null) { // pass along any view attribtues inflated from XML to the image view imageView = new GWSGestureCacheableImageView(context, attributes); } else { imageView = new GWSGestureCacheableImageView(context); } LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); lp.gravity = Gravity.CENTER; addView(imageView, 1, lp); } /** * Use this method to trigger the image download if you had previously set autoLoad to false. */ public void loadImage() { if (imageUrl == null) { throw new IllegalStateException( "image URL is null; did you forget to set it for this view?"); } setDisplayedChild(0); imageLoader.loadImage(imageUrl, imageView, new DefaultImageLoaderHandler()); } /** * Checks if is loaded. * * @return true, if is loaded */ public boolean isLoaded() { return isLoaded; } /** * Sets the image url. * * @param imageUrl the new image url */ public void setImageUrl(String imageUrl) { this.imageUrl = imageUrl; } /** * Often you have resources which usually have an image, but some don't. For these cases, use * this method to supply a placeholder drawable which will be loaded instead of a web image. * * @param imageResourceId * the resource of the placeholder image drawable */ public void setNoImageDrawable(int imageResourceId) { imageView.setImageDrawable(getContext().getResources().getDrawable(imageResourceId)); setDisplayedChild(1); } /** * Reset. * * @see android.widget.ViewSwitcher#reset() */ @Override public void reset() { super.reset(); this.setDisplayedChild(0); } /** * The Class DefaultImageLoaderHandler. */ @SuppressLint("HandlerLeak") private class DefaultImageLoaderHandler extends RemoteImageLoaderHandler { /** * Instantiates a new default image loader handler. */ public DefaultImageLoaderHandler() { super(imageView, imageUrl, errorDrawable); } /** * Handle image loaded. * * @param bitmap the bitmap * @param msg the msg * @return true, if successful * @see gws.grottworkshop.gwsholmeswatson.view.RemoteImageLoaderHandler#handleImageLoaded(android.graphics.Bitmap, android.os.Message) */ @Override protected boolean handleImageLoaded(Bitmap bitmap, Message msg) { boolean wasUpdated = super.handleImageLoaded(bitmap, msg); if (wasUpdated) { isLoaded = true; setDisplayedChild(1); } return wasUpdated; } } /** * Returns the URL of the image to show. Corresponds to the view attribute ignition:imageUrl. * * @return the image URL */ public String getImageUrl() { return imageUrl; } /** * Whether or not the image should be downloaded immediately after view inflation. Corresponds * to the view attribute ignition:autoLoad (default: true). * * @return true if auto downloading of the image is enabled */ public boolean isAutoLoad() { return autoLoad; } /** * The drawable that should be used to indicate progress while downloading the image. * Corresponds to the view attribute ignition:progressDrawable. If left blank, the platform's * standard indeterminate progress drawable will be used. * * @return the progress drawable */ public Drawable getProgressDrawable() { return progressDrawable; } /** * The drawable that will be shown when the image download fails. Corresponds to the view * attribute ignition:errorDrawable. If left blank, a stock alert icon from the Android platform * will be used. * * @return the error drawable */ public Drawable getErrorDrawable() { return errorDrawable; } /** * The image view that will render the downloaded image. * * @return the {@link ImageView} */ public GWSGestureCacheableImageView getImageView() { return imageView; } /** * The progress bar that is shown while the image is loaded. * * @return the {@link ProgressBar} */ public ProgressBar getProgressBar() { return loadingSpinner; } }