/*
* CDDL HEADER START
*
* The contents of this file are subject to the terms of the Common Development
* and Distribution License (the "License").
* You may not use this file except in compliance with the License.
*
* You can obtain a copy of the license at
* src/com/vodafone360/people/VODAFONE.LICENSE.txt or
* http://github.com/360/360-Engine-for-Android
* See the License for the specific language governing permissions and
* limitations under the License.
*
* When distributing Covered Code, include this CDDL HEADER in each file and
* include the License file at src/com/vodafone360/people/VODAFONE.LICENSE.txt.
* If applicable, add the following below this CDDL HEADER, with the fields
* enclosed by brackets "[]" replaced with your own identifying information:
* Portions Copyright [yyyy] [name of copyright owner]
*
* CDDL HEADER END
*
* Copyright 2010 Vodafone Sales & Services Ltd. All rights reserved.
* Use is subject to license terms.
*/
package com.vodafone360.people.engine.content;
import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import com.vodafone360.people.database.DatabaseHelper.ThumbnailInfo;
import com.vodafone360.people.engine.EngineManager;
import com.vodafone360.people.engine.content.ContentObject.TransferStatus;
import com.vodafone360.people.utils.LogUtils;
import com.vodafone360.people.utils.ThumbnailUtils;
/**
* ThumbnailHandler is a handler for contents. The ContentEngine uses handlers
* to handle transfers of ContentObjects. The handler implements the
* TransferListener interface and registers itself in the ContentObjects before
* sending them to the ContentEngine. The ContentEngine calls the
* TransferListener on a ContentObject after a transfer completes. The
* ThumbnailHandler handles the fetching of thumbnails for contacts, saving them
* and refreshing the list.
*/
public class ThumbnailHandler implements TransferListener {
/**
* instance of ThumbnailHandler. Used for singleton.
*/
private static ThumbnailHandler mThumbnailHandlerInstance;
/**
* Number of thumbnails to fetch in a single RPG request batch.
*/
private static final int MAX_THUMBS_FETCHED_PER_PAGE = 5;
/**
* Queue with contact IDs to fetch thumbnails for. The contact IDs are queued here
* when the downloadContactThumbnails is called and are then fetched batch
* for batch. The variable MAX_THUMBS_FETCHED_PER_PAGE defines how much are
* processed at one time
*/
private List<Long> mContactsQueue = new LinkedList<Long>();
/**
* List with ContentObjects. Every time a ContentObjects is created for
* downloading it is puted in this list. Every time it is processed, it will
* be removed. When the list is empty, the next batch of Contacts is
* processed.
*/
private List<ContentObject> mContentObjects = new ArrayList<ContentObject>();
/**
* Factory method for creating and getting singleton instance of this
* handler.
*
* @return Instance of Thumbnailhandler
*/
public static synchronized ThumbnailHandler getInstance() {
if (mThumbnailHandlerInstance == null) {
mThumbnailHandlerInstance = new ThumbnailHandler();
}
return mThumbnailHandlerInstance;
}
/**
* Private constructor. The singleton method has to be used to obtain an
* instance.
*/
private ThumbnailHandler() {
}
/**
* Called by ContentEngine when a Transfer is done. It saves the thumbnail
* to file, links it to the contact and refreshes the view.
*
* @param content Transfered ContentObject containing the Thumbnail
*/
@Override
public final void transferComplete(final ContentObject content) {
mContentObjects.remove(content);
Long contactId = (Long) content.getLink();
try {
ThumbnailUtils.saveExternalResponseObjectToFile(contactId, content
.getExternalResponseObject());
ContentEngine contentEngine = EngineManager.getInstance().getContentEngine();
contentEngine.getDatabaseHelper().modifyPictureLoadedFlag(contactId, true);
} catch (IOException e) {
LogUtils.logE("ThumbnailHandler.TransferComplete", e);
}
if (mContentObjects.size() == 0) {
downloadThumbnails(MAX_THUMBS_FETCHED_PER_PAGE);
}
}
/**
* Called when there was an error transfering a ContentObject. It can happen
* when a timeout occurs, the URL is not valid, the server is not responding
* and so on.
*
* @param content The failing ContentObject
* @param exc RuntimeException explaining what happened
*/
@Override
public final void transferError(final ContentObject content, final RuntimeException exc) {
mContentObjects.remove(content);
if (mContentObjects.size() == 0) {
downloadThumbnails(MAX_THUMBS_FETCHED_PER_PAGE);
}
}
/**
* Puts the contactlist in to a queue and starts downloading the thumbnails
* for them.
*/
public final void downloadContactThumbnails() {
List<Long> contactIdList = new ArrayList<Long>();
ContentEngine contentEngine = EngineManager.getInstance().getContentEngine();
contentEngine.getDatabaseHelper().fetchContactIdsWithThumbnails(contactIdList);
for (Long contactId : contactIdList) {
if (!mContactsQueue.contains(contactId)) {
mContactsQueue.add(contactId);
}
}
LogUtils.logI("Downloading " + mContactsQueue.size() + " thumbnails");
downloadThumbnails(MAX_THUMBS_FETCHED_PER_PAGE);
}
/**
* Download the next bunch of contacts from the queue. The method uses the
* ContentEngine to download the thumbnails and sets this class as a handler
*
* @param thumbsPerPage Indicates the number of thumbnails to be downloaded
* in this page
*/
private void downloadThumbnails(final int thumbsPerPage) {
List<Long> contactList = new ArrayList<Long>();
for (int i = 0; i < thumbsPerPage; i++) {
if (mContactsQueue.size() == 0) {
break;
}
contactList.add((Long) mContactsQueue.remove(0));
}
// nothing to do? exit!
if (contactList.size() == 0) {
LogUtils.logI("Thumbnail download finished");
return;
}
// get the contentengine, so we can access the database
ContentEngine contentEngine = EngineManager.getInstance().getContentEngine();
// list for holding the fetched ThumbnailURLs
ArrayList<ThumbnailInfo> thumbnailInfoList = new ArrayList<ThumbnailInfo>();
// fetches the URLs of all thumbnails that are not downloaded by now
contentEngine.getDatabaseHelper().fetchThumbnailUrlsForContacts(thumbnailInfoList,
contactList);
// This list is needed because of following usecase: We have started the
// thumbnail sync. 5 thumbnails are requested and we have got the
// response for 3 of them. At this point, the thumbnail sync starts
// again(maybe because somethign got changed in the server). This
// function gets called again. And if we don't use this temporary
// contentList, those contentObjects for which we haven't got the
// response yet are also added into the queue of the COntent Engine.
List<ContentObject> contentList = new ArrayList<ContentObject>();
// iterate over the given thumbnailInfoList
for (ThumbnailInfo thumbnailInfo : thumbnailInfoList) {
// not every contact has a thumbnail, so continue in this case
if (thumbnailInfo == null) {
continue;
}
try {
// create a ContentObject for downloading the particular
// Thumbnail...
ContentObject contentObject = new ContentObject(null, thumbnailInfo.localContactId, this,
ContentObject.TransferDirection.DOWNLOAD, ContentObject.Protocol.RPG);
// ... set the right URL and params...
contentObject.setUrl(new URL(thumbnailInfo.photoServerUrl));
contentObject.setUrlParams(ThumbnailUtils.REQUEST_THUMBNAIL_URI);
contentObject.setTransferStatus(TransferStatus.INIT);
contentList.add(contentObject);
// ... and put it to the list
mContentObjects.add(contentObject);
} catch (MalformedURLException e) {
LogUtils.logE("ThumbanailHandler.downloadContactThumbnails: "
+ thumbnailInfo.photoServerUrl + " is not a valid URL");
}
}
// if the list is not empty, let the ContentEngine process them
if (mContentObjects.size() > 0) {
contentEngine.processContentObjects(contentList);
}
}
/**
* Dummy method to replace the dummy processor.
*/
public void uploadServerThumbnails() {
}
/**
* Performs a reset.
*/
public void reset() {
mContactsQueue.clear();
mContentObjects.clear();
}
}