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