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 java.io.ByteArrayOutputStream;
     22 import java.io.FileNotFoundException;
     23 import java.io.IOException;
     24 import java.io.InputStream;
     25 import java.util.ArrayList;
     26 import java.util.Collection;
     27 import java.util.HashMap;
     28 import java.util.Iterator;
     29 import java.util.List;
     30 import java.util.ListIterator;
     31 
     32 import org.w3c.dom.NodeList;
     33 import org.w3c.dom.events.EventTarget;
     34 import org.w3c.dom.smil.SMILDocument;
     35 import org.w3c.dom.smil.SMILElement;
     36 import org.w3c.dom.smil.SMILLayoutElement;
     37 import org.w3c.dom.smil.SMILMediaElement;
     38 import org.w3c.dom.smil.SMILParElement;
     39 import org.w3c.dom.smil.SMILRegionElement;
     40 import org.w3c.dom.smil.SMILRootLayoutElement;
     41 
     42 import android.content.ContentResolver;
     43 import android.content.ContentUris;
     44 import android.content.Context;
     45 import android.net.Uri;
     46 import android.text.TextUtils;
     47 import android.util.Log;
     48 
     49 import com.android.mms.ContentRestrictionException;
     50 import com.android.mms.ExceedMessageSizeException;
     51 import com.android.mms.LogTag;
     52 import com.android.mms.MmsConfig;
     53 import com.android.mms.dom.smil.parser.SmilXmlSerializer;
     54 import com.android.mms.layout.LayoutManager;
     55 import com.google.android.mms.ContentType;
     56 import com.google.android.mms.MmsException;
     57 import com.google.android.mms.pdu.GenericPdu;
     58 import com.google.android.mms.pdu.MultimediaMessagePdu;
     59 import com.google.android.mms.pdu.PduBody;
     60 import com.google.android.mms.pdu.PduHeaders;
     61 import com.google.android.mms.pdu.PduPart;
     62 import com.google.android.mms.pdu.PduPersister;
     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 (IOException e) {
    214                     Log.e(TAG, e.getMessage(), e);
    215                 } catch (IllegalArgumentException e) {
    216                     Log.e(TAG, e.getMessage(), e);
    217                 }
    218             }
    219 
    220             SlideModel slide = new SlideModel((int) (par.getDur() * 1000), mediaSet);
    221             slide.setFill(par.getFill());
    222             SmilHelper.addParElementEventListeners((EventTarget) par, slide);
    223             slides.add(slide);
    224         }
    225 
    226         SlideshowModel slideshow = new SlideshowModel(layouts, slides, document, pb, context);
    227         slideshow.mTotalMessageSize = totalMessageSize;
    228         slideshow.registerModelChangedObserver(slideshow);
    229         return slideshow;
    230     }
    231 
    232     public PduBody toPduBody() {
    233         if (mPduBodyCache == null) {
    234             mDocumentCache = SmilHelper.getDocument(this);
    235             mPduBodyCache = makePduBody(mDocumentCache);
    236         }
    237         return mPduBodyCache;
    238     }
    239 
    240     private PduBody makePduBody(SMILDocument document) {
    241         PduBody pb = new PduBody();
    242 
    243         boolean hasForwardLock = false;
    244         for (SlideModel slide : mSlides) {
    245             for (MediaModel media : slide) {
    246                 PduPart part = new PduPart();
    247 
    248                 if (media.isText()) {
    249                     TextModel text = (TextModel) media;
    250                     // Don't create empty text part.
    251                     if (TextUtils.isEmpty(text.getText())) {
    252                         continue;
    253                     }
    254                     // Set Charset if it's a text media.
    255                     part.setCharset(text.getCharset());
    256                 }
    257 
    258                 // Set Content-Type.
    259                 part.setContentType(media.getContentType().getBytes());
    260 
    261                 String src = media.getSrc();
    262                 String location;
    263                 boolean startWithContentId = src.startsWith("cid:");
    264                 if (startWithContentId) {
    265                     location = src.substring("cid:".length());
    266                 } else {
    267                     location = src;
    268                 }
    269 
    270                 // Set Content-Location.
    271                 part.setContentLocation(location.getBytes());
    272 
    273                 // Set Content-Id.
    274                 if (startWithContentId) {
    275                     //Keep the original Content-Id.
    276                     part.setContentId(location.getBytes());
    277                 }
    278                 else {
    279                     int index = location.lastIndexOf(".");
    280                     String contentId = (index == -1) ? location
    281                             : location.substring(0, index);
    282                     part.setContentId(contentId.getBytes());
    283                 }
    284 
    285                 if (media.isText()) {
    286                     part.setData(((TextModel) media).getText().getBytes());
    287                 } else if (media.isImage() || media.isVideo() || media.isAudio()) {
    288                     part.setDataUri(media.getUri());
    289                 } else {
    290                     Log.w(TAG, "Unsupport media: " + media);
    291                 }
    292 
    293                 pb.addPart(part);
    294             }
    295         }
    296 
    297         // Create and insert SMIL part(as the first part) into the PduBody.
    298         ByteArrayOutputStream out = new ByteArrayOutputStream();
    299         SmilXmlSerializer.serialize(document, out);
    300         PduPart smilPart = new PduPart();
    301         smilPart.setContentId("smil".getBytes());
    302         smilPart.setContentLocation("smil.xml".getBytes());
    303         smilPart.setContentType(ContentType.APP_SMIL.getBytes());
    304         smilPart.setData(out.toByteArray());
    305         pb.addPart(0, smilPart);
    306 
    307         return pb;
    308     }
    309 
    310     public HashMap<Uri, InputStream> openPartFiles(ContentResolver cr) {
    311         HashMap<Uri, InputStream> openedFiles = null;     // Don't create unless we have to
    312 
    313         for (SlideModel slide : mSlides) {
    314             for (MediaModel media : slide) {
    315                 if (media.isText()) {
    316                     continue;
    317                 }
    318                 Uri uri = media.getUri();
    319                 InputStream is;
    320                 try {
    321                     is = cr.openInputStream(uri);
    322                     if (is != null) {
    323                         if (openedFiles == null) {
    324                             openedFiles = new HashMap<Uri, InputStream>();
    325                         }
    326                         openedFiles.put(uri, is);
    327                     }
    328                 } catch (FileNotFoundException e) {
    329                     Log.e(TAG, "openPartFiles couldn't open: " + uri, e);
    330                 }
    331             }
    332         }
    333         return openedFiles;
    334     }
    335 
    336     public PduBody makeCopy() {
    337         return makePduBody(SmilHelper.getDocument(this));
    338     }
    339 
    340     public SMILDocument toSmilDocument() {
    341         if (mDocumentCache == null) {
    342             mDocumentCache = SmilHelper.getDocument(this);
    343         }
    344         return mDocumentCache;
    345     }
    346 
    347     public static PduBody getPduBody(Context context, Uri msg) throws MmsException {
    348         PduPersister p = PduPersister.getPduPersister(context);
    349         GenericPdu pdu = p.load(msg);
    350 
    351         int msgType = pdu.getMessageType();
    352         if ((msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ)
    353                 || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)) {
    354             return ((MultimediaMessagePdu) pdu).getBody();
    355         } else {
    356             throw new MmsException();
    357         }
    358     }
    359 
    360     public void setCurrentMessageSize(int size) {
    361         mCurrentMessageSize = size;
    362     }
    363 
    364     // getCurrentMessageSize returns the size of the message, not including resizable attachments
    365     // such as photos. mCurrentMessageSize is used when adding/deleting/replacing non-resizable
    366     // attachments (movies, sounds, etc) in order to compute how much size is left in the message.
    367     // The difference between mCurrentMessageSize and the maxSize allowed for a message is then
    368     // divided up between the remaining resizable attachments. While this function is public,
    369     // it is only used internally between various MMS classes. If the UI wants to know the
    370     // size of a MMS message, it should call getTotalMessageSize() instead.
    371     public int getCurrentMessageSize() {
    372         return mCurrentMessageSize;
    373     }
    374 
    375     // getTotalMessageSize returns the total size of the message, including resizable attachments
    376     // such as photos. This function is intended to be used by the UI for displaying the size of the
    377     // MMS message.
    378     public int getTotalMessageSize() {
    379         return mTotalMessageSize;
    380     }
    381 
    382     public void increaseMessageSize(int increaseSize) {
    383         if (increaseSize > 0) {
    384             mCurrentMessageSize += increaseSize;
    385         }
    386     }
    387 
    388     public void decreaseMessageSize(int decreaseSize) {
    389         if (decreaseSize > 0) {
    390             mCurrentMessageSize -= decreaseSize;
    391         }
    392     }
    393 
    394     public LayoutModel getLayout() {
    395         return mLayout;
    396     }
    397 
    398     //
    399     // Implement List<E> interface.
    400     //
    401     public boolean add(SlideModel object) {
    402         int increaseSize = object.getSlideSize();
    403         checkMessageSize(increaseSize);
    404 
    405         if ((object != null) && mSlides.add(object)) {
    406             increaseMessageSize(increaseSize);
    407             object.registerModelChangedObserver(this);
    408             for (IModelChangedObserver observer : mModelChangedObservers) {
    409                 object.registerModelChangedObserver(observer);
    410             }
    411             notifyModelChanged(true);
    412             return true;
    413         }
    414         return false;
    415     }
    416 
    417     public boolean addAll(Collection<? extends SlideModel> collection) {
    418         throw new UnsupportedOperationException("Operation not supported.");
    419     }
    420 
    421     public void clear() {
    422         if (mSlides.size() > 0) {
    423             for (SlideModel slide : mSlides) {
    424                 slide.unregisterModelChangedObserver(this);
    425                 for (IModelChangedObserver observer : mModelChangedObservers) {
    426                     slide.unregisterModelChangedObserver(observer);
    427                 }
    428             }
    429             mCurrentMessageSize = 0;
    430             mSlides.clear();
    431             notifyModelChanged(true);
    432         }
    433     }
    434 
    435     public boolean contains(Object object) {
    436         return mSlides.contains(object);
    437     }
    438 
    439     public boolean containsAll(Collection<?> collection) {
    440         return mSlides.containsAll(collection);
    441     }
    442 
    443     public boolean isEmpty() {
    444         return mSlides.isEmpty();
    445     }
    446 
    447     public Iterator<SlideModel> iterator() {
    448         return mSlides.iterator();
    449     }
    450 
    451     public boolean remove(Object object) {
    452         if ((object != null) && mSlides.remove(object)) {
    453             SlideModel slide = (SlideModel) object;
    454             decreaseMessageSize(slide.getSlideSize());
    455             slide.unregisterAllModelChangedObservers();
    456             notifyModelChanged(true);
    457             return true;
    458         }
    459         return false;
    460     }
    461 
    462     public boolean removeAll(Collection<?> collection) {
    463         throw new UnsupportedOperationException("Operation not supported.");
    464     }
    465 
    466     public boolean retainAll(Collection<?> collection) {
    467         throw new UnsupportedOperationException("Operation not supported.");
    468     }
    469 
    470     public int size() {
    471         return mSlides.size();
    472     }
    473 
    474     public Object[] toArray() {
    475         return mSlides.toArray();
    476     }
    477 
    478     public <T> T[] toArray(T[] array) {
    479         return mSlides.toArray(array);
    480     }
    481 
    482     public void add(int location, SlideModel object) {
    483         if (object != null) {
    484             int increaseSize = object.getSlideSize();
    485             checkMessageSize(increaseSize);
    486 
    487             mSlides.add(location, object);
    488             increaseMessageSize(increaseSize);
    489             object.registerModelChangedObserver(this);
    490             for (IModelChangedObserver observer : mModelChangedObservers) {
    491                 object.registerModelChangedObserver(observer);
    492             }
    493             notifyModelChanged(true);
    494         }
    495     }
    496 
    497     public boolean addAll(int location,
    498             Collection<? extends SlideModel> collection) {
    499         throw new UnsupportedOperationException("Operation not supported.");
    500     }
    501 
    502     public SlideModel get(int location) {
    503         return (location >= 0 && location < mSlides.size()) ? mSlides.get(location) : null;
    504     }
    505 
    506     public int indexOf(Object object) {
    507         return mSlides.indexOf(object);
    508     }
    509 
    510     public int lastIndexOf(Object object) {
    511         return mSlides.lastIndexOf(object);
    512     }
    513 
    514     public ListIterator<SlideModel> listIterator() {
    515         return mSlides.listIterator();
    516     }
    517 
    518     public ListIterator<SlideModel> listIterator(int location) {
    519         return mSlides.listIterator(location);
    520     }
    521 
    522     public SlideModel remove(int location) {
    523         SlideModel slide = mSlides.remove(location);
    524         if (slide != null) {
    525             decreaseMessageSize(slide.getSlideSize());
    526             slide.unregisterAllModelChangedObservers();
    527             notifyModelChanged(true);
    528         }
    529         return slide;
    530     }
    531 
    532     public SlideModel set(int location, SlideModel object) {
    533         SlideModel slide = mSlides.get(location);
    534         if (null != object) {
    535             int removeSize = 0;
    536             int addSize = object.getSlideSize();
    537             if (null != slide) {
    538                 removeSize = slide.getSlideSize();
    539             }
    540             if (addSize > removeSize) {
    541                 checkMessageSize(addSize - removeSize);
    542                 increaseMessageSize(addSize - removeSize);
    543             } else {
    544                 decreaseMessageSize(removeSize - addSize);
    545             }
    546         }
    547 
    548         slide =  mSlides.set(location, object);
    549         if (slide != null) {
    550             slide.unregisterAllModelChangedObservers();
    551         }
    552 
    553         if (object != null) {
    554             object.registerModelChangedObserver(this);
    555             for (IModelChangedObserver observer : mModelChangedObservers) {
    556                 object.registerModelChangedObserver(observer);
    557             }
    558         }
    559 
    560         notifyModelChanged(true);
    561         return slide;
    562     }
    563 
    564     public List<SlideModel> subList(int start, int end) {
    565         return mSlides.subList(start, end);
    566     }
    567 
    568     @Override
    569     protected void registerModelChangedObserverInDescendants(
    570             IModelChangedObserver observer) {
    571         mLayout.registerModelChangedObserver(observer);
    572 
    573         for (SlideModel slide : mSlides) {
    574             slide.registerModelChangedObserver(observer);
    575         }
    576     }
    577 
    578     @Override
    579     protected void unregisterModelChangedObserverInDescendants(
    580             IModelChangedObserver observer) {
    581         mLayout.unregisterModelChangedObserver(observer);
    582 
    583         for (SlideModel slide : mSlides) {
    584             slide.unregisterModelChangedObserver(observer);
    585         }
    586     }
    587 
    588     @Override
    589     protected void unregisterAllModelChangedObserversInDescendants() {
    590         mLayout.unregisterAllModelChangedObservers();
    591 
    592         for (SlideModel slide : mSlides) {
    593             slide.unregisterAllModelChangedObservers();
    594         }
    595     }
    596 
    597     public void onModelChanged(Model model, boolean dataChanged) {
    598         if (dataChanged) {
    599             mDocumentCache = null;
    600             mPduBodyCache = null;
    601         }
    602     }
    603 
    604     public void sync(PduBody pb) {
    605         for (SlideModel slide : mSlides) {
    606             for (MediaModel media : slide) {
    607                 PduPart part = pb.getPartByContentLocation(media.getSrc());
    608                 if (part != null) {
    609                     media.setUri(part.getDataUri());
    610                 }
    611             }
    612         }
    613     }
    614 
    615     public void checkMessageSize(int increaseSize) throws ContentRestrictionException {
    616         ContentRestriction cr = ContentRestrictionFactory.getContentRestriction();
    617         cr.checkMessageSize(mCurrentMessageSize, increaseSize, mContext.getContentResolver());
    618     }
    619 
    620     /**
    621      * Determines whether this is a "simple" slideshow.
    622      * Criteria:
    623      * - Exactly one slide
    624      * - Exactly one multimedia attachment, but no audio
    625      * - It can optionally have a caption
    626     */
    627     public boolean isSimple() {
    628         // There must be one (and only one) slide.
    629         if (size() != 1)
    630             return false;
    631 
    632         SlideModel slide = get(0);
    633         // The slide must have either an image or video, but not both.
    634         if (!(slide.hasImage() ^ slide.hasVideo()))
    635             return false;
    636 
    637         // No audio allowed.
    638         if (slide.hasAudio())
    639             return false;
    640 
    641         return true;
    642     }
    643 
    644     /**
    645      * Make sure the text in slide 0 is no longer holding onto a reference to the text
    646      * in the message text box.
    647      */
    648     public void prepareForSend() {
    649         if (size() == 1) {
    650             TextModel text = get(0).getText();
    651             if (text != null) {
    652                 text.cloneText();
    653             }
    654         }
    655     }
    656 
    657     /**
    658      * Resize all the resizeable media objects to fit in the remaining size of the slideshow.
    659      * This should be called off of the UI thread.
    660      *
    661      * @throws MmsException, ExceedMessageSizeException
    662      */
    663     public void finalResize(Uri messageUri) throws MmsException, ExceedMessageSizeException {
    664 
    665         // Figure out if we have any media items that need to be resized and total up the
    666         // sizes of the items that can't be resized.
    667         int resizableCnt = 0;
    668         int fixedSizeTotal = 0;
    669         for (SlideModel slide : mSlides) {
    670             for (MediaModel media : slide) {
    671                 if (media.getMediaResizable()) {
    672                     ++resizableCnt;
    673                 } else {
    674                     fixedSizeTotal += media.getMediaSize();
    675                 }
    676             }
    677         }
    678         if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
    679             Log.v(TAG, "finalResize: original message size: " + getCurrentMessageSize() +
    680                     " getMaxMessageSize: " + MmsConfig.getMaxMessageSize() +
    681                     " fixedSizeTotal: " + fixedSizeTotal);
    682         }
    683         if (resizableCnt > 0) {
    684             int remainingSize = MmsConfig.getMaxMessageSize() - fixedSizeTotal - SLIDESHOW_SLOP;
    685             if (remainingSize <= 0) {
    686                 throw new ExceedMessageSizeException("No room for pictures");
    687             }
    688             long messageId = ContentUris.parseId(messageUri);
    689             int bytesPerMediaItem = remainingSize / resizableCnt;
    690             // Resize the resizable media items to fit within their byte limit.
    691             for (SlideModel slide : mSlides) {
    692                 for (MediaModel media : slide) {
    693                     if (media.getMediaResizable()) {
    694                         media.resizeMedia(bytesPerMediaItem, messageId);
    695                     }
    696                 }
    697             }
    698             // One last time through to calc the real message size.
    699             int totalSize = 0;
    700             for (SlideModel slide : mSlides) {
    701                 for (MediaModel media : slide) {
    702                     totalSize += media.getMediaSize();
    703                 }
    704             }
    705             if (Log.isLoggable(LogTag.APP, Log.VERBOSE)) {
    706                 Log.v(TAG, "finalResize: new message size: " + totalSize);
    707             }
    708 
    709             if (totalSize > MmsConfig.getMaxMessageSize()) {
    710                 throw new ExceedMessageSizeException("After compressing pictures, message too big");
    711             }
    712             setCurrentMessageSize(totalSize);
    713 
    714             onModelChanged(this, true);     // clear the cached pdu body
    715             PduBody pb = toPduBody();
    716             // This will write out all the new parts to:
    717             //      /data/data/com.android.providers.telephony/app_parts
    718             // and at the same time delete the old parts.
    719             PduPersister.getPduPersister(mContext).updateParts(messageUri, pb, null);
    720         }
    721     }
    722 
    723 }
    724