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