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