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 18package com.android.mms.model; 19 20import com.android.mms.ContentRestrictionException; 21import com.android.mms.dom.smil.SmilParElementImpl; 22import com.google.android.mms.ContentType; 23 24import org.w3c.dom.events.Event; 25import org.w3c.dom.events.EventListener; 26import org.w3c.dom.smil.ElementTime; 27 28import android.util.Config; 29import android.util.Log; 30import android.text.TextUtils; 31 32import java.util.ArrayList; 33import java.util.Collection; 34import java.util.Iterator; 35import java.util.List; 36import java.util.ListIterator; 37 38public 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