public static void viewSimpleSlideshow(Context context, SlideshowModel slideshow) {
    if (!slideshow.isSimple()) {
      throw new IllegalArgumentException("viewSimpleSlideshow() called on a non-simple slideshow");
    }
    SlideModel slide = slideshow.get(0);
    MediaModel mm = null;
    if (slide.hasImage()) {
      mm = slide.getImage();
    } else if (slide.hasVideo()) {
      mm = slide.getVideo();
    }

    Intent intent = new Intent(Intent.ACTION_VIEW);
    intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
    intent.putExtra("SingleItemOnly", true); // So we don't see "surrounding" images in Gallery

    String contentType;
    if (mm.isDrmProtected()) {
      contentType = mm.getDrmObject().getContentType();
    } else {
      contentType = mm.getContentType();
    }
    intent.setDataAndType(mm.getUri(), contentType);
    context.startActivity(intent);
  }
  @Override
  protected void unregisterModelChangedObserverInDescendants(IModelChangedObserver observer) {
    mLayout.unregisterModelChangedObserver(observer);

    for (SlideModel slide : mSlides) {
      slide.unregisterModelChangedObserver(observer);
    }
  }
  @Override
  protected void unregisterAllModelChangedObserversInDescendants() {
    mLayout.unregisterAllModelChangedObservers();

    for (SlideModel slide : mSlides) {
      slide.unregisterAllModelChangedObservers();
    }
  }
 public SlideModel remove(int location) {
   SlideModel slide = mSlides.remove(location);
   if (slide != null) {
     decreaseMessageSize(slide.getSlideSize());
     slide.unregisterAllModelChangedObservers();
     notifyModelChanged(true);
   }
   return slide;
 }
 public boolean remove(Object object) {
   if ((object != null) && mSlides.remove(object)) {
     SlideModel slide = (SlideModel) object;
     decreaseMessageSize(slide.getSlideSize());
     slide.unregisterAllModelChangedObservers();
     notifyModelChanged(true);
     return true;
   }
   return false;
 }
  /**
   * Determines whether this is a "simple" slideshow. Criteria: - Exactly one slide - Exactly one
   * multimedia attachment, but no audio - It can optionally have a caption
   */
  public boolean isSimple() {
    // There must be one (and only one) slide.
    if (size() != 1) return false;

    SlideModel slide = get(0);
    // The slide must have either an image or video, but not both.
    if (!(slide.hasImage() ^ slide.hasVideo())) return false;

    // No audio allowed.
    if (slide.hasAudio()) return false;

    return true;
  }
 public void clear() {
   if (mSlides.size() > 0) {
     for (SlideModel slide : mSlides) {
       slide.unregisterModelChangedObserver(this);
       for (IModelChangedObserver observer : mModelChangedObservers) {
         slide.unregisterModelChangedObserver(observer);
       }
     }
     mCurrentMessageSize = 0;
     mSlides.clear();
     notifyModelChanged(true);
   }
 }
 public void onClick(View v) {
   SlideModel slide = mSlideshowModel.get(mPosition);
   if (slide != null && slide.hasVideo()) {
     Toast.makeText(
             SlideEditorActivity.this,
             R.string.cannot_add_picture_and_video,
             Toast.LENGTH_SHORT)
         .show();
     return;
   }
   Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
   intent.setType(ContentType.IMAGE_UNSPECIFIED);
   startActivityForResult(intent, REQUEST_CODE_CHANGE_PICTURE);
 }
  public void add(int location, SlideModel object) {
    if (object != null) {
      int increaseSize = object.getSlideSize();
      checkMessageSize(increaseSize);

      mSlides.add(location, object);
      increaseMessageSize(increaseSize);
      object.registerModelChangedObserver(this);
      for (IModelChangedObserver observer : mModelChangedObservers) {
        object.registerModelChangedObserver(observer);
      }
      notifyModelChanged(true);
    }
  }
  //
  // Implement List<E> interface.
  //
  public boolean add(SlideModel object) {
    int increaseSize = object.getSlideSize();
    checkMessageSize(increaseSize);

    if ((object != null) && mSlides.add(object)) {
      increaseMessageSize(increaseSize);
      object.registerModelChangedObserver(this);
      for (IModelChangedObserver observer : mModelChangedObservers) {
        object.registerModelChangedObserver(observer);
      }
      notifyModelChanged(true);
      return true;
    }
    return false;
  }
  private SlideshowModel(
      LayoutModel layouts,
      ArrayList<SlideModel> slides,
      SMILDocument documentCache,
      PduBody pbCache,
      ContentResolver contentResolver) {
    mLayout = layouts;
    mSlides = slides;
    mContentResolver = contentResolver;

    mDocumentCache = documentCache;
    mPduBodyCache = pbCache;
    for (SlideModel slide : mSlides) {
      increaseMessageSize(slide.getSlideSize());
      slide.setParent(this);
    }
  }
  public static int getAttachmentType(SlideshowModel model) {
    if (model == null) {
      return WorkingMessage.TEXT;
    }

    int numberOfSlides = model.size();
    if (numberOfSlides > 1) {
      return WorkingMessage.SLIDESHOW;
    } else if (numberOfSlides == 1) {
      // Only one slide in the slide-show.
      SlideModel slide = model.get(0);
      if (slide.hasVideo()) {
        return WorkingMessage.VIDEO;
      }

      if (slide.hasAudio() && slide.hasImage()) {
        return WorkingMessage.SLIDESHOW;
      }

      if (slide.hasAudio()) {
        return WorkingMessage.AUDIO;
      }

      if (slide.hasImage()) {
        return WorkingMessage.IMAGE;
      }

      if (slide.hasText()) {
        return WorkingMessage.TEXT;
      }
    }

    return WorkingMessage.TEXT;
  }
  public SlideModel set(int location, SlideModel object) {
    SlideModel slide = mSlides.get(location);
    if (null != object) {
      int removeSize = 0;
      int addSize = object.getSlideSize();
      if (null != slide) {
        removeSize = slide.getSlideSize();
      }
      if (addSize > removeSize) {
        checkMessageSize(addSize - removeSize);
        increaseMessageSize(addSize - removeSize);
      } else {
        decreaseMessageSize(removeSize - addSize);
      }
    }

    slide = mSlides.set(location, object);
    if (slide != null) {
      slide.unregisterAllModelChangedObservers();
    }

    if (object != null) {
      object.registerModelChangedObserver(this);
      for (IModelChangedObserver observer : mModelChangedObservers) {
        object.registerModelChangedObserver(observer);
      }
    }

    notifyModelChanged(true);
    return slide;
  }
    private View createViewFromResource(int position, View convertView, int resource) {
      SlideListItemView slideListItemView;
      slideListItemView = (SlideListItemView) mInflater.inflate(resource, null);

      // Show slide number.
      TextView text;
      text = (TextView) slideListItemView.findViewById(R.id.slide_number_text);
      text.setText(mContext.getString(R.string.slide_number, position + 1));

      SlideModel slide = getItem(position);
      int dur = slide.getDuration() / 1000;
      text = (TextView) slideListItemView.findViewById(R.id.duration_text);
      text.setText(mContext.getResources().getQuantityString(R.plurals.slide_duration, dur, dur));

      Presenter presenter =
          PresenterFactory.getPresenter(
              "SlideshowPresenter", mContext, slideListItemView, mSlideshow);
      ((SlideshowPresenter) presenter).setLocation(position);
      presenter.present();

      return slideListItemView;
    }
  private void bindCommonMessage() {
    if (mDownloadButton != null) {
      mDownloadButton.setVisibility(View.GONE);
      mDownloadingLabel.setVisibility(View.GONE);
    }
    // Since the message text should be concatenated with the sender's
    // address(or name), I have to display it here instead of
    // displaying it by the Presenter.
    mBodyTextView.setTransformationMethod(HideReturnsTransformationMethod.getInstance());

    boolean isSelf = Sms.isOutgoingFolder(mMessageItem.mBoxId);
    String addr = isSelf ? null : mMessageItem.mAddress;
    updateAvatarView(addr, isSelf);

    // Get and/or lazily set the formatted message from/on the
    // MessageItem.  Because the MessageItem instances come from a
    // cache (currently of size ~50), the hit rate on avoiding the
    // expensive formatMessage() call is very high.
    CharSequence formattedMessage = mMessageItem.getCachedFormattedMessage();
    if (formattedMessage == null) {
      formattedMessage =
          formatMessage(
              mMessageItem,
              mMessageItem.mContact,
              mMessageItem.mBody,
              mMessageItem.mSubject,
              mMessageItem.mHighlight,
              mMessageItem.mTextContentType);
      mMessageItem.setCachedFormattedMessage(formattedMessage);
    }
    mBodyTextView.setText(formattedMessage);

    // Debugging code to put the URI of the image attachment in the body of the list item.
    if (DEBUG) {
      String debugText = null;
      if (mMessageItem.mSlideshow == null) {
        debugText = "NULL slideshow";
      } else {
        SlideModel slide = ((SlideshowModel) mMessageItem.mSlideshow).get(0);
        if (slide == null) {
          debugText = "NULL first slide";
        } else if (!slide.hasImage()) {
          debugText = "Not an image";
        } else {
          debugText = slide.getImage().getUri().toString();
        }
      }
      mBodyTextView.setText(mPosition + ": " + debugText);
    }

    // If we're in the process of sending a message (i.e. pending), then we show a "SENDING..."
    // string in place of the timestamp.
    mDateView.setText(
        mMessageItem.isSending()
            ? mContext.getResources().getString(R.string.sending_message)
            : mMessageItem.mTimestamp);

    if (mMessageItem.isSms()) {
      showMmsView(false);
      mMessageItem.setOnPduLoaded(null);
    } else {
      if (DEBUG) {
        Log.v(
            TAG,
            "bindCommonMessage for item: "
                + mPosition
                + " "
                + mMessageItem.toString()
                + " mMessageItem.mAttachmentType: "
                + mMessageItem.mAttachmentType);
      }
      if (mMessageItem.mAttachmentType != WorkingMessage.TEXT) {
        setImage(null, null);
        setOnClickListener(mMessageItem);
        drawPlaybackButton(mMessageItem);
      } else {
        showMmsView(false);
      }
      if (mMessageItem.mSlideshow == null) {
        mMessageItem.setOnPduLoaded(
            new MessageItem.PduLoadedCallback() {
              public void onPduLoaded(MessageItem messageItem) {
                if (DEBUG) {
                  Log.v(
                      TAG,
                      "PduLoadedCallback in MessageListItem for item: "
                          + mPosition
                          + " "
                          + (mMessageItem == null ? "NULL" : mMessageItem.toString())
                          + " passed in item: "
                          + (messageItem == null ? "NULL" : messageItem.toString()));
                }
                if (messageItem != null
                    && mMessageItem != null
                    && messageItem.getMessageId() == mMessageItem.getMessageId()) {
                  mMessageItem.setCachedFormattedMessage(null);
                  bindCommonMessage();
                }
              }
            });
      } else {
        if (mPresenter == null) {
          mPresenter =
              PresenterFactory.getPresenter(
                  "MmsThumbnailPresenter", mContext, this, mMessageItem.mSlideshow);
        } else {
          mPresenter.setModel(mMessageItem.mSlideshow);
          mPresenter.setView(this);
        }
        if (mImageLoadedCallback == null) {
          mImageLoadedCallback = new ImageLoadedCallback(this);
        } else {
          mImageLoadedCallback.reset(this);
        }
        mPresenter.present(mImageLoadedCallback);
      }
    }
    drawRightStatusIndicator(mMessageItem);

    requestLayout();
  }
  @Override
  public boolean onPrepareOptionsMenu(Menu menu) {
    if (isFinishing()) {
      return false;
    }
    menu.clear();

    SlideModel slide = mSlideshowModel.get(mPosition);

    if (slide == null) {
      return false;
    }

    // Preview slideshow.
    menu.add(0, MENU_PREVIEW_SLIDESHOW, 0, R.string.preview_slideshow)
        .setIcon(com.android.internal.R.drawable.ic_menu_play_clip);

    // Text
    if (slide.hasText() && !TextUtils.isEmpty(slide.getText().getText())) {
      // "Change text" if text is set.
      menu.add(0, MENU_REMOVE_TEXT, 0, R.string.remove_text)
          .setIcon(R.drawable.ic_menu_remove_text);
    }

    // Picture
    if (slide.hasImage()) {
      menu.add(0, MENU_DEL_PICTURE, 0, R.string.remove_picture)
          .setIcon(R.drawable.ic_menu_remove_picture);
    } else if (!slide.hasVideo()) {
      menu.add(0, MENU_ADD_PICTURE, 0, R.string.add_picture).setIcon(R.drawable.ic_menu_picture);
      menu.add(0, MENU_TAKE_PICTURE, 0, R.string.attach_take_photo)
          .setIcon(R.drawable.ic_menu_picture);
    }

    // Music
    if (slide.hasAudio()) {
      menu.add(0, MENU_DEL_AUDIO, 0, R.string.remove_music)
          .setIcon(R.drawable.ic_menu_remove_sound);
    } else if (!slide.hasVideo()) {
      if (MmsConfig.getAllowAttachAudio()) {
        SubMenu subMenu =
            menu.addSubMenu(0, MENU_SUB_AUDIO, 0, R.string.add_music)
                .setIcon(R.drawable.ic_menu_add_sound);
        subMenu.add(0, MENU_ADD_AUDIO, 0, R.string.attach_sound);
        subMenu.add(0, MENU_RECORD_SOUND, 0, R.string.attach_record_sound);
      } else {
        menu.add(0, MENU_RECORD_SOUND, 0, R.string.attach_record_sound)
            .setIcon(R.drawable.ic_menu_add_sound);
      }
    }

    // Video
    if (slide.hasVideo()) {
      menu.add(0, MENU_DEL_VIDEO, 0, R.string.remove_video)
          .setIcon(R.drawable.ic_menu_remove_video);
    } else if (!slide.hasAudio() && !slide.hasImage()) {
      menu.add(0, MENU_ADD_VIDEO, 0, R.string.add_video).setIcon(R.drawable.ic_menu_movie);
      menu.add(0, MENU_TAKE_VIDEO, 0, R.string.attach_record_video)
          .setIcon(R.drawable.ic_menu_movie);
    }

    // Add slide
    menu.add(0, MENU_ADD_SLIDE, 0, R.string.add_slide).setIcon(R.drawable.ic_menu_add_slide);

    // Slide duration
    String duration = getResources().getString(R.string.duration_sec);
    menu.add(
            0, MENU_DURATION, 0, duration.replace("%s", String.valueOf(slide.getDuration() / 1000)))
        .setIcon(R.drawable.ic_menu_duration);

    // Slide layout
    int resId;
    if (mSlideshowModel.getLayout().getLayoutType() == LayoutModel.LAYOUT_TOP_TEXT) {
      resId = R.string.layout_top;
    } else {
      resId = R.string.layout_bottom;
    }
    // FIXME: set correct icon when layout icon is available.
    menu.add(0, MENU_LAYOUT, 0, resId).setIcon(R.drawable.ic_menu_picture);
    return true;
  }
  @Override
  public boolean onOptionsItemSelected(MenuItem item) {
    switch (item.getItemId()) {
      case MENU_PREVIEW_SLIDESHOW:
        previewSlideshow();
        break;

      case MENU_REMOVE_TEXT:
        SlideModel slide = mSlideshowModel.get(mPosition);
        if (slide != null) {
          slide.removeText();
        }
        break;

      case MENU_ADD_PICTURE:
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT, null);
        intent.setType(ContentType.IMAGE_UNSPECIFIED);
        startActivityForResult(intent, REQUEST_CODE_CHANGE_PICTURE);
        break;

      case MENU_TAKE_PICTURE:
        intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        // We have to pass a uri to store the picture data, otherwise the camera will return
        // a very small image bitmap.
        intent.putExtra(MediaStore.EXTRA_OUTPUT, TempFileProvider.SCRAP_CONTENT_URI);
        startActivityForResult(intent, REQUEST_CODE_TAKE_PICTURE);
        break;

      case MENU_DEL_PICTURE:
        mSlideshowEditor.removeImage(mPosition);
        setReplaceButtonText(R.string.add_picture);
        break;

      case MENU_ADD_AUDIO:
        MessageUtils.selectAudio(this, REQUEST_CODE_CHANGE_MUSIC);
        break;

      case MENU_RECORD_SOUND:
        slide = mSlideshowModel.get(mPosition);
        int currentSlideSize = slide.getSlideSize();
        long sizeLimit =
            ComposeMessageActivity.computeAttachmentSizeLimit(mSlideshowModel, currentSlideSize);
        MessageUtils.recordSound(this, REQUEST_CODE_RECORD_SOUND, sizeLimit);
        break;

      case MENU_DEL_AUDIO:
        mSlideshowEditor.removeAudio(mPosition);
        break;

      case MENU_ADD_VIDEO:
        intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType(ContentType.VIDEO_UNSPECIFIED);
        intent.putExtra(Intent.EXTRA_LOCAL_ONLY, true);
        startActivityForResult(intent, REQUEST_CODE_CHANGE_VIDEO);
        break;

      case MENU_TAKE_VIDEO:
        slide = mSlideshowModel.get(mPosition);
        currentSlideSize = slide.getSlideSize();
        sizeLimit =
            ComposeMessageActivity.computeAttachmentSizeLimit(mSlideshowModel, currentSlideSize);
        if (sizeLimit > 0) {
          MessageUtils.recordVideo(this, REQUEST_CODE_TAKE_VIDEO, sizeLimit);
        } else {
          Toast.makeText(this, getString(R.string.message_too_big_for_video), Toast.LENGTH_SHORT)
              .show();
        }
        break;

      case MENU_DEL_VIDEO:
        mSlideshowEditor.removeVideo(mPosition);
        break;

      case MENU_ADD_SLIDE:
        mPosition++;
        if (mSlideshowEditor.addNewSlide(mPosition)) {
          // add successfully
          showCurrentSlide();
        } else {
          // move position back
          mPosition--;
          Toast.makeText(this, R.string.cannot_add_slide_anymore, Toast.LENGTH_SHORT).show();
        }
        break;

      case MENU_LAYOUT:
        showLayoutSelectorDialog();
        break;

      case MENU_DURATION:
        showDurationDialog();
        break;
    }

    return true;
  }
  public static SlideshowModel createFromPduBody(Context context, PduBody pb) throws MmsException {
    SMILDocument document = SmilHelper.getDocument(pb);

    // Create root-layout model.
    SMILLayoutElement sle = document.getLayout();
    SMILRootLayoutElement srle = sle.getRootLayout();
    int w = srle.getWidth();
    int h = srle.getHeight();
    if ((w == 0) || (h == 0)) {
      w = LayoutManager.getInstance().getLayoutParameters().getWidth();
      h = LayoutManager.getInstance().getLayoutParameters().getHeight();
      srle.setWidth(w);
      srle.setHeight(h);
    }
    RegionModel rootLayout = new RegionModel(null, 0, 0, w, h);

    // Create region models.
    ArrayList<RegionModel> regions = new ArrayList<RegionModel>();
    NodeList nlRegions = sle.getRegions();
    int regionsNum = nlRegions.getLength();

    for (int i = 0; i < regionsNum; i++) {
      SMILRegionElement sre = (SMILRegionElement) nlRegions.item(i);
      RegionModel r =
          new RegionModel(
              sre.getId(),
              sre.getFit(),
              sre.getLeft(),
              sre.getTop(),
              sre.getWidth(),
              sre.getHeight(),
              sre.getBackgroundColor());
      regions.add(r);
    }
    LayoutModel layouts = new LayoutModel(rootLayout, regions);

    // Create slide models.
    SMILElement docBody = document.getBody();
    NodeList slideNodes = docBody.getChildNodes();
    int slidesNum = slideNodes.getLength();
    ArrayList<SlideModel> slides = new ArrayList<SlideModel>(slidesNum);

    for (int i = 0; i < slidesNum; i++) {
      // FIXME: This is NOT compatible with the SMILDocument which is
      // generated by some other mobile phones.
      SMILParElement par = (SMILParElement) slideNodes.item(i);

      // Create media models for each slide.
      NodeList mediaNodes = par.getChildNodes();
      int mediaNum = mediaNodes.getLength();
      ArrayList<MediaModel> mediaSet = new ArrayList<MediaModel>(mediaNum);

      for (int j = 0; j < mediaNum; j++) {
        SMILMediaElement sme = (SMILMediaElement) mediaNodes.item(j);
        try {
          MediaModel media = MediaModelFactory.getMediaModel(context, sme, layouts, pb);
          SmilHelper.addMediaElementEventListeners((EventTarget) sme, media);
          mediaSet.add(media);
        } catch (DrmException e) {
          Log.e(TAG, e.getMessage(), e);
        } catch (IOException e) {
          Log.e(TAG, e.getMessage(), e);
        } catch (IllegalArgumentException e) {
          Log.e(TAG, e.getMessage(), e);
        }
      }

      SlideModel slide = new SlideModel((int) (par.getDur() * 1000), mediaSet);
      slide.setFill(par.getFill());
      SmilHelper.addParElementEventListeners((EventTarget) par, slide);
      slides.add(slide);
    }

    SlideshowModel slideshow =
        new SlideshowModel(layouts, slides, document, pb, context.getContentResolver());
    slideshow.registerModelChangedObserver(slideshow);
    return slideshow;
  }