• 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 java.util.ArrayList;
21 import java.util.Collection;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.ListIterator;
25 
26 import org.w3c.dom.events.Event;
27 import org.w3c.dom.events.EventListener;
28 import org.w3c.dom.smil.ElementTime;
29 
30 import android.text.TextUtils;
31 import android.util.Config;
32 import android.util.Log;
33 
34 import com.android.mms.ContentRestrictionException;
35 import com.android.mms.dom.smil.SmilParElementImpl;
36 import com.google.android.mms.ContentType;
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             if (mSlideSize < 0) {
237                 mSlideSize = 0;
238             }
239         }
240     }
241 
setParent(SlideshowModel parent)242     public void setParent(SlideshowModel parent) {
243         mParent = parent;
244     }
245 
increaseMessageSize(int increaseSize)246     public void increaseMessageSize(int increaseSize) {
247         if ((increaseSize > 0) && (null != mParent)) {
248             int size = mParent.getCurrentMessageSize();
249             size += increaseSize;
250             mParent.setCurrentMessageSize(size);
251         }
252     }
253 
decreaseMessageSize(int decreaseSize)254     public void decreaseMessageSize(int decreaseSize) {
255         if ((decreaseSize > 0) && (null != mParent)) {
256             int size = mParent.getCurrentMessageSize();
257             size -= decreaseSize;
258             if (size < 0) {
259                 size = 0;
260             }
261             mParent.setCurrentMessageSize(size);
262         }
263     }
264 
265     //
266     // Implement List<E> interface.
267     //
268 
269     /**
270      * Add a MediaModel to the slide. If the slide has already contained
271      * a media object in the same type, the media object will be replaced by
272      * the new one.
273      *
274      * @param object A media object to be added into the slide.
275      * @return true
276      * @throws IllegalStateException One or more media in the mediaList cannot
277      *         be added into the slide due to a slide cannot contain image
278      *         and video or audio and video at the same time.
279      * @throws ContentRestrictionException when can not add this object.
280      *
281      */
add(MediaModel object)282     public boolean add(MediaModel object) {
283         internalAdd(object);
284         notifyModelChanged(true);
285         return true;
286     }
287 
addAll(Collection<? extends MediaModel> collection)288     public boolean addAll(Collection<? extends MediaModel> collection) {
289         throw new UnsupportedOperationException("Operation not supported.");
290     }
291 
clear()292     public void clear() {
293         if (mMedia.size() > 0) {
294             for (MediaModel media : mMedia) {
295                 media.unregisterAllModelChangedObservers();
296                 int decreaseSize = media.getMediaSize();
297                 decreaseSlideSize(decreaseSize);
298                 decreaseMessageSize(decreaseSize);
299             }
300             mMedia.clear();
301 
302             mText = null;
303             mImage = null;
304             mAudio = null;
305             mVideo = null;
306 
307             mCanAddImage = true;
308             mCanAddAudio = true;
309             mCanAddVideo = true;
310 
311             notifyModelChanged(true);
312         }
313     }
314 
contains(Object object)315     public boolean contains(Object object) {
316         return mMedia.contains(object);
317     }
318 
containsAll(Collection<?> collection)319     public boolean containsAll(Collection<?> collection) {
320         return mMedia.containsAll(collection);
321     }
322 
isEmpty()323     public boolean isEmpty() {
324         return mMedia.isEmpty();
325     }
326 
iterator()327     public Iterator<MediaModel> iterator() {
328         return mMedia.iterator();
329     }
330 
remove(Object object)331     public boolean remove(Object object) {
332         if ((object != null) && (object instanceof MediaModel)
333                 && internalRemove(object)) {
334             notifyModelChanged(true);
335             return true;
336         }
337         return false;
338     }
339 
removeAll(Collection<?> collection)340     public boolean removeAll(Collection<?> collection) {
341         throw new UnsupportedOperationException("Operation not supported.");
342     }
343 
retainAll(Collection<?> collection)344     public boolean retainAll(Collection<?> collection) {
345         throw new UnsupportedOperationException("Operation not supported.");
346     }
347 
size()348     public int size() {
349         return mMedia.size();
350     }
351 
toArray()352     public Object[] toArray() {
353         return mMedia.toArray();
354     }
355 
toArray(T[] array)356     public <T> T[] toArray(T[] array) {
357         return mMedia.toArray(array);
358     }
359 
add(int location, MediaModel object)360     public void add(int location, MediaModel object) {
361         throw new UnsupportedOperationException("Operation not supported.");
362     }
363 
addAll(int location, Collection<? extends MediaModel> collection)364     public boolean addAll(int location,
365             Collection<? extends MediaModel> collection) {
366         throw new UnsupportedOperationException("Operation not supported.");
367     }
368 
get(int location)369     public MediaModel get(int location) {
370         if (mMedia.size() == 0) {
371             return null;
372         }
373 
374         return mMedia.get(location);
375     }
376 
indexOf(Object object)377     public int indexOf(Object object) {
378         return mMedia.indexOf(object);
379     }
380 
lastIndexOf(Object object)381     public int lastIndexOf(Object object) {
382         return mMedia.lastIndexOf(object);
383     }
384 
listIterator()385     public ListIterator<MediaModel> listIterator() {
386         return mMedia.listIterator();
387     }
388 
listIterator(int location)389     public ListIterator<MediaModel> listIterator(int location) {
390         return mMedia.listIterator(location);
391     }
392 
remove(int location)393     public MediaModel remove(int location) {
394         MediaModel media = mMedia.get(location);
395         if ((media != null) && internalRemove(media)) {
396             notifyModelChanged(true);
397         }
398         return media;
399     }
400 
set(int location, MediaModel object)401     public MediaModel set(int location, MediaModel object) {
402         throw new UnsupportedOperationException("Operation not supported.");
403     }
404 
subList(int start, int end)405     public List<MediaModel> subList(int start, int end) {
406         return mMedia.subList(start, end);
407     }
408 
409     /**
410      * @return the mVisible
411      */
isVisible()412     public boolean isVisible() {
413         return mVisible;
414     }
415 
416     /**
417      * @param visible the mVisible to set
418      */
setVisible(boolean visible)419     public void setVisible(boolean visible) {
420         mVisible = visible;
421         notifyModelChanged(true);
422     }
423 
424     /**
425      * @return the mFill
426      */
getFill()427     public short getFill() {
428         return mFill;
429     }
430 
431     /**
432      * @param fill the mFill to set
433      */
setFill(short fill)434     public void setFill(short fill) {
435         mFill = fill;
436         notifyModelChanged(true);
437     }
438 
439     @Override
registerModelChangedObserverInDescendants( IModelChangedObserver observer)440     protected void registerModelChangedObserverInDescendants(
441             IModelChangedObserver observer) {
442         for (MediaModel media : mMedia) {
443             media.registerModelChangedObserver(observer);
444         }
445     }
446 
447     @Override
unregisterModelChangedObserverInDescendants( IModelChangedObserver observer)448     protected void unregisterModelChangedObserverInDescendants(
449             IModelChangedObserver observer) {
450         for (MediaModel media : mMedia) {
451             media.unregisterModelChangedObserver(observer);
452         }
453     }
454 
455     @Override
unregisterAllModelChangedObserversInDescendants()456     protected void unregisterAllModelChangedObserversInDescendants() {
457         for (MediaModel media : mMedia) {
458             media.unregisterAllModelChangedObservers();
459         }
460     }
461 
462     // EventListener Interface
handleEvent(Event evt)463     public void handleEvent(Event evt) {
464         if (evt.getType().equals(SmilParElementImpl.SMIL_SLIDE_START_EVENT)) {
465             if (LOCAL_LOGV) {
466                 Log.v(TAG, "Start to play slide: " + this);
467             }
468             mVisible = true;
469         } else if (mFill != ElementTime.FILL_FREEZE) {
470             if (LOCAL_LOGV) {
471                 Log.v(TAG, "Stop playing slide: " + this);
472             }
473             mVisible = false;
474         }
475 
476         notifyModelChanged(false);
477     }
478 
hasText()479     public boolean hasText() {
480         return mText != null;
481     }
482 
hasImage()483     public boolean hasImage() {
484         return mImage != null;
485     }
486 
hasAudio()487     public boolean hasAudio() {
488         return mAudio != null;
489     }
490 
hasVideo()491     public boolean hasVideo() {
492         return mVideo != null;
493     }
494 
removeText()495     public boolean removeText() {
496         return remove(mText);
497     }
498 
removeImage()499     public boolean removeImage() {
500         return remove(mImage);
501     }
502 
removeAudio()503     public boolean removeAudio() {
504         boolean result = remove(mAudio);
505         resetDuration();
506         return result;
507     }
508 
removeVideo()509     public boolean removeVideo() {
510         boolean result = remove(mVideo);
511         resetDuration();
512         return result;
513     }
514 
getText()515     public TextModel getText() {
516         return (TextModel) mText;
517     }
518 
getImage()519     public ImageModel getImage() {
520         return (ImageModel) mImage;
521     }
522 
getAudio()523     public AudioModel getAudio() {
524         return (AudioModel) mAudio;
525     }
526 
getVideo()527     public VideoModel getVideo() {
528         return (VideoModel) mVideo;
529     }
530 
resetDuration()531     public void resetDuration() {
532         // If we remove all the objects that have duration, reset the slide back to its
533         // default duration. If we don't do this, if the user replaces a 10 sec video with
534         // a 3 sec audio, the duration will remain at 10 sec (see the way updateDuration() below
535         // works).
536         if (!hasAudio() && !hasVideo()) {
537             mDuration = DEFAULT_SLIDE_DURATION;
538         }
539     }
540 
updateDuration(int duration)541     public void updateDuration(int duration) {
542         if (duration <= 0) {
543             return;
544         }
545 
546         if ((duration > mDuration)
547                 || (mDuration == DEFAULT_SLIDE_DURATION)) {
548             mDuration = duration;
549         }
550     }
551 }
552