• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
SlideshowModel(Context context)80     private SlideshowModel(Context context) {
81         mLayout = new LayoutModel();
82         mSlides = new ArrayList<SlideModel>();
83         mContext = context;
84     }
85 
SlideshowModel( LayoutModel layouts, ArrayList<SlideModel> slides, SMILDocument documentCache, PduBody pbCache, Context context)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 
createNew(Context context)102     public static SlideshowModel createNew(Context context) {
103         return new SlideshowModel(context);
104     }
105 
createFromMessageUri( Context context, Uri uri)106     public static SlideshowModel createFromMessageUri(
107             Context context, Uri uri) throws MmsException {
108         return createFromPduBody(context, getPduBody(context, uri));
109     }
110 
createFromPduBody(Context context, PduBody pb)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 
toPduBody()232     public PduBody toPduBody() {
233         if (mPduBodyCache == null) {
234             mDocumentCache = SmilHelper.getDocument(this);
235             mPduBodyCache = makePduBody(mDocumentCache);
236         }
237         return mPduBodyCache;
238     }
239 
makePduBody(SMILDocument document)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 
openPartFiles(ContentResolver cr)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 
makeCopy()336     public PduBody makeCopy() {
337         return makePduBody(SmilHelper.getDocument(this));
338     }
339 
toSmilDocument()340     public SMILDocument toSmilDocument() {
341         if (mDocumentCache == null) {
342             mDocumentCache = SmilHelper.getDocument(this);
343         }
344         return mDocumentCache;
345     }
346 
getPduBody(Context context, Uri msg)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 
setCurrentMessageSize(int size)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.
getCurrentMessageSize()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.
getTotalMessageSize()378     public int getTotalMessageSize() {
379         return mTotalMessageSize;
380     }
381 
increaseMessageSize(int increaseSize)382     public void increaseMessageSize(int increaseSize) {
383         if (increaseSize > 0) {
384             mCurrentMessageSize += increaseSize;
385         }
386     }
387 
decreaseMessageSize(int decreaseSize)388     public void decreaseMessageSize(int decreaseSize) {
389         if (decreaseSize > 0) {
390             mCurrentMessageSize -= decreaseSize;
391         }
392     }
393 
getLayout()394     public LayoutModel getLayout() {
395         return mLayout;
396     }
397 
398     //
399     // Implement List<E> interface.
400     //
add(SlideModel object)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 
addAll(Collection<? extends SlideModel> collection)417     public boolean addAll(Collection<? extends SlideModel> collection) {
418         throw new UnsupportedOperationException("Operation not supported.");
419     }
420 
clear()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 
contains(Object object)435     public boolean contains(Object object) {
436         return mSlides.contains(object);
437     }
438 
containsAll(Collection<?> collection)439     public boolean containsAll(Collection<?> collection) {
440         return mSlides.containsAll(collection);
441     }
442 
isEmpty()443     public boolean isEmpty() {
444         return mSlides.isEmpty();
445     }
446 
iterator()447     public Iterator<SlideModel> iterator() {
448         return mSlides.iterator();
449     }
450 
remove(Object object)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 
removeAll(Collection<?> collection)462     public boolean removeAll(Collection<?> collection) {
463         throw new UnsupportedOperationException("Operation not supported.");
464     }
465 
retainAll(Collection<?> collection)466     public boolean retainAll(Collection<?> collection) {
467         throw new UnsupportedOperationException("Operation not supported.");
468     }
469 
size()470     public int size() {
471         return mSlides.size();
472     }
473 
toArray()474     public Object[] toArray() {
475         return mSlides.toArray();
476     }
477 
toArray(T[] array)478     public <T> T[] toArray(T[] array) {
479         return mSlides.toArray(array);
480     }
481 
add(int location, SlideModel object)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 
addAll(int location, Collection<? extends SlideModel> collection)497     public boolean addAll(int location,
498             Collection<? extends SlideModel> collection) {
499         throw new UnsupportedOperationException("Operation not supported.");
500     }
501 
get(int location)502     public SlideModel get(int location) {
503         return (location >= 0 && location < mSlides.size()) ? mSlides.get(location) : null;
504     }
505 
indexOf(Object object)506     public int indexOf(Object object) {
507         return mSlides.indexOf(object);
508     }
509 
lastIndexOf(Object object)510     public int lastIndexOf(Object object) {
511         return mSlides.lastIndexOf(object);
512     }
513 
listIterator()514     public ListIterator<SlideModel> listIterator() {
515         return mSlides.listIterator();
516     }
517 
listIterator(int location)518     public ListIterator<SlideModel> listIterator(int location) {
519         return mSlides.listIterator(location);
520     }
521 
remove(int location)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 
set(int location, SlideModel object)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 
subList(int start, int end)564     public List<SlideModel> subList(int start, int end) {
565         return mSlides.subList(start, end);
566     }
567 
568     @Override
registerModelChangedObserverInDescendants( IModelChangedObserver observer)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
unregisterModelChangedObserverInDescendants( IModelChangedObserver observer)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
unregisterAllModelChangedObserversInDescendants()589     protected void unregisterAllModelChangedObserversInDescendants() {
590         mLayout.unregisterAllModelChangedObservers();
591 
592         for (SlideModel slide : mSlides) {
593             slide.unregisterAllModelChangedObservers();
594         }
595     }
596 
onModelChanged(Model model, boolean dataChanged)597     public void onModelChanged(Model model, boolean dataChanged) {
598         if (dataChanged) {
599             mDocumentCache = null;
600             mPduBodyCache = null;
601         }
602     }
603 
sync(PduBody pb)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 
checkMessageSize(int increaseSize)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     */
isSimple()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      */
prepareForSend()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      */
finalResize(Uri messageUri)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