Android Showing Remote Image on ImageView of App-Widget ListView


This is followup tutorial of my previous tutorials on Android AppWidget ListView.

  1. Android app widget with ListView
  2. Populate AppWidget ListView with remote data(data from web)


In this part of tutorial,I am going to explain on how to download images from server and show those images on Android AppWidget ListView's ImageView. As I stated earlier on my AppWidget ListView tutorial,it is better to use Database rather than static ArrayList. So ,here first of all I am going to implement a Database,where I will store all the data fetched from the web. Before going down to code, I will explain the logic that will be implemented in this tutorial.

  1. First fetch the content of json file
  2. Save the fetched content into database
  3. Download images from web
  4. Save the image Bitmap on file
  5. Populate appwidget listview with data from Database and image from File


Let’s first create our Database which holds content,heading,image url of our remote json file. Further,this Database of ours will also hold the file path where our images will be saved.Let’s create SQListOpenHelper class which will define our Database and table structure. Let’s name it

public class DatabaseHelper extends SQLiteOpenHelper {

	public static final String WIDGET_TABLE = "widgetContentTable",
			WIDGET_ID = "widgetId", HEADING = "heading", CONTENT = "content",
			IMAGE_REMOTE_URL = "imageUrl", IMAGE_FILE_URL = "fileUrl";
	public static final int DB_VERSION = 1;
	public static final String DB_NAME = "listViewWidgetDB";

	public static final String CREATE_TABLE_QUERY = "create table if not exists "
			+ WIDGET_TABLE
			+ " (_id integer primary key autoincrement,"
			+ WIDGET_ID
			+ " text, "
			+ HEADING
			+ " text, "
			+ CONTENT
			+ " text, "
			+ IMAGE_REMOTE_URL + " text," + IMAGE_FILE_URL + " text);";

	public DatabaseHelper(Context context) {
		super(context, DB_NAME, null, DB_VERSION);
	}

	@Override
	public void onCreate(SQLiteDatabase db) {
		db.execSQL(CREATE_TABLE_QUERY);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		db.execSQL("drop table if exists " + WIDGET_TABLE);
		onCreate(db);
	}

}


The DatabaseHelper class is pretty self-explanatory to those who have already worked on Android SQLite. Moving on,let’s create a class which manages actions like adding,retrieving,updating database record and let’s name it

/**
 * A singleton class of manage all the Database related work
 */
public enum DatabaseManager {
	INSTANCE;

	private SQLiteDatabase db;
	private boolean isDbClosed = true;

	/**
	 * Initializng SQListDatabase with help of our SQLiteOpenHelper class
	 * DatabaseHelper
	 */
	public void init(Context context) {
		if (context != null && isDbClosed) {
			isDbClosed = false;
			DatabaseHelper dbHelper = new DatabaseHelper(context);
			db = dbHelper.getWritableDatabase();
		}

	}

	/**
	 * Closing already opened writable database
	 */
	public void closeDatabase() {
		if (!isDbClosed && db != null) {
			isDbClosed = true;
			db.close();
		}
	}

	/**
	 * Knowing the opened or closed state of writable database
	 */
	public boolean isDbClosed() {
		return isDbClosed;
	}

	/**
	 * Storing the fetched and parsed content of Json file on database
	 */
	public void storeListItems(int widgetId, ArrayList<ListItem> listItems) {
		String widgetIdValue = Integer.toString(widgetId);
		db.delete(DatabaseHelper.WIDGET_TABLE, DatabaseHelper.WIDGET_ID + "=?",
				new String[] { widgetIdValue });
		for (ListItem listItem : listItems) {
			ContentValues cv = new ContentValues();
			cv.put(DatabaseHelper.WIDGET_ID, widgetIdValue);
			cv.put(DatabaseHelper.HEADING, listItem.heading);
			cv.put(DatabaseHelper.CONTENT, listItem.content);
			cv.put(DatabaseHelper.IMAGE_REMOTE_URL, listItem.imageUrl);
			Log.i("listtem image url @ databaseManger", listItem.imageUrl);
			db.insert(DatabaseHelper.WIDGET_TABLE, null, cv);
		}
	}

	/**
	 * 
	 * Fetching the stored items with given widget id
	 */
	public ArrayList<ListItem> getStoredListItems(int widgetId) {
		String widgetIdValue = Integer.toString(widgetId);
		Cursor cursor = db.query(DatabaseHelper.WIDGET_TABLE, null,
				DatabaseHelper.WIDGET_ID + "=?",
				new String[] { widgetIdValue }, null, null, null);
		ArrayList<ListItem> listItems = new ArrayList<ListItem>();
		while (cursor.moveToNext()) {
			ListItem listItem = new ListItem();
			listItem.heading = cursor.getString(cursor
					.getColumnIndex(DatabaseHelper.HEADING));
			listItem.content = cursor.getString(cursor
					.getColumnIndex(DatabaseHelper.CONTENT));
			listItem.imageUrl = cursor.getString(cursor
					.getColumnIndex(DatabaseHelper.IMAGE_REMOTE_URL));
			listItem.fileUrl = cursor.getString(cursor
					.getColumnIndex(DatabaseHelper.IMAGE_FILE_URL));
			listItems.add(listItem);
		}

		cursor.close();
		return listItems.size() == 0 ? null : listItems;
	}

	/**
	 * Once Image have been downloaded and saved on file then update the file
	 * path of saved image to database row
	 * 
	 */
	public boolean updateImageFilePath(int widgetId, String filePath,
			String heading) {
		ContentValues cv = new ContentValues();
		cv.put(DatabaseHelper.IMAGE_FILE_URL, filePath);
		return db.update(DatabaseHelper.WIDGET_TABLE, cv,
				DatabaseHelper.WIDGET_ID + "=? and " + DatabaseHelper.HEADING
						+ "=?", new String[] { Integer.toString(widgetId),
						heading }) > 0;
	}
}


Moving on,we need to create a class which handles file writing operation i.e. using the Bitmap of downloaded images and converting them or writing them to jpeg images. This class is

/**
 * A singleton class to manage file operation
 */
public enum FileManager {
    INSTANCE;
    
	String directoryName = Environment.getExternalStorageDirectory()
			+ "/appwidgetlistview/";

    /**
     * creating a file or initializing a file
     * here I haven't checked sdcard availability
     * so while doing any file operation,better to check
     * sdcard state first
     */
    public void init(String fileName){
    	File file = new File(directoryName+fileName+"/");
    	if(!file.exists())
    		file.mkdirs();
    }
    
    /**
     * Making a jpeg file with downloaded bitmap
     * and giving the database the image path to be updated on
     * its equivalent record
     */
    public void storeBitmap(int appWidgetId,Bitmap bitmap,String heading,Context context){
    	init(Integer.toString(appWidgetId));
    	String fileName = directoryName+Integer.toString(appWidgetId)+"/"+heading+".jpg";
    	try {
			FileOutputStream fileOutputStream =new FileOutputStream(fileName);
			bitmap.compress(Bitmap.CompressFormat.JPEG, 90, fileOutputStream);
			fileOutputStream.close();
			fileOutputStream.flush();
			DatabaseManager dbManager = DatabaseManager.INSTANCE;
			dbManager.init(context);
			dbManager.updateImageFilePath(appWidgetId, fileName, heading);
    	} catch (FileNotFoundException e) {
			e.printStackTrace();
		}catch(Exception e){
			e.printStackTrace();
		}
    }
}


FileManager not only saves images on file but also updates the database record with the saved image path. Now we need to modify our RemoteService.java class to accommodate function like storing json content to database,downloading images and saving them to file. I will just point out the added portion on the code

	/**
	 * Json parsing of result and populating ArrayList<ListItem> as per json
	 * data retrieved from the string
	 */
	private void processResult(String result) {
		Log.i("Resutl", result);
		listItemList = new ArrayList<ListItem>();
		try {
			JSONArray jsonArray = new JSONArray(result);
			int length = jsonArray.length();
			for (int i = 0; i < length; i++) {
				JSONObject jsonObject = jsonArray.getJSONObject(i);
				ListItem listItem = new ListItem();
				listItem.heading = jsonObject.getString("heading");
				listItem.content = jsonObject.getString("content");
				listItem.imageUrl = jsonObject.getString("imageUrl");
				listItemList.add(listItem);
			}
			storeListItem();

		} catch (JSONException e) {
			e.printStackTrace();
		}
	}

	/**
	 * Instead of using static ArrayList as we have used before,no we rely upon
	 * data stored on database so saving the fetched json file content into
	 * database and at same time downloading the image from web as well
	 */
	private void storeListItem() {
		DatabaseManager dbManager = DatabaseManager.INSTANCE;
		dbManager.init(getBaseContext());
		dbManager.storeListItems(appWidgetId, listItemList);

		int length = listItemList.size();
		for (int i = 0; i < length; i++) {
			ListItem listItem = listItemList.get(i);
			final int index = i;
			aquery.ajax(listItem.imageUrl, Bitmap.class,
					new AjaxCallback<Bitmap>() {
						@Override
						public void callback(String url, Bitmap bitmap,
								AjaxStatus status) {
							super.callback(url, bitmap, status);
							storeBitmap(index, bitmap);
						};
					});
		}
	}

	/**
	 * Saving the downloaded images into file and after all the download of
	 * images be complete begin to populate widget as done previously
	 */
	private void storeBitmap(int index, Bitmap bitmap) {
		FileManager.INSTANCE.storeBitmap(appWidgetId, bitmap,
				listItemList.get(index).heading, getBaseContext());
		count++;
		Log.i("count",
				String.valueOf(count) + "::"
						+ Integer.toString(listItemList.size()));
		if (count == listItemList.size()) {
			count = 0;
			populateWidget();
		}

	}


All above procedure is done to save content on database and file. Now, instead of using static ArrayList as we had done previously,we will fetch the content from database and update or populate the appwidget listview with those contents.We need to modify our WidgetService.java and ListProvider.java classes a little. On our WidgetService class,we fetch the data from database and supply those data to our ListProvider class.

public class WidgetService extends RemoteViewsService {
	/*
	 * So pretty simple just defining the Adapter of the listview here Adapter
	 * is ListProvider and fetching the stored data from Database and providing
	 * it to ListProvider
	 */

	@Override
	public RemoteViewsFactory onGetViewFactory(Intent intent) {
		int appWidgetId = intent.getIntExtra(
				AppWidgetManager.EXTRA_APPWIDGET_ID,
				AppWidgetManager.INVALID_APPWIDGET_ID);
		DatabaseManager dbManager = DatabaseManager.INSTANCE;
		dbManager.init(getBaseContext());
		return (new ListProvider(this.getApplicationContext(), intent,
				dbManager.getStoredListItems(appWidgetId)));
	}

}


And moving on to ListProvider class,we simply use the imagepath to show images on ImageView of appwidget listview .

public ListProvider(Context context, Intent intent,
			ArrayList<ListItem> listItems) {
		this.context = context;
		appWidgetId = intent.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID,
				AppWidgetManager.INVALID_APPWIDGET_ID);
		if (listItems != null)
			this.listItemList = (ArrayList<ListItem>) listItems.clone();
	}
/*
	 * Similar to getView of Adapter where instead of View return RemoteViews
	 * And using the saved ImagePath to show images on ImageView
	 */
	@Override
	public RemoteViews getViewAt(int position) {
		final RemoteViews remoteView = new RemoteViews(
				context.getPackageName(), R.layout.list_row);
		ListItem listItem = listItemList.get(position);
		remoteView.setTextViewText(R.id.heading, listItem.heading);
		remoteView.setTextViewText(R.id.content, listItem.content);
		Bitmap bitmap = BitmapFactory.decodeFile(listItem.fileUrl);
		if (bitmap != null)
			remoteView.setImageViewBitmap(R.id.imageView, bitmap);

		return remoteView;
	}


This is all one have to do in order to show downloaded images on appwidget listview's imageview.Further this tutorial is just a barebone implementation on how to download content(images or other) and populate them on appwidget listview. There may be better ways to do also and if anyone has done so in efficient manner please share us also.
Finally all the source code can be downloaded from my github. Till next time,enjoy life.

Advertisements

3 thoughts on “Android Showing Remote Image on ImageView of App-Widget ListView

  1. Pingback: Android Setting Update Interval On Appwidget With Listview | Laaptu
  2. Pingback: Android update app widget with ListView after phone reboot | Laaptu

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s