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