SlideshowModel.java revision 21b919f737006ffd654ac788aeedcaed05f5b367
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 20 21import com.android.mms.ContentRestrictionException; 22import com.android.mms.R; 23import com.android.mms.dom.smil.parser.SmilXmlSerializer; 24import com.android.mms.drm.DrmWrapper; 25import com.android.mms.layout.LayoutManager; 26import com.google.android.mms.ContentType; 27import com.google.android.mms.MmsException; 28import com.google.android.mms.pdu.GenericPdu; 29import com.google.android.mms.pdu.MultimediaMessagePdu; 30import com.google.android.mms.pdu.PduBody; 31import com.google.android.mms.pdu.PduHeaders; 32import com.google.android.mms.pdu.PduPart; 33import com.google.android.mms.pdu.PduPersister; 34 35import org.w3c.dom.NodeList; 36import org.w3c.dom.events.EventTarget; 37import org.w3c.dom.smil.SMILDocument; 38import org.w3c.dom.smil.SMILElement; 39import org.w3c.dom.smil.SMILLayoutElement; 40import org.w3c.dom.smil.SMILMediaElement; 41import org.w3c.dom.smil.SMILParElement; 42import org.w3c.dom.smil.SMILRegionElement; 43import org.w3c.dom.smil.SMILRootLayoutElement; 44 45import android.content.ContentResolver; 46import android.content.Context; 47import android.drm.mobile1.DrmException; 48import android.net.Uri; 49import android.util.Log; 50import android.widget.Toast; 51 52import java.io.ByteArrayOutputStream; 53import java.io.IOException; 54import java.util.ArrayList; 55import java.util.Collection; 56import java.util.Iterator; 57import java.util.List; 58import java.util.ListIterator; 59 60public class SlideshowModel extends Model 61 implements List<SlideModel>, IModelChangedObserver { 62 private static final String TAG = "SlideshowModel"; 63 64 private final LayoutModel mLayout; 65 private final ArrayList<SlideModel> mSlides; 66 private SMILDocument mDocumentCache; 67 private PduBody mPduBodyCache; 68 private int mCurrentMessageSize; 69 private ContentResolver mContentResolver; 70 71 private SlideshowModel(ContentResolver contentResolver) { 72 mLayout = new LayoutModel(); 73 mSlides = new ArrayList<SlideModel>(); 74 mContentResolver = contentResolver; 75 } 76 77 private SlideshowModel ( 78 LayoutModel layouts, ArrayList<SlideModel> slides, 79 SMILDocument documentCache, PduBody pbCache, 80 ContentResolver contentResolver) { 81 mLayout = layouts; 82 mSlides = slides; 83 mContentResolver = contentResolver; 84 85 mDocumentCache = documentCache; 86 mPduBodyCache = pbCache; 87 for (SlideModel slide : mSlides) { 88 increaseMessageSize(slide.getSlideSize()); 89 slide.setParent(this); 90 } 91 } 92 93 public static SlideshowModel createNew(Context context) { 94 return new SlideshowModel(context.getContentResolver()); 95 } 96 97 public static SlideshowModel createFromMessageUri( 98 Context context, Uri uri) throws MmsException { 99 return createFromPduBody(context, getPduBody(context, uri)); 100 } 101 102 public static SlideshowModel createFromPduBody(Context context, PduBody pb) throws MmsException { 103 SMILDocument document = SmilHelper.getDocument(pb); 104 105 // Create root-layout model. 106 SMILLayoutElement sle = document.getLayout(); 107 SMILRootLayoutElement srle = sle.getRootLayout(); 108 int w = srle.getWidth(); 109 int h = srle.getHeight(); 110 if ((w == 0) || (h == 0)) { 111 w = LayoutManager.getInstance().getLayoutParameters().getWidth(); 112 h = LayoutManager.getInstance().getLayoutParameters().getHeight(); 113 srle.setWidth(w); 114 srle.setHeight(h); 115 } 116 RegionModel rootLayout = new RegionModel( 117 null, 0, 0, w, h); 118 119 // Create region models. 120 ArrayList<RegionModel> regions = new ArrayList<RegionModel>(); 121 NodeList nlRegions = sle.getRegions(); 122 int regionsNum = nlRegions.getLength(); 123 124 for (int i = 0; i < regionsNum; i++) { 125 SMILRegionElement sre = (SMILRegionElement) nlRegions.item(i); 126 RegionModel r = new RegionModel(sre.getId(), sre.getFit(), 127 sre.getLeft(), sre.getTop(), sre.getWidth(), sre.getHeight(), 128 sre.getBackgroundColor()); 129 regions.add(r); 130 } 131 LayoutModel layouts = new LayoutModel(rootLayout, regions); 132 133 // Create slide models. 134 SMILElement docBody = document.getBody(); 135 NodeList slideNodes = docBody.getChildNodes(); 136 int slidesNum = slideNodes.getLength(); 137 ArrayList<SlideModel> slides = new ArrayList<SlideModel>(slidesNum); 138 139 for (int i = 0; i < slidesNum; i++) { 140 // FIXME: This is NOT compatible with the SMILDocument which is 141 // generated by some other mobile phones. 142 SMILParElement par = (SMILParElement) slideNodes.item(i); 143 144 // Create media models for each slide. 145 NodeList mediaNodes = par.getChildNodes(); 146 int mediaNum = mediaNodes.getLength(); 147 ArrayList<MediaModel> mediaSet = new ArrayList<MediaModel>(mediaNum); 148 149 for (int j = 0; j < mediaNum; j++) { 150 SMILMediaElement sme = (SMILMediaElement) mediaNodes.item(j); 151 try { 152 MediaModel media = MediaModelFactory.getMediaModel( 153 context, sme, layouts, pb); 154 SmilHelper.addMediaElementEventListeners( 155 (EventTarget) sme, media); 156 mediaSet.add(media); 157 } catch (DrmException e) { 158 Log.e(TAG, e.getMessage(), e); 159 } catch (IOException e) { 160 Log.e(TAG, e.getMessage(), e); 161 } catch (IllegalArgumentException e) { 162 Log.e(TAG, e.getMessage(), e); 163 } 164 } 165 166 SlideModel slide = new SlideModel((int) (par.getDur() * 1000), mediaSet); 167 slide.setFill(par.getFill()); 168 SmilHelper.addParElementEventListeners((EventTarget) par, slide); 169 slides.add(slide); 170 } 171 172 SlideshowModel slideshow = new SlideshowModel(layouts, slides, document, pb, 173 context.getContentResolver()); 174 slideshow.registerModelChangedObserver(slideshow); 175 return slideshow; 176 } 177 178 public PduBody toPduBody() { 179 if (mPduBodyCache == null) { 180 mDocumentCache = SmilHelper.getDocument(this); 181 mPduBodyCache = makePduBody(mDocumentCache); 182 } 183 return mPduBodyCache; 184 } 185 186 private PduBody makePduBody(SMILDocument document) { 187 return makePduBody(null, document, false); 188 } 189 190 private PduBody makePduBody(Context context, SMILDocument document, boolean isMakingCopy) { 191 PduBody pb = new PduBody(); 192 193 boolean hasForwardLock = false; 194 for (SlideModel slide : mSlides) { 195 for (MediaModel media : slide) { 196 if (isMakingCopy) { 197 if (media.isDrmProtected() && !media.isAllowedToForward()) { 198 hasForwardLock = true; 199 continue; 200 } 201 } 202 203 PduPart part = new PduPart(); 204 205 // Set Charset if it's a text media. 206 if (media.isText()) { 207 part.setCharset(((TextModel) media).getCharset()); 208 } 209 210 // Set Content-Type. 211 part.setContentType(media.getContentType().getBytes()); 212 213 String src = media.getSrc(); 214 String location; 215 boolean startWithContentId = src.startsWith("cid:"); 216 if (startWithContentId) { 217 location = src.substring("cid:".length()); 218 } else { 219 location = src; 220 } 221 222 // Set Content-Location. 223 part.setContentLocation(location.getBytes()); 224 225 // Set Content-Id. 226 if (startWithContentId) { 227 //Keep the original Content-Id. 228 part.setContentId(location.getBytes()); 229 } 230 else { 231 int index = location.lastIndexOf("."); 232 String contentId = (index == -1) ? location 233 : location.substring(0, index); 234 part.setContentId(contentId.getBytes()); 235 } 236 237 if (media.isDrmProtected()) { 238 DrmWrapper wrapper = media.getDrmObject(); 239 part.setDataUri(wrapper.getOriginalUri()); 240 part.setData(wrapper.getOriginalData()); 241 } else if (media.isText()) { 242 part.setData(((TextModel) media).getText().getBytes()); 243 } else if (media.isImage() || media.isVideo() || media.isAudio()) { 244 part.setDataUri(media.getUri()); 245 } else { 246 Log.w(TAG, "Unsupport media: " + media); 247 } 248 249 pb.addPart(part); 250 } 251 } 252 253 if (hasForwardLock && isMakingCopy && context != null) { 254 Toast.makeText(context, 255 context.getString(R.string.cannot_forward_drm_obj), 256 Toast.LENGTH_LONG).show(); 257 document = SmilHelper.getDocument(pb); 258 } 259 260 // Create and insert SMIL part(as the first part) into the PduBody. 261 ByteArrayOutputStream out = new ByteArrayOutputStream(); 262 SmilXmlSerializer.serialize(document, out); 263 PduPart smilPart = new PduPart(); 264 smilPart.setContentId("smil".getBytes()); 265 smilPart.setContentLocation("smil.xml".getBytes()); 266 smilPart.setContentType(ContentType.APP_SMIL.getBytes()); 267 smilPart.setData(out.toByteArray()); 268 pb.addPart(0, smilPart); 269 270 return pb; 271 } 272 273 public PduBody makeCopy(Context context) { 274 return makePduBody(context, SmilHelper.getDocument(this), true); 275 } 276 277 public SMILDocument toSmilDocument() { 278 if (mDocumentCache == null) { 279 mDocumentCache = SmilHelper.getDocument(this); 280 } 281 return mDocumentCache; 282 } 283 284 public static PduBody getPduBody(Context context, Uri msg) throws MmsException { 285 PduPersister p = PduPersister.getPduPersister(context); 286 GenericPdu pdu = p.load(msg); 287 288 int msgType = pdu.getMessageType(); 289 if ((msgType == PduHeaders.MESSAGE_TYPE_SEND_REQ) 290 || (msgType == PduHeaders.MESSAGE_TYPE_RETRIEVE_CONF)) { 291 return ((MultimediaMessagePdu) pdu).getBody(); 292 } else { 293 throw new MmsException(); 294 } 295 } 296 297 public void setCurrentMessageSize(int size) { 298 mCurrentMessageSize = size; 299 } 300 301 public int getCurrentMessageSize() { 302 return mCurrentMessageSize; 303 } 304 305 public void increaseMessageSize(int increaseSize) { 306 if (increaseSize > 0) { 307 mCurrentMessageSize += increaseSize; 308 } 309 } 310 311 public void decreaseMessageSize(int decreaseSize) { 312 if (decreaseSize > 0) { 313 mCurrentMessageSize -= decreaseSize; 314 } 315 } 316 317 public LayoutModel getLayout() { 318 return mLayout; 319 } 320 321 // 322 // Implement List<E> interface. 323 // 324 public boolean add(SlideModel object) { 325 int increaseSize = object.getSlideSize(); 326 checkMessageSize(increaseSize); 327 328 if ((object != null) && mSlides.add(object)) { 329 increaseMessageSize(increaseSize); 330 object.registerModelChangedObserver(this); 331 for (IModelChangedObserver observer : mModelChangedObservers) { 332 object.registerModelChangedObserver(observer); 333 } 334 notifyModelChanged(true); 335 return true; 336 } 337 return false; 338 } 339 340 public boolean addAll(Collection<? extends SlideModel> collection) { 341 throw new UnsupportedOperationException("Operation not supported."); 342 } 343 344 public void clear() { 345 if (mSlides.size() > 0) { 346 for (SlideModel slide : mSlides) { 347 slide.unregisterModelChangedObserver(this); 348 for (IModelChangedObserver observer : mModelChangedObservers) { 349 slide.unregisterModelChangedObserver(observer); 350 } 351 } 352 mCurrentMessageSize = 0; 353 mSlides.clear(); 354 notifyModelChanged(true); 355 } 356 } 357 358 public boolean contains(Object object) { 359 return mSlides.contains(object); 360 } 361 362 public boolean containsAll(Collection<?> collection) { 363 return mSlides.containsAll(collection); 364 } 365 366 public boolean isEmpty() { 367 return mSlides.isEmpty(); 368 } 369 370 public Iterator<SlideModel> iterator() { 371 return mSlides.iterator(); 372 } 373 374 public boolean remove(Object object) { 375 if ((object != null) && mSlides.remove(object)) { 376 SlideModel slide = (SlideModel) object; 377 decreaseMessageSize(slide.getSlideSize()); 378 slide.unregisterAllModelChangedObservers(); 379 notifyModelChanged(true); 380 return true; 381 } 382 return false; 383 } 384 385 public boolean removeAll(Collection<?> collection) { 386 throw new UnsupportedOperationException("Operation not supported."); 387 } 388 389 public boolean retainAll(Collection<?> collection) { 390 throw new UnsupportedOperationException("Operation not supported."); 391 } 392 393 public int size() { 394 return mSlides.size(); 395 } 396 397 public Object[] toArray() { 398 return mSlides.toArray(); 399 } 400 401 public <T> T[] toArray(T[] array) { 402 return mSlides.toArray(array); 403 } 404 405 public void add(int location, SlideModel object) { 406 if (object != null) { 407 int increaseSize = object.getSlideSize(); 408 checkMessageSize(increaseSize); 409 410 mSlides.add(location, object); 411 increaseMessageSize(increaseSize); 412 object.registerModelChangedObserver(this); 413 for (IModelChangedObserver observer : mModelChangedObservers) { 414 object.registerModelChangedObserver(observer); 415 } 416 notifyModelChanged(true); 417 } 418 } 419 420 public boolean addAll(int location, 421 Collection<? extends SlideModel> collection) { 422 throw new UnsupportedOperationException("Operation not supported."); 423 } 424 425 public SlideModel get(int location) { 426 return mSlides.get(location); 427 } 428 429 public int indexOf(Object object) { 430 return mSlides.indexOf(object); 431 } 432 433 public int lastIndexOf(Object object) { 434 return mSlides.lastIndexOf(object); 435 } 436 437 public ListIterator<SlideModel> listIterator() { 438 return mSlides.listIterator(); 439 } 440 441 public ListIterator<SlideModel> listIterator(int location) { 442 return mSlides.listIterator(location); 443 } 444 445 public SlideModel remove(int location) { 446 SlideModel slide = mSlides.remove(location); 447 if (slide != null) { 448 decreaseMessageSize(slide.getSlideSize()); 449 slide.unregisterAllModelChangedObservers(); 450 notifyModelChanged(true); 451 } 452 return slide; 453 } 454 455 public SlideModel set(int location, SlideModel object) { 456 SlideModel slide = mSlides.get(location); 457 if (null != object) { 458 int removeSize = 0; 459 int addSize = object.getSlideSize(); 460 if (null != slide) { 461 removeSize = slide.getSlideSize(); 462 } 463 if (addSize > removeSize) { 464 checkMessageSize(addSize - removeSize); 465 increaseMessageSize(addSize - removeSize); 466 } else { 467 decreaseMessageSize(removeSize - addSize); 468 } 469 } 470 471 slide = mSlides.set(location, object); 472 if (slide != null) { 473 slide.unregisterAllModelChangedObservers(); 474 } 475 476 if (object != null) { 477 object.registerModelChangedObserver(this); 478 for (IModelChangedObserver observer : mModelChangedObservers) { 479 object.registerModelChangedObserver(observer); 480 } 481 } 482 483 notifyModelChanged(true); 484 return slide; 485 } 486 487 public List<SlideModel> subList(int start, int end) { 488 return mSlides.subList(start, end); 489 } 490 491 @Override 492 protected void registerModelChangedObserverInDescendants( 493 IModelChangedObserver observer) { 494 mLayout.registerModelChangedObserver(observer); 495 496 for (SlideModel slide : mSlides) { 497 slide.registerModelChangedObserver(observer); 498 } 499 } 500 501 @Override 502 protected void unregisterModelChangedObserverInDescendants( 503 IModelChangedObserver observer) { 504 mLayout.unregisterModelChangedObserver(observer); 505 506 for (SlideModel slide : mSlides) { 507 slide.unregisterModelChangedObserver(observer); 508 } 509 } 510 511 @Override 512 protected void unregisterAllModelChangedObserversInDescendants() { 513 mLayout.unregisterAllModelChangedObservers(); 514 515 for (SlideModel slide : mSlides) { 516 slide.unregisterAllModelChangedObservers(); 517 } 518 } 519 520 public void onModelChanged(Model model, boolean dataChanged) { 521 if (dataChanged) { 522 mDocumentCache = null; 523 mPduBodyCache = null; 524 } 525 } 526 527 public void sync(PduBody pb) { 528 for (SlideModel slide : mSlides) { 529 for (MediaModel media : slide) { 530 PduPart part = pb.getPartByContentLocation(media.getSrc()); 531 if (part != null) { 532 media.setUri(part.getDataUri()); 533 } 534 } 535 } 536 } 537 538 public void checkMessageSize(int increaseSize) throws ContentRestrictionException { 539 ContentRestriction cr = ContentRestrictionFactory.getContentRestriction(); 540 cr.checkMessageSize(mCurrentMessageSize, increaseSize, mContentResolver); 541 } 542} 543