/* * 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(); } }