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