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