• 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 import com.android.mms.ContentRestrictionException;
21 import com.android.mms.dom.smil.SmilParElementImpl;
22 import com.google.android.mms.ContentType;
23 
24 import org.w3c.dom.events.Event;
25 import org.w3c.dom.events.EventListener;
26 import org.w3c.dom.smil.ElementTime;
27 
28 import android.util.Config;
29 import android.util.Log;
30 import android.text.TextUtils;
31 
32 import java.util.ArrayList;
33 import java.util.Collection;
34 import java.util.Iterator;
35 import java.util.List;
36 import java.util.ListIterator;
37 
38 public class SlideModel extends Model implements List<MediaModel>, EventListener {
39     public static final String TAG = "Mms/slideshow";
40     private static final boolean DEBUG = false;
41     private static final boolean LOCAL_LOGV = DEBUG ? Config.LOGD : Config.LOGV;
42     private static final int DEFAULT_SLIDE_DURATION = 5000;
43 
44     private final ArrayList<MediaModel> mMedia = new ArrayList<MediaModel>();
45 
46     private MediaModel mText;
47     private MediaModel mImage;
48     private MediaModel mAudio;
49     private MediaModel mVideo;
50 
51     private boolean mCanAddImage = true;
52     private boolean mCanAddAudio = true;
53     private boolean mCanAddVideo = true;
54 
55     private int mDuration;
56     private boolean mVisible = true;
57     private short mFill;
58     private int mSlideSize;
59     private SlideshowModel mParent;
60 
SlideModel(SlideshowModel slideshow)61     public SlideModel(SlideshowModel slideshow) {
62         this(DEFAULT_SLIDE_DURATION, slideshow);
63     }
64 
SlideModel(int duration, SlideshowModel slideshow)65     public SlideModel(int duration, SlideshowModel slideshow) {
66         mDuration = duration;
67         mParent = slideshow;
68     }
69 
70     /**
71      * Create a SlideModel with exist media collection.
72      *
73      * @param duration The duration of the slide.
74      * @param mediaList The exist media collection.
75      *
76      * @throws IllegalStateException One or more media in the mediaList cannot
77      *         be added into the slide due to a slide cannot contain image
78      *         and video or audio and video at the same time.
79      */
SlideModel(int duration, ArrayList<MediaModel> mediaList)80     public SlideModel(int duration, ArrayList<MediaModel> mediaList) {
81         mDuration = duration;
82 
83         int maxDur = 0;
84         for (MediaModel media : mediaList) {
85             internalAdd(media);
86 
87             int mediaDur = media.getDuration();
88             if (mediaDur > maxDur) {
89                 maxDur = mediaDur;
90             }
91         }
92 
93         updateDuration(maxDur);
94     }
95 
internalAdd(MediaModel media)96     private void internalAdd(MediaModel media) throws IllegalStateException {
97         if (media == null) {
98             // Don't add null value into the list.
99             return;
100         }
101 
102         if (media.isText()) {
103             String contentType = media.getContentType();
104             if (TextUtils.isEmpty(contentType) || ContentType.TEXT_PLAIN.equals(contentType)
105                     || ContentType.TEXT_HTML.equals(contentType)) {
106                 internalAddOrReplace(mText, media);
107                 mText = media;
108             } else {
109                 Log.w(TAG, "[SlideModel] content type " + media.getContentType() +
110                         " isn't supported (as text)");
111             }
112         } else if (media.isImage()) {
113             if (mCanAddImage) {
114                 internalAddOrReplace(mImage, media);
115                 mImage = media;
116                 mCanAddVideo = false;
117             } else {
118                 Log.w(TAG, "[SlideModel] content type " + media.getContentType() +
119                     " - can't add image in this state");
120             }
121         } else if (media.isAudio()) {
122             if (mCanAddAudio) {
123                 internalAddOrReplace(mAudio, media);
124                 mAudio = media;
125                 mCanAddVideo = false;
126             } else {
127                 Log.w(TAG, "[SlideModel] content type " + media.getContentType() +
128                     " - can't add audio in this state");
129             }
130         } else if (media.isVideo()) {
131             if (mCanAddVideo) {
132                 internalAddOrReplace(mVideo, media);
133                 mVideo = media;
134                 mCanAddImage = false;
135                 mCanAddAudio = false;
136             } else {
137                 Log.w(TAG, "[SlideModel] content type " + media.getContentType() +
138                     " - can't add video in this state");
139             }
140         }
141     }
142 
internalAddOrReplace(MediaModel old, MediaModel media)143     private void internalAddOrReplace(MediaModel old, MediaModel media) {
144         // If the media is resizable, at this point consider it to be zero length.
145         // Just before we send the slideshow, we take the remaining space in the
146         // slideshow and equally allocate it to all the resizeable media items and resize them.
147         int addSize = media.getMediaResizable() ? 0 : media.getMediaSize();
148         int removeSize;
149         if (old == null) {
150             if (null != mParent) {
151                 mParent.checkMessageSize(addSize);
152             }
153             mMedia.add(media);
154             increaseSlideSize(addSize);
155             increaseMessageSize(addSize);
156         } else {
157             removeSize = old.getMediaResizable() ? 0 : old.getMediaSize();
158             if (addSize > removeSize) {
159                 if (null != mParent) {
160                     mParent.checkMessageSize(addSize - removeSize);
161                 }
162                 increaseSlideSize(addSize - removeSize);
163                 increaseMessageSize(addSize - removeSize);
164             } else {
165                 decreaseSlideSize(removeSize - addSize);
166                 decreaseMessageSize(removeSize - addSize);
167             }
168             mMedia.set(mMedia.indexOf(old), media);
169             old.unregisterAllModelChangedObservers();
170         }
171 
172         for (IModelChangedObserver observer : mModelChangedObservers) {
173             media.registerModelChangedObserver(observer);
174         }
175     }
176 
internalRemove(Object object)177     private boolean internalRemove(Object object) {
178         if (mMedia.remove(object)) {
179             if (object instanceof TextModel) {
180                 mText = null;
181             } else if (object instanceof ImageModel) {
182                 mImage = null;
183                 mCanAddVideo = true;
184             } else if (object instanceof AudioModel) {
185                 mAudio = null;
186                 mCanAddVideo = true;
187             } else if (object instanceof VideoModel) {
188                 mVideo = null;
189                 mCanAddImage = true;
190                 mCanAddAudio = true;
191             }
192             // If the media is resizable, at this point consider it to be zero length.
193             // Just before we send the slideshow, we take the remaining space in the
194             // slideshow and equally allocate it to all the resizeable media items and resize them.
195             int decreaseSize = ((MediaModel) object).getMediaResizable() ? 0
196                                         : ((MediaModel) object).getMediaSize();
197             decreaseSlideSize(decreaseSize);
198             decreaseMessageSize(decreaseSize);
199 
200             ((Model) object).unregisterAllModelChangedObservers();
201 
202             return true;
203         }
204 
205         return false;
206     }
207 
208     /**
209      * @return the mDuration
210      */
getDuration()211     public int getDuration() {
212         return mDuration;
213     }
214 
215     /**
216      * @param duration the mDuration to set
217      */
setDuration(int duration)218     public void setDuration(int duration) {
219         mDuration = duration;
220         notifyModelChanged(true);
221     }
222 
getSlideSize()223     public int getSlideSize() {
224         return mSlideSize;
225     }
226 
increaseSlideSize(int increaseSize)227     public void increaseSlideSize(int increaseSize) {
228         if (increaseSize > 0) {
229             mSlideSize += increaseSize;
230         }
231     }
232 
decreaseSlideSize(int decreaseSize)233     public void decreaseSlideSize(int decreaseSize) {
234         if (decreaseSize > 0) {
235             mSlideSize -= decreaseSize;
236         }
237     }
238 
setParent(SlideshowModel parent)239     public void setParent(SlideshowModel parent) {
240         mParent = parent;
241     }
242 
increaseMessageSize(int increaseSize)243     public void increaseMessageSize(int increaseSize) {
244         if ((increaseSize > 0) && (null != mParent)) {
245             int size = mParent.getCurrentMessageSize();
246             size += increaseSize;
247             mParent.setCurrentMessageSize(size);
248         }
249     }
250 
decreaseMessageSize(int decreaseSize)251     public void decreaseMessageSize(int decreaseSize) {
252         if ((decreaseSize > 0) && (null != mParent)) {
253             int size = mParent.getCurrentMessageSize();
254             size -= decreaseSize;
255             mParent.setCurrentMessageSize(size);
256         }
257     }
258 
259     //
260     // Implement List<E> interface.
261     //
262 
263     /**
264      * Add a MediaModel to the slide. If the slide has already contained
265      * a media object in the same type, the media object will be replaced by
266      * the new one.
267      *
268      * @param object A media object to be added into the slide.
269      * @return true
270      * @throws IllegalStateException One or more media in the mediaList cannot
271      *         be added into the slide due to a slide cannot contain image
272      *         and video or audio and video at the same time.
273      * @throws ContentRestrictionException when can not add this object.
274      *
275      */
add(MediaModel object)276     public boolean add(MediaModel object) {
277         internalAdd(object);
278         notifyModelChanged(true);
279         return true;
280     }
281 
addAll(Collection<? extends MediaModel> collection)282     public boolean addAll(Collection<? extends MediaModel> collection) {
283         throw new UnsupportedOperationException("Operation not supported.");
284     }
285 
clear()286     public void clear() {
287         if (mMedia.size() > 0) {
288             for (MediaModel media : mMedia) {
289                 media.unregisterAllModelChangedObservers();
290                 int decreaseSize = media.getMediaSize();
291                 decreaseSlideSize(decreaseSize);
292                 decreaseMessageSize(decreaseSize);
293             }
294             mMedia.clear();
295 
296             mText = null;
297             mImage = null;
298             mAudio = null;
299             mVideo = null;
300 
301             mCanAddImage = true;
302             mCanAddAudio = true;
303             mCanAddVideo = true;
304 
305             notifyModelChanged(true);
306         }
307     }
308 
contains(Object object)309     public boolean contains(Object object) {
310         return mMedia.contains(object);
311     }
312 
containsAll(Collection<?> collection)313     public boolean containsAll(Collection<?> collection) {
314         return mMedia.containsAll(collection);
315     }
316 
isEmpty()317     public boolean isEmpty() {
318         return mMedia.isEmpty();
319     }
320 
iterator()321     public Iterator<MediaModel> iterator() {
322         return mMedia.iterator();
323     }
324 
remove(Object object)325     public boolean remove(Object object) {
326         if ((object != null) && (object instanceof MediaModel)
327                 && internalRemove(object)) {
328             notifyModelChanged(true);
329             return true;
330         }
331         return false;
332     }
333 
removeAll(Collection<?> collection)334     public boolean removeAll(Collection<?> collection) {
335         throw new UnsupportedOperationException("Operation not supported.");
336     }
337 
retainAll(Collection<?> collection)338     public boolean retainAll(Collection<?> collection) {
339         throw new UnsupportedOperationException("Operation not supported.");
340     }
341 
size()342     public int size() {
343         return mMedia.size();
344     }
345 
toArray()346     public Object[] toArray() {
347         return mMedia.toArray();
348     }
349 
toArray(T[] array)350     public <T> T[] toArray(T[] array) {
351         return mMedia.toArray(array);
352     }
353 
add(int location, MediaModel object)354     public void add(int location, MediaModel object) {
355         throw new UnsupportedOperationException("Operation not supported.");
356     }
357 
addAll(int location, Collection<? extends MediaModel> collection)358     public boolean addAll(int location,
359             Collection<? extends MediaModel> collection) {
360         throw new UnsupportedOperationException("Operation not supported.");
361     }
362 
get(int location)363     public MediaModel get(int location) {
364         if (mMedia.size() == 0) {
365             return null;
366         }
367 
368         return mMedia.get(location);
369     }
370 
indexOf(Object object)371     public int indexOf(Object object) {
372         return mMedia.indexOf(object);
373     }
374 
lastIndexOf(Object object)375     public int lastIndexOf(Object object) {
376         return mMedia.lastIndexOf(object);
377     }
378 
listIterator()379     public ListIterator<MediaModel> listIterator() {
380         return mMedia.listIterator();
381     }
382 
listIterator(int location)383     public ListIterator<MediaModel> listIterator(int location) {
384         return mMedia.listIterator(location);
385     }
386 
remove(int location)387     public MediaModel remove(int location) {
388         MediaModel media = mMedia.get(location);
389         if ((media != null) && internalRemove(media)) {
390             notifyModelChanged(true);
391         }
392         return media;
393     }
394 
set(int location, MediaModel object)395     public MediaModel set(int location, MediaModel object) {
396         throw new UnsupportedOperationException("Operation not supported.");
397     }
398 
subList(int start, int end)399     public List<MediaModel> subList(int start, int end) {
400         return mMedia.subList(start, end);
401     }
402 
403     /**
404      * @return the mVisible
405      */
isVisible()406     public boolean isVisible() {
407         return mVisible;
408     }
409 
410     /**
411      * @param visible the mVisible to set
412      */
setVisible(boolean visible)413     public void setVisible(boolean visible) {
414         mVisible = visible;
415         notifyModelChanged(true);
416     }
417 
418     /**
419      * @return the mFill
420      */
getFill()421     public short getFill() {
422         return mFill;
423     }
424 
425     /**
426      * @param fill the mFill to set
427      */
setFill(short fill)428     public void setFill(short fill) {
429         mFill = fill;
430         notifyModelChanged(true);
431     }
432 
433     @Override
registerModelChangedObserverInDescendants( IModelChangedObserver observer)434     protected void registerModelChangedObserverInDescendants(
435             IModelChangedObserver observer) {
436         for (MediaModel media : mMedia) {
437             media.registerModelChangedObserver(observer);
438         }
439     }
440 
441     @Override
unregisterModelChangedObserverInDescendants( IModelChangedObserver observer)442     protected void unregisterModelChangedObserverInDescendants(
443             IModelChangedObserver observer) {
444         for (MediaModel media : mMedia) {
445             media.unregisterModelChangedObserver(observer);
446         }
447     }
448 
449     @Override
unregisterAllModelChangedObserversInDescendants()450     protected void unregisterAllModelChangedObserversInDescendants() {
451         for (MediaModel media : mMedia) {
452             media.unregisterAllModelChangedObservers();
453         }
454     }
455 
456     // EventListener Interface
handleEvent(Event evt)457     public void handleEvent(Event evt) {
458         if (evt.getType().equals(SmilParElementImpl.SMIL_SLIDE_START_EVENT)) {
459             if (LOCAL_LOGV) {
460                 Log.v(TAG, "Start to play slide: " + this);
461             }
462             mVisible = true;
463         } else if (mFill != ElementTime.FILL_FREEZE) {
464             if (LOCAL_LOGV) {
465                 Log.v(TAG, "Stop playing slide: " + this);
466             }
467             mVisible = false;
468         }
469 
470         notifyModelChanged(false);
471     }
472 
hasText()473     public boolean hasText() {
474         return mText != null;
475     }
476 
hasImage()477     public boolean hasImage() {
478         return mImage != null;
479     }
480 
hasAudio()481     public boolean hasAudio() {
482         return mAudio != null;
483     }
484 
hasVideo()485     public boolean hasVideo() {
486         return mVideo != null;
487     }
488 
removeText()489     public boolean removeText() {
490         return remove(mText);
491     }
492 
removeImage()493     public boolean removeImage() {
494         return remove(mImage);
495     }
496 
removeAudio()497     public boolean removeAudio() {
498         boolean result = remove(mAudio);
499         resetDuration();
500         return result;
501     }
502 
removeVideo()503     public boolean removeVideo() {
504         boolean result = remove(mVideo);
505         resetDuration();
506         return result;
507     }
508 
getText()509     public TextModel getText() {
510         return (TextModel) mText;
511     }
512 
getImage()513     public ImageModel getImage() {
514         return (ImageModel) mImage;
515     }
516 
getAudio()517     public AudioModel getAudio() {
518         return (AudioModel) mAudio;
519     }
520 
getVideo()521     public VideoModel getVideo() {
522         return (VideoModel) mVideo;
523     }
524 
resetDuration()525     public void resetDuration() {
526         // If we remove all the objects that have duration, reset the slide back to its
527         // default duration. If we don't do this, if the user replaces a 10 sec video with
528         // a 3 sec audio, the duration will remain at 10 sec (see the way updateDuration() below
529         // works).
530         if (!hasAudio() && !hasVideo()) {
531             mDuration = DEFAULT_SLIDE_DURATION;
532         }
533     }
534 
updateDuration(int duration)535     public void updateDuration(int duration) {
536         if (duration <= 0) {
537             return;
538         }
539 
540         if ((duration > mDuration)
541                 || (mDuration == DEFAULT_SLIDE_DURATION)) {
542             mDuration = duration;
543         }
544     }
545 }
546