/*
* Copyright (C) 2012 The Android Open Source Project
*
* 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 com.android.mms.util;
import java.util.Set;
import android.content.Context;
import android.net.Uri;
import android.util.Log;
import com.android.mms.LogTag;
import com.android.mms.model.SlideshowModel;
import com.google.android.mms.MmsException;
import com.google.android.mms.pdu.GenericPdu;
import com.google.android.mms.pdu.MultimediaMessagePdu;
import com.google.android.mms.pdu.PduPersister;
import com.google.android.mms.util.PduCache;
import com.google.android.mms.util.PduCacheEntry;
/**
* Primary {@link PduLoaderManager} implementation used by {@link MessagingApplication}.
* <p>
* Public methods should only be used from a single thread (typically the UI
* thread). Callbacks will be invoked on the thread where the PduLoaderManager
* was instantiated.
* <p>
* Uses a thread-pool ExecutorService instead of AsyncTasks since clients may
* request lots of pdus around the same time, and AsyncTask may reject tasks
* in that case and has no way of bounding the number of threads used by those
* tasks.
* <p>
* PduLoaderManager is used to asynchronously load mms pdu's and then build a slideshow model
* from that loaded pdu. Then it will call the passed in callback with the result. This class
* uses the PduCache built into the mms framework. It also manages a local cache of slideshow
* models. The slideshow cache uses SoftReferences to hang onto the slideshow.
*
* Based on BooksImageManager by Virgil King.
*/
public class PduLoaderManager extends BackgroundLoaderManager {
private static final String TAG = "Mms:PduLoaderManager";
private static final boolean DEBUG_DISABLE_CACHE = false;
private static final boolean DEBUG_DISABLE_PDUS = false;
private static final boolean DEBUG_LONG_WAIT = false;
private static PduCache mPduCache;
private final PduPersister mPduPersister;
private final SimpleCache<Uri, SlideshowModel> mSlideshowCache;
private final Context mContext;
public PduLoaderManager(final Context context) {
super(context);
mSlideshowCache = new SimpleCache<Uri, SlideshowModel>(8, 16, 0.75f, true);
mPduCache = PduCache.getInstance();
mPduPersister = PduPersister.getPduPersister(context);
mContext = context;
}
public ItemLoadedFuture getPdu(Uri uri, boolean requestSlideshow,
final ItemLoadedCallback<PduLoaded> callback) {
if (uri == null) {
throw new NullPointerException();
}
PduCacheEntry cacheEntry = null;
synchronized(mPduCache) {
if (!mPduCache.isUpdating(uri)) {
cacheEntry = mPduCache.get(uri);
}
}
final SlideshowModel slideshow = (requestSlideshow && !DEBUG_DISABLE_CACHE) ?
mSlideshowCache.get(uri) : null;
final boolean slideshowExists = (!requestSlideshow || slideshow != null);
final boolean pduExists = (cacheEntry != null && cacheEntry.getPdu() != null);
final boolean taskExists = mPendingTaskUris.contains(uri);
final boolean newTaskRequired = (!pduExists || !slideshowExists) && !taskExists;
final boolean callbackRequired = (callback != null);
if (pduExists && slideshowExists) {
if (callbackRequired) {
PduLoaded pduLoaded = new PduLoaded(cacheEntry.getPdu(), slideshow);
callback.onItemLoaded(pduLoaded, null);
}
return new NullItemLoadedFuture();
}
if (callbackRequired) {
addCallback(uri, callback);
}
if (newTaskRequired) {
mPendingTaskUris.add(uri);
Runnable task = new PduTask(uri, requestSlideshow);
mExecutor.execute(task);
}
return new ItemLoadedFuture() {
private boolean mIsDone;
public void cancel(Uri uri) {
cancelCallback(callback);
removePdu(uri); // the pdu and/or slideshow might be half loaded. Make sure
// we load fresh the next time this uri is requested.
}
public void setIsDone(boolean done) {
mIsDone = done;
}
public boolean isDone() {
return mIsDone;
}
};
}
@Override
public void clear() {
super.clear();
synchronized(mPduCache) {
mPduCache.purgeAll();
}
mSlideshowCache.clear();
}
public void removePdu(Uri uri) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "removePdu: " + uri);
}
if (uri != null) {
synchronized(mPduCache) {
mPduCache.purge(uri);
}
mSlideshowCache.remove(uri);
}
}
public String getTag() {
return TAG;
}
public class PduTask implements Runnable {
private final Uri mUri;
private final boolean mRequestSlideshow;
public PduTask(Uri uri, boolean requestSlideshow) {
if (uri == null) {
throw new NullPointerException();
}
mUri = uri;
mRequestSlideshow = requestSlideshow;
}
/** {@inheritDoc} */
public void run() {
if (DEBUG_DISABLE_PDUS) {
return;
}
if (DEBUG_LONG_WAIT) {
try {
Thread.sleep(10000);
} catch (InterruptedException e) {
}
}
GenericPdu pdu = null;
SlideshowModel slideshow = null;
Throwable exception = null;
try {
pdu = mPduPersister.load(mUri);
if (pdu != null && mRequestSlideshow) {
slideshow = SlideshowModel.createFromPduBody(mContext,
((MultimediaMessagePdu)pdu).getBody());
}
} catch (final MmsException e) {
Log.e(TAG, "MmsException loading uri: " + mUri, e);
exception = e;
}
final GenericPdu resultPdu = pdu;
final SlideshowModel resultSlideshow = slideshow;
final Throwable resultException = exception;
mCallbackHandler.post(new Runnable() {
public void run() {
final Set<ItemLoadedCallback> callbacks = mCallbacks.get(mUri);
if (callbacks != null) {
// Make a copy so that the callback can unregister itself
for (final ItemLoadedCallback<PduLoaded> callback : asList(callbacks)) {
if (Log.isLoggable(TAG, Log.DEBUG)) {
Log.d(TAG, "Invoking pdu callback " + callback);
}
PduLoaded pduLoaded = new PduLoaded(resultPdu, resultSlideshow);
callback.onItemLoaded(pduLoaded, resultException);
}
}
// Add the slideshow to the soft cache if the load succeeded
if (resultSlideshow != null) {
mSlideshowCache.put(mUri, resultSlideshow);
}
mCallbacks.remove(mUri);
mPendingTaskUris.remove(mUri);
if (Log.isLoggable(LogTag.PDU_CACHE, Log.DEBUG)) {
Log.d(TAG, "Pdu task for " + mUri + "exiting; " + mPendingTaskUris.size()
+ " remain");
}
}
});
}
}
public static class PduLoaded {
public final GenericPdu mPdu;
public final SlideshowModel mSlideshow;
public PduLoaded(GenericPdu pdu, SlideshowModel slideshow) {
mPdu = pdu;
mSlideshow = slideshow;
}
}
}