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