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