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