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