/**
   * @param context The context for the view.
   * @param controls Whether to show controls in the player.
   */
  public JWPlayer(Context context, boolean controls) {
    mModel = new Model(this);
    mController = new Controller(this, mModel);
    mView = new View(mModel, mController, context, controls);

    mEventLogger = new EventLogger(Log.DEBUG);
    // Log some interesting events
    mEventLogger.startForward(PlayerEvent.JWPLAYER_ERROR, mModel);
    mEventLogger.startForward(MediaEvent.JWPLAYER_MEDIA_BUFFER, mModel);
    mEventLogger.startForward(MediaEvent.JWPLAYER_MEDIA_SELECTED, mModel);
    mEventLogger.startForward(MediaEvent.JWPLAYER_MEDIA_BUFFER_FULL, mModel);
    mEventLogger.startForward(MediaEvent.JWPLAYER_MEDIA_COMPLETE, mModel);
    mEventLogger.startForward(MediaEvent.JWPLAYER_MEDIA_LOADED, mModel);
    mEventLogger.startForward(MediaEvent.JWPLAYER_MEDIA_SEEK, mModel);
    mEventLogger.startForward(MediaEvent.JWPLAYER_MEDIA_TIME, mModel);
    mEventLogger.startForward(MediaEvent.JWPLAYER_MEDIA_META, mModel);
    mEventLogger.startForward(PlayerStateEvent.JWPLAYER_PLAYER_STATE, mModel);
    mEventLogger.startForward(ViewEvent.JWPLAYER_VIEW_FULLSCREEN, mView);
    mEventLogger.startForward(QualityEvent.JWPLAYER_QUALITY_LEVELS, mModel);
    mEventLogger.startForward(QualityEvent.JWPLAYER_QUALITY_CHANGE, mModel);

    // Forward the mediaComplete event as onComplete
    mModel.addEventListener(
        MediaEvent.JWPLAYER_MEDIA_COMPLETE,
        new EventListener<MediaEvent>() {
          @Override
          public void handle(MediaEvent event) {
            if (mOnCompleteListener != null) {
              mOnCompleteListener.onComplete();
            }
          }
        });

    // Forward the playerState event as one of onPlay, onPause, onBuffer or
    // onIdle.
    mModel.addEventListener(
        PlayerStateEvent.JWPLAYER_PLAYER_STATE,
        new EventListener<PlayerStateEvent>() {
          @Override
          public void handle(PlayerStateEvent event) {
            switch (event.getNewState()) {
              case PLAYING:
                if (mOnPlayListener != null) {
                  mOnPlayListener.onPlay();
                }
                break;
              case PAUSED:
                if (mOnPauseListener != null) {
                  mOnPauseListener.onPause();
                }
                break;
              case BUFFERING:
                if (mOnBufferListener != null) {
                  mOnBufferListener.onBuffer();
                }
                break;
              case IDLE:
                if (mOnIdleListener != null) {
                  mOnIdleListener.onIdle();
                }
                break;
              default:
                break;
            }
          }
        });

    // Forward the mediaSeek event as onSeek
    mModel.addEventListener(
        MediaEvent.JWPLAYER_MEDIA_SEEK,
        new EventListener<MediaEvent>() {
          @Override
          public void handle(MediaEvent event) {
            if (mOnSeekListener != null) {
              mOnSeekListener.onSeek(event.getPosition(), event.getOffset());
            }
          }
        });

    // Forward the mediaTime event as onTime
    mModel.addEventListener(
        MediaEvent.JWPLAYER_MEDIA_TIME,
        new EventListener<MediaEvent>() {
          @Override
          public void handle(MediaEvent event) {
            // Do not forward for livestreams
            if (mOnTimeListener != null && !mModel.getMedia().isLive()) {
              mOnTimeListener.onTime(event.getPosition(), event.getDuration());
            }
          }
        });

    // Forward the playerError event as onError
    mModel.addEventListener(
        PlayerEvent.JWPLAYER_ERROR,
        new EventListener<PlayerEvent>() {
          @Override
          public void handle(PlayerEvent event) {
            if (mOnErrorListener != null) {
              mOnErrorListener.onError(event.getMessage());
            }
          }
        });

    // Forward the viewFullscreen event as onFullscreen
    mView.addEventListener(
        ViewEvent.JWPLAYER_VIEW_FULLSCREEN,
        new EventListener<ViewEvent>() {
          @Override
          public void handle(ViewEvent event) {
            if (mOnFullscreenListener != null) {
              mOnFullscreenListener.onFullscreen(event.isEnabled());
            }
          }
        });

    // Forward the qualityLevels event as onQualityLevels
    mModel.addEventListener(
        QualityEvent.JWPLAYER_QUALITY_LEVELS,
        new EventListener<QualityEvent>() {
          @Override
          public void handle(QualityEvent event) {
            if (mOnQualityLevelsListener != null) {
              mOnQualityLevelsListener.onQualityLevels(event.getLevels());
            }
          }
        });

    // Forward the qualityChange event as onQualityChange
    mModel.addEventListener(
        QualityEvent.JWPLAYER_QUALITY_CHANGE,
        new EventListener<QualityEvent>() {
          @Override
          public void handle(QualityEvent event) {
            if (mOnQualityChangeListener != null) {
              mOnQualityChangeListener.onQualityChange(event.getCurrentLevel());
            }
          }
        });
  }
 /** Release the resources that are in use by the player. */
 public void release() {
   mModel.release();
 }
 /** @param index The index of the desired quality level. */
 public void setCurrentQuality(int index) {
   mModel.setCurrentQuality(index);
 }
 /** @return The index of the current quality level. */
 public int getCurrentQuality() {
   return mModel.getCurrentQuality();
 }
 /** @return The array of available quality levels. */
 public QualityLevel[] getQualityLevels() {
   return mModel.getQualityLevels();
 }
 /** @return The current state of the media being played. */
 public PlayerState getState() {
   return mModel.getState();
 }