Home | History | Annotate | Download | only in model
      1 /*
      2  * Copyright (C) 2008 Esmertec AG.
      3  * Copyright (C) 2008 The Android Open Source Project
      4  *
      5  * Licensed under the Apache License, Version 2.0 (the "License");
      6  * you may not use this file except in compliance with the License.
      7  * You may obtain a copy of the License at
      8  *
      9  *      http://www.apache.org/licenses/LICENSE-2.0
     10  *
     11  * Unless required by applicable law or agreed to in writing, software
     12  * distributed under the License is distributed on an "AS IS" BASIS,
     13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     14  * See the License for the specific language governing permissions and
     15  * limitations under the License.
     16  */
     17 
     18 package com.android.mms.model;
     19 
     20 
     21 import com.android.mms.ContentRestrictionException;
     22 import com.android.mms.ExceedMessageSizeException;
     23 import com.android.mms.LogTag;
     24 import com.android.mms.MmsConfig;
     25 import com.android.mms.R;
     26 import com.android.mms.dom.smil.parser.SmilXmlSerializer;
     27 import com.android.mms.layout.LayoutManager;
     28 import com.google.android.mms.ContentType;
     29 import com.google.android.mms.MmsException;
     30 import com.google.android.mms.pdu.GenericPdu;
     31 import com.google.android.mms.pdu.MultimediaMessagePdu;
     32 import com.google.android.mms.pdu.PduBody;
     33 import com.google.android.mms.pdu.PduHeaders;
     34 import com.google.android.mms.pdu.PduPart;
     35 import com.google.android.mms.pdu.PduPersister;
     36 
     37 import org.w3c.dom.NodeList;
     38 import org.w3c.dom.events.EventTarget;
     39 import org.w3c.dom.smil.SMILDocument;
     40 import org.w3c.dom.smil.SMILElement;
     41 import org.w3c.dom.smil.SMILLayoutElement;
     42 import org.w3c.dom.smil.SMILMediaElement;
     43 import org.w3c.dom.smil.SMILParElement;
     44 import org.w3c.dom.smil.SMILRegionElement;
     45 import org.w3c.dom.smil.SMILRootLayoutElement;
     46 
     47 import android.content.ContentUris;
     48 import android.content.Context;
     49 import android.net.Uri;
     50 import android.text.TextUtils;
     51 import android.util.Log;
     52 import android.widget.Toast;
     53 
     54 import java.io.ByteArrayOutputStream;
     55 import java.io.IOException;
     56 import java.util.ArrayList;
     57 import java.util.Collection;
     58 import java.util.Iterator;
     59 import java.util.List;
     60 import java.util.ListIterator;
     61 
     62 public class SlideshowModel extends Model
     63         implements List<SlideModel>, IModelChangedObserver {
     64     private static final String TAG = "Mms/slideshow";
     65 
     66     private final LayoutModel mLayout;
     67     private final ArrayList<SlideModel> mSlides;
     68     private SMILDocument mDocumentCache;
     69     private PduBody mPduBodyCache;
     70     private int mCurrentMessageSize;    // This is the current message size, not including
     71                                         // attachments that can be resized (such as photos)
     72     private int mTotalMessageSize;      // This is the computed total message size
     73     private Context mContext;
     74 
     75     // amount of space to leave in a slideshow for text and overhead.
     76     public static final int SLIDESHOW_SLOP = 1024;
     77 
     78     private SlideshowModel(Context context) {
     79         mLayout = new LayoutModel();
     80         mSlides = new ArrayList<SlideModel>();
     81         mContext = context;
     82     }
     83 
     84     private SlideshowModel (
     85             LayoutModel layouts, ArrayList<SlideModel> slides,
     86             SMILDocument documentCache, PduBody pbCache,
     87             Context context) {
     88         mLayout = layouts;
     89         mSlides = slides;
     90         mContext = context;
     91 
     92         mDocumentCache = documentCache;
     93         mPduBodyCache = pbCache;
     94         for (SlideModel slide : mSlides) {
     95             increaseMessageSize(slide.getSlideSize());
     96             slide.setParent(this);
     97         }
     98     }
     99 
    100     public static SlideshowModel createNew(Context context) {
    101         return new SlideshowModel(context);
    102     }
    103 
    104     public static SlideshowModel createFromMessageUri(
    105             Context context, Uri uri) throws MmsException {
    106         return createFromPduBody(context, getPduBody(context, uri));
    107     }
    108 
    109     public static SlideshowModel createFromPduBody(Context context, PduBody pb) throws MmsException {
    110         SMILDocument document = SmilHelper.getDocument(pb);
    111 
    112         // Create root-layout model.
    113         SMILLayoutElement sle = document.getLayout();
    114         SMILRootLayoutElement srle = sle.getRootLayout();
    115         int w = srle.getWidth();
    116         int h = srle.getHeight();
    117         if ((w == 0) || (h == 0)) {
    118             w = LayoutManager.getInstance().getLayoutParameters().getWidth();
    119             h = LayoutManager.getInstance().getLayoutParameters().getHeight();
    120             srle.setWidth(w);
    121             srle.setHeight(h);
    122         }
    123         RegionModel rootLayout = new RegionModel(
    124                 null, 0, 0, w, h);
    125 
    126         // Create region models.
    127         ArrayList<RegionModel> regions = new ArrayList<RegionModel>();
    128         NodeList nlRegions = sle.getRegions();
    129         int regionsNum = nlRegions.getLength();
    130 
    131         for (int i = 0; i < regionsNum; i++) {
    132             SMILRegionElement sre = (SMILRegionElement) nlRegions.item(i);
    133             RegionModel r = new RegionModel(sre.getId(), sre.getFit(),
    134                     sre.getLeft(), sre.getTop(), sre.getWidth(), sre.getHeight(),
    135                     sre.getBackgroundColor());
    136             regions.add(r);
    137         }
    138         LayoutModel layouts = new LayoutModel(rootLayout, regions);
    139 
    140         // Create slide models.
    141         SMILElement docBody = document.getBody();
    142         NodeList slideNodes = docBody.getChildNodes();
    143         int slidesNum = slideNodes.getLength();
    144         ArrayList<SlideModel> slides = new ArrayList<SlideModel>(slidesNum);
    145         int totalMessageSize = 0;
    146 
    147         for (int i = 0; i < slidesNum; i++) {
    148             // FIXME: This is NOT compatible with the SMILDocument which is
    149             // generated by some other mobile phones.
    150             SMILParElement par = (SMILParElement) slideNodes.item(i);
    151 
    152             // Create media models for each slide.
    153             NodeList mediaNodes = par.getChildNodes();
    154             int mediaNum = mediaNodes.getLength();
    155             ArrayList<MediaModel> mediaSet = new ArrayList<MediaModel>(mediaNum);
    156 
    157             for (int j = 0; j < mediaNum; j++) {
    158                 SMILMediaElement sme = (SMILMediaElement) mediaNodes.item(j);
    159                 try {
    160                     MediaModel media = MediaModelFactory.getMediaModel(
    161                             context, sme, layouts, pb);
    162 
    163                     /*
    164                     * This is for slide duration value set.
    165                     * If mms server does not support slide duration.
    166                     */
    167                     if (!MmsConfig.getSlideDurationEnabled()) {
    168                         int mediadur = media.getDuration();
    169                         float dur = par.getDur();
    170                         if (dur == 0) {
    171                             mediadur = MmsConfig.getMinimumSlideElementDuration() * 1000;
    172                             media.setDuration(mediadur);
    173                         }
    174 
    175                         if ((int)mediadur / 1000 != dur) {
    176                             String tag = sme.getTagName();
    177 
    178                             if (ContentType.isVideoType(media.mContentType)
    179                               || tag.equals(SmilHelper.ELEMENT_TAG_VIDEO)
    180                               || ContentType.isAudioType(media.mContentType)
    181                               || tag.equals(SmilHelper.ELEMENT_TAG_AUDIO)) {
    182                                 /*
    183                                 * add 1 sec to release and close audio/video
    184                                 * for guaranteeing the audio/video playing.
    185                                 * because the mmsc does not support the slide duration.
    186                                 */
    187                                 par.setDur((float)mediadur / 1000 + 1);
    188                             } else {
    189                                 /*
    190                                 * If a slide has an image and an audio/video element
    191                                 * and the audio/video element has longer duration than the image,
    192                                 * The Image disappear before the slide play done. so have to match
    193                                 * an image duration to the slide duration.
    194                                 */
    195                                 if ((int)mediadur / 1000 < dur) {
    196                                     media.setDuration((int)dur * 1000);
    197                                 } else {
    198                                     if ((int)dur != 0) {
    199                                         media.setDuration((int)dur * 1000);
    200                                     } else {
    201                                         par.setDur((float)mediadur / 1000);
    202                                     }
    203                                 }
    204                             }
    205                         }
    206                     }
    207                     SmilHelper.addMediaElementEventListeners(
    208                             (EventTarget) sme, media);
    209                     mediaSet.add(media);
    210                     totalMessageSize += media.getMediaSize();
    211                 } catch (IOException e) {
    212                     Log.e(TAG, e.getMessage(), e);
    213                 } catch (IllegalArgumentException e) {
    214                     Log.e(TAG, e.getMessage(), e);
    215                 }
    216             }
    217 
    218             SlideModel slide = new SlideModel((int) (par.getDur() * 1000), mediaSet);
    219             slide.setFill(par.getFill());
    220             SmilHelper.addParElementEventListeners((EventTarget) par, slide);
    221             slides.add(slide);
    222         }
    223 
    224         SlideshowModel slideshow = new SlideshowModel(layouts, slides, document, pb, context);
    225         slideshow.mTotalMessageSize = totalMessageSize;
    226         slideshow.registerModelChangedObserver(slideshow);
    227         return slideshow;
    228     }
    229 
    230     public PduBody toPduBody() {
    231         if (mPduBodyCache == null) {
    232             mDocumentCache = SmilHelper.getDocument(this);
    233             mPduBodyCache = makePduBody(mDocumentCache);
    234         }
    235         return mPduBodyCache;
    236     }
    237 
    238     private PduBody makePduBody(SMILDocument document) {
    239         PduBody pb = new PduBody();
    240 
    241         boolean hasForwardLock = false;
    242         for (SlideModel slide : mSlides) {
    243             for (MediaModel media : slide) {
    244                 PduPart part = new PduPart();
    245 
    246                 if (media.isText()) {
    247                     TextModel text = (TextModel) media;
    248                     // Don't create empty text part.
    249                     if (TextUtils.isEmpty(text.getText())) {
    250                         continue;
    251                     }
    252                     // Set Charset if it's a text media.
    253                     part.setCharset(text.getCharset());
    254                 }
    255 
    256                 // Set Content-Type.
    257                 part.setContentType(media.getContentType().getBytes());
    258 
    259                 String src = media.getSrc();
    260                 String location;
    261                 boolean startWithContentId = src.startsWith("cid:");
    262                 if (startWithContentId) {
    263                     location = src.substring("cid:".length());
    264                 } else {
    265                     location = src;
    266                 }
    267 
    268                 // Set Content-Location.
    269                 part.setContentLocation(location.getBytes());
    270 
    271                 // Set Content-Id.
    272                 if (startWithContentId) {
    273                     //Keep the original Content-Id.
    274                     part.setContentId(location.getBytes());
    275                 }
    276                 else {
    277                     int index = location.lastIndexOf(".");
    278                     String contentId = (index == -1) ? location
    279                             : location.substring(0, index);
    280                     part.setContentId(contentId.getBytes());
    281                 }
    282 
    283                 if (media.isText()) {
    284                     part.setData(((TextModel) media).getText().getBytes());
    285                 } else if (media.isImage() || media.isVideo() || media.isAudio()) {
    286                     part.setDataUri(media.getUri());
    287                 } else {
    288                     Log.w(TAG, "Unsupport media: " + media);
    289                 }
    290 
    291                 pb.addPart(part);
    292             }
    293         }
    294 
    295         // Create and insert SMIL part(as the first part) into the PduBody.
    296         ByteArrayOutputStream out = new ByteArrayOutputStream();
    297         SmilXmlSerializer.serialize(document, out);
    298         PduPart smilPart = new PduPart();
    299         smilPart.setContentId("smil".getBytes());
    300         smilPart.setContentLocation("smil.xml".getBytes());
    301         smilPart.setContentType(ContentType.APP_SMIL.getBytes());
    302         smilPart.setData(out.toByteArray());
    303         pb.addPart(0, smilPart);
    304 
    305         return pb;
    306     }
    307 
    308     public PduBody makeCopy() {
    309         return makePduBody(SmilHelper.getDocument(this));
    310     }
    311 
    312     public SMILDocument toSmilDocument() {
    313         if (mDocumentCache == null) {
    314             mDocumentCache = SmilHelper.getDocument(this);
    315         }
    316         return mDocumentCache;
    317     }
    318 
    319     public static PduBody getPduBody(Context context, Uri msg) throws MmsException {
    320         PduPersister p = PduPersister.getPduPersister(context);
    321         GenericPdu pdu = p.load(msg);
    322 
    323         int msgType = pdu.getMessageType();
    324         if ((msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)
    325                 || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)) {
    326             return ((MultimediaMessagePdu) pdu).getBody();
    327         } else {
    328             throw new MmsException();
    329         }
    330     }
    331 
    332     public void setCurrentMessageSize(int size) {
    333         mCurrentMessageSize = size;
    334     }
    335 
    336     // getCurrentMessageSize returns the size of the message, not including resizable attachments
    337     // such as photos. mCurrentMessageSize is used when adding/deleting/replacing non-resizable
    338     // attachments (movies, sounds, etc) in order to compute how much size is left in the message.
    339     // The difference between mCurrentMessageSize and the maxSize allowed for a message is then
    340     // divided up between the remaining resizable attachments. While this function is public,
    341     // it is only used internally between various MMS classes. If the UI wants to know the
    342     // size of a MMS message, it should call getTotalMessageSize() instead.
    343     public int getCurrentMessageSize() {
    344         return mCurrentMessageSize;
    345     }
    346 
    347     // getTotalMessageSize returns the total size of the message, including resizable attachments
    348     // such as photos. This function is intended to be used by the UI for displaying the size of the
    349     // MMS message.
    350     public int getTotalMessageSize() {
    351         return mTotalMessageSize;
    352     }
    353 
    354     public void increaseMessageSize(int increaseSize) {
    355         if (increaseSize > 0) {
    356             mCurrentMessageSize += increaseSize;
    357         }
    358     }
    359 
    360     public void decreaseMessageSize(int decreaseSize) {
    361         if (decreaseSize > 0) {
    362             mCurrentMessageSize -= decreaseSize;
    363         }
    364     }
    365 
    366     public LayoutModel getLayout() {
    367         return mLayout;
    368     }
    369 
    370     //
    371     // Implement List<E> interface.
    372     //
    373     public boolean add(SlideModel object) {
    374         int increaseSize = object.getSlideSize();
    375         checkMessageSize(increaseSize);
    376 
    377         if ((object != null) && mSlides.add(object)) {
    378             increaseMessageSize(increaseSize);
    379             object.registerModelChangedObserver(this);
    380             for (IModelChangedObserver observer : mModelChangedObservers) {
    381                 object.registerModelChangedObserver(observer);
    382             }
    383             notifyModelChanged(true);
    384             return true;
    385         }
    386         return false;
    387     }
    388 
    389     public boolean addAll(Collection<? extends SlideModel> collection) {
    390         throw new UnsupportedOperationException("Operation not supported.");
    391     }
    392 
    393     public void clear() {
    394         if (mSlides.size() > 0) {
    395             for (SlideModel slide : mSlides) {
    396                 slide.unregisterModelChangedObserver(this);
    397                 for (IModelChangedObserver observer : mModelChangedObservers) {
    398                     slide.unregisterModelChangedObserver(observer);
    399                 }
    400             }
    401             mCurrentMessageSize = 0;
    402             mSlides.clear();
    403             notifyModelChanged(true);
    404         }
    405     }
    406 
    407     public boolean contains(Object object) {
    408         return mSlides.contains(object);
    409     }
    410 
    411     public boolean containsAll(Collection<?> collection) {
    412         return mSlides.containsAll(collection);
    413     }
    414 
    415     public boolean isEmpty() {
    416         return mSlides.isEmpty();
    417     }
    418 
    419     public Iterator<SlideModel> iterator() {
    420         return mSlides.iterator();
    421     }
    422 
    423     public boolean remove(Object object) {
    424         if ((object != null) && mSlides.remove(object)) {
    425             SlideModel slide = (SlideModel) object;
    426             decreaseMessageSize(slide.getSlideSize());
    427             slide.unregisterAllModelChangedObservers();
    428             notifyModelChanged(true);
    429             return true;
    430         }
    431         return false;
    432     }
    433 
    434     public boolean removeAll(Collection<?> collection) {
    435         throw new UnsupportedOperationException("Operation not supported.");
    436     }
    437 
    438     public boolean retainAll(Collection<?> collection) {
    439         throw new UnsupportedOperationException("Operation not supported.");
    440     }
    441 
    442     public int size() {
    443         return mSlides.size();
    444     }
    445 
    446     public Object[] toArray() {
    447         return mSlides.toArray();
    448     }
    449 
    450     public <T> T[] toArray(T[] array) {
    451         return mSlides.toArray(array);
    452     }
    453 
    454     public void add(int location, SlideModel object) {
    455         if (object != null) {
    456             int increaseSize = object.getSlideSize();
    457             checkMessageSize(increaseSize);
    458 
    459             mSlides.add(location, object);
    460             increaseMessageSize(increaseSize);
    461             object.registerModelChangedObserver(this);
    462             for (IModelChangedObserver observer : mModelChangedObservers) {
    463                 object.registerModelChangedObserver(observer);
    464             }
    465             notifyModelChanged(true);
    466         }
    467     }
    468 
    469     public boolean addAll(int location,
    470             Collection<? extends SlideModel> collection) {
    471         throw new UnsupportedOperationException("Operation not supported.");
    472     }
    473 
    474     public SlideModel get(int location) {
    475         return (location >= 0 && location < mSlides.size()) ? mSlides.get(location) : null;
    476     }
    477 
    478     public int indexOf(Object object) {
    479         return mSlides.indexOf(object);
    480     }
    481 
    482     public int lastIndexOf(Object object) {
    483         return mSlides.lastIndexOf(object);
    484     }
    485 
    486     public ListIterator<SlideModel> listIterator() {
    487         return mSlides.listIterator();
    488     }
    489 
    490     public ListIterator<SlideModel> listIterator(int location) {
    491         return mSlides.listIterator(location);
    492     }
    493 
    494     public SlideModel remove(int location) {
    495         SlideModel slide = mSlides.remove(location);
    496         if (slide != null) {
    497             decreaseMessageSize(slide.getSlideSize());
    498             slide.unregisterAllModelChangedObservers();
    499             notifyModelChanged(true);
    500         }
    501         return slide;
    502     }
    503 
    504     public SlideModel set(int location, SlideModel object) {
    505         SlideModel slide = mSlides.get(location);
    506         if (null != object) {
    507             int removeSize = 0;
    508             int addSize = object.getSlideSize();
    509             if (null != slide) {
    510                 removeSize = slide.getSlideSize();
    511             }
    512             if (addSize > removeSize) {
    513                 checkMessageSize(addSize - removeSize);
    514                 increaseMessageSize(addSize - removeSize);
    515             } else {
    516                 decreaseMessageSize(removeSize - addSize);
    517             }
    518         }
    519 
    520         slide =  mSlides.set(location, object);
    521         if (slide != null) {
    522             slide.unregisterAllModelChangedObservers();
    523         }
    524 
    525         if (object != null) {
    526             object.registerModelChangedObserver(this);
    527             for (IModelChangedObserver observer : mModelChangedObservers) {
    528                 object.registerModelChangedObserver(observer);
    529             }
    530         }
    531 
    532         notifyModelChanged(true);
    533         return slide;
    534     }
    535 
    536     public List<SlideModel> subList(int start, int end) {
    537         return mSlides.subList(start, end);
    538     }
    539 
    540     @Override
    541     protected void registerModelChangedObserverInDescendants(
    542             IModelChangedObserver observer) {
    543         mLayout.registerModelChangedObserver(observer);
    544 
    545         for (SlideModel slide : mSlides) {
    546             slide.registerModelChangedObserver(observer);
    547         }
    548     }
    549 
    550     @Override
    551     protected void unregisterModelChangedObserverInDescendants(
    552             IModelChangedObserver observer) {
    553         mLayout.unregisterModelChangedObserver(observer);
    554 
    555         for (SlideModel slide : mSlides) {
    556             slide.unregisterModelChangedObserver(observer);
    557         }
    558     }
    559 
    560     @Override
    561     protected void unregisterAllModelChangedObserversInDescendants() {
    562         mLayout.unregisterAllModelChangedObservers();
    563 
    564         for (SlideModel slide : mSlides) {
    565             slide.unregisterAllModelChangedObservers();
    566         }
    567     }
    568 
    569     public void onModelChanged(Model model, boolean dataChanged) {
    570         if (dataChanged) {
    571             mDocumentCache = null;
    572             mPduBodyCache = null;
    573         }
    574     }
    575 
    576     public void sync(PduBody pb) {
    577         for (SlideModel slide : mSlides) {
    578             for (MediaModel media : slide) {
    579                 PduPart part = pb.getPartByContentLocation(media.getSrc());
    580                 if (part != null) {
    581                     media.setUri(part.getDataUri());
    582                 }
    583             }
    584         }
    585     }
    586 
    587     public void checkMessageSize(int increaseSize) throws ContentRestrictionException {
    588         ContentRestriction cr = ContentRestrictionFactory.getContentRestriction();
    589         cr.checkMessageSize(mCurrentMessageSize, increaseSize, mContext.getContentResolver());
    590     }
    591 
    592     /**
    593      * Determines whether this is a "simple" slideshow.
    594      * Criteria:
    595      * - Exactly one slide
    596      * - Exactly one multimedia attachment, but no audio
    597      * - It can optionally have a caption
    598     */
    599     public boolean isSimple() {
    600         // There must be one (and only one) slide.
    601         if (size() != 1)
    602             return false;
    603 
    604         SlideModel slide = get(0);
    605         // The slide must have either an image or video, but not both.
    606         if (!(slide.hasImage() ^ slide.hasVideo()))
    607             return false;
    608 
    609         // No audio allowed.
    610         if (slide.hasAudio())
    611             return false;
    612 
    613         return true;
    614     }
    615 
    616     /**
    617      * Make sure the text in slide 0 is no longer holding onto a reference to the text
    618      * in the message text box.
    619      */
    620     public void prepareForSend() {
    621         if (size() == 1) {
    622             TextModel text = get(0).getText();
    623             if (text != null) {
    624                 text.cloneText();
    625             }
    626         }
    627     }
    628 
    629     /**
    630      * Resize all the resizeable media objects to fit in the remaining size of the slideshow.
    631      * This should be called off of the UI thread.
    632      *
    633      * @throws MmsException, ExceedMessageSizeException
    634      */
    635     public void finalResize(Uri messageUri) throws MmsException, ExceedMessageSizeException {
    636 
    637         // Figure out if we have any media items that need to be resized and total up the
    638         // sizes of the items that can't be resized.
    639         int resizableCnt = 0;
    640         int fixedSizeTotal = 0;
    641         for (SlideModel slide : mSlides) {
    642             for (MediaModel media : slide) {
    643                 if (media.getMediaResizable()) {
    644                     ++resizableCnt;
    645                 } else {
    646                     fixedSizeTotal += media.getMediaSize();
    647                 }
    648             }
    649         }
    650         if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
    651             Log.v(TAG, "finalResize: original message size: " + getCurrentMessageSize() +
    652                     " getMaxMessageSize: " + MmsConfig.getMaxMessageSize() +
    653                     " fixedSizeTotal: " + fixedSizeTotal);
    654         }
    655         if (resizableCnt > 0) {
    656             int remainingSize = MmsConfig.getMaxMessageSize() - fixedSizeTotal - SLIDESHOW_SLOP;
    657             if (remainingSize <= 0) {
    658                 throw new ExceedMessageSizeException("No room for pictures");
    659             }
    660             long messageId = ContentUris.parseId(messageUri);
    661             int bytesPerMediaItem = remainingSize / resizableCnt;
    662             // Resize the resizable media items to fit within their byte limit.
    663             for (SlideModel slide : mSlides) {
    664                 for (MediaModel media : slide) {
    665                     if (media.getMediaResizable()) {
    666                         media.resizeMedia(bytesPerMediaItem, messageId);
    667                     }
    668                 }
    669             }
    670             // One last time through to calc the real message size.
    671             int totalSize = 0;
    672             for (SlideModel slide : mSlides) {
    673                 for (MediaModel media : slide) {
    674                     totalSize += media.getMediaSize();
    675                 }
    676             }
    677             if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
    678                 Log.v(TAG, "finalResize: new message size: " + totalSize);
    679             }
    680 
    681             if (totalSize > MmsConfig.getMaxMessageSize()) {
    682                 throw new ExceedMessageSizeException("After compressing pictures, message too big");
    683             }
    684             setCurrentMessageSize(totalSize);
    685 
    686             onModelChanged(this, true);     // clear the cached pdu body
    687             PduBody pb = toPduBody();
    688             // This will write out all the new parts to:
    689             //      /data/data/com.android.providers.telephony/app_parts
    690             // and at the same time delete the old parts.
    691             PduPersister.getPduPersister(mContext).updateParts(messageUri, pb);
    692         }
    693     }
    694 
    695 }
    696