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