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