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