/*
* Copyright (C) 2011 Virginia Tech Department of Computer Science
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package sofia.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Handler;
import android.os.Looper;
import android.util.AttributeSet;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
//-------------------------------------------------------------------------
/**
* A subclass of {@link android.widget.ImageView} that can load images from
* HTTP/HTTPS URIs as well as resource and content resolver URIs.
*
* @author Tony Allevato
*/
public class ImageView extends android.widget.ImageView
{
//~ Fields ................................................................
private Uri imageURI;
private boolean loaded;
private static final Logger log = LoggerFactory.getLogger(ImageView.class);
//~ Constructors ..........................................................
// ----------------------------------------------------------
public ImageView(Context context)
{
super(context);
}
// ----------------------------------------------------------
public ImageView(Context context, AttributeSet attrs)
{
super(context, attrs);
}
// ----------------------------------------------------------
public ImageView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
}
//~ Public methods ........................................................
// ----------------------------------------------------------
/**
* Gets the image URI that was last set to this view. This will return null
* if the image was set through a method other than
* {@link #setImageURI(Uri)}.
*
* @return the image URI
*/
public Uri getImageURI()
{
return imageURI;
}
// ----------------------------------------------------------
/**
* Sets the content of this ImageView to the specified Uri.
*
* @param uri the Uri of an image
*/
public void setImageURI(Uri uri)
{
// FIXME Possible race conditions!
this.imageURI = uri;
loaded = false;
if (uri != null)
{
String scheme = uri.getScheme();
if ("http".equalsIgnoreCase(scheme)
|| "https".equalsIgnoreCase(scheme))
{
new AsyncImageLoader().execute();
return;
}
else
{
loaded = true;
super.setImageURI(uri);
}
}
else
{
loaded = true;
setImageBitmap(null);
}
}
// ----------------------------------------------------------
@Override
public void setImageBitmap(Bitmap bm)
{
imageURI = null;
super.setImageBitmap(bm);
}
// ----------------------------------------------------------
@Override
public void setImageDrawable(Drawable drawable)
{
imageURI = null;
super.setImageDrawable(drawable);
}
// ----------------------------------------------------------
@Override
public void setImageResource(int resId)
{
imageURI = null;
super.setImageResource(resId);
}
// ----------------------------------------------------------
public boolean isLoaded()
{
// FIXME Possible race conditions!
return loaded;
}
//~ Inner classes .........................................................
// ----------------------------------------------------------
/**
* FIXME Rapid fire calls to setImageURI might cause this to behave oddly;
* it should probably cancel any pending requests and only use the last
* one.
*/
private class AsyncImageLoader extends AsyncTask<Void, Void, Void>
{
//~ Fields ............................................................
private Handler handler;
//~ Constructors ......................................................
// ----------------------------------------------------------
public AsyncImageLoader()
{
handler = new Handler(Looper.getMainLooper());
}
//~ Methods ...........................................................
// ----------------------------------------------------------
@Override
protected Void doInBackground(Void... arg0)
{
final Uri uri = getImageURI();
final Bitmap bitmap = bitmapFromURI(uri);
handler.post(new Runnable() {
@Override
public void run()
{
// FIXME Possible race conditions!
loaded = true;
setImageBitmap(bitmap);
imageURI = uri;
}
});
return null;
}
// ----------------------------------------------------------
private Bitmap bitmapFromURI(Uri uri)
{
Bitmap bm = null;
InputStream is = null;
BufferedInputStream bis = null;
try
{
URLConnection conn = new URL(uri.toString()).openConnection();
conn.connect();
is = conn.getInputStream();
bis = new BufferedInputStream(is, 8192);
bm = BitmapFactory.decodeStream(bis);
}
catch (Exception e)
{
log.error("Error loading bitmap from URI", e);
}
finally
{
if (bis != null)
{
try
{
bis.close();
}
catch (IOException e)
{
// Do nothing.
}
}
if (is != null)
{
try
{
is.close();
}
catch (IOException e)
{
e.printStackTrace();
}
}
}
return bm;
}
}
}