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