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