package com.viaccessorca.voplayerdemo.activities;


import android.annotation.TargetApi;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.LightingColorFilter;
import android.graphics.drawable.Drawable;
import android.media.AudioManager;
import android.net.ConnectivityManager;
import android.net.NetworkInfo;
import android.net.Uri;
import android.net.wifi.WifiInfo;
import android.net.wifi.WifiManager;
import android.os.Build;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.os.Handler;
import android.os.Looper;
import android.os.Message;
import android.preference.PreferenceManager;
import android.support.v4.graphics.drawable.DrawableCompat;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.app.MediaRouteButton;
import android.telephony.TelephonyManager;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.GestureDetector;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.view.WindowManager;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.webkit.WebSettings;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.ScrollView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
import android.widget.Toast;

//CONVIVA_ANALYTICS: import com.conviva.api.ContentMetadata;
import com.google.android.gms.cast.MediaMetadata;
import com.google.android.gms.cast.MediaStatus;
import com.google.android.gms.cast.framework.CastButtonFactory;
import com.google.android.gms.cast.framework.CastContext;
import com.google.android.gms.cast.framework.Session;
import com.google.android.gms.cast.framework.SessionManager;
import com.google.android.gms.cast.framework.SessionManagerListener;
import com.google.android.gms.cast.framework.media.RemoteMediaClient;
import com.google.android.gms.common.images.WebImage;
import com.npaw.youbora.lib6.YouboraLog;
import com.npaw.youbora.lib6.plugin.Options;
import com.npaw.youbora.lib6.plugin.Plugin;
import com.npaw.youbora.lib6.utils.youboraconfigutils.YouboraConfigManager;
import com.npaw.youbora.lib6.viaccessorca.ViaccessorcaAdapter;
import com.npaw.youbora.lib6.viaccessorca.ViaccessorcaAdsAdapter;
import com.viaccessorca.drm.VOWidevineAgent;
import com.viaccessorca.drm.impl.DrmAgent;
import com.viaccessorca.exceptions.VOException;
import com.viaccessorca.voplayer.VODownloadedFragment;
import com.viaccessorca.voplayer.VOSurfaceView;
import com.viaccessorca.common.VOUtils;
import com.viaccessorca.voplayer.VODeviceAdaptation;
import com.viaccessorca.voplayer.VOAlternateInfo;
import com.viaccessorca.voplayer.VOAudioTrack;
import com.viaccessorca.voplayer.VOPlayer;
import com.viaccessorca.voplayer.VOPlayer.CustomMode;
import com.viaccessorca.voplayer.VOSecondaryContent;
import com.viaccessorca.voplayer.VOSubtitle;
import com.viaccessorca.voplayer.VOSubtitleTrack;
import com.viaccessorca.voplayer.VOFlat360Manager;
import com.viaccessorca.voplayer.VOSystemInfo;
import com.viaccessorca.voplayer.VOSystemInfoRetriever;
import com.viaccessorca.voplayerdemo.Constants;
import com.viaccessorca.voplayerdemo.R;
import com.viaccessorca.voplayerdemo.Utils;
import com.viaccessorca.voplayerdemo.adapters.BasicStringAdapter;
import com.viaccessorca.voplayerdemo.adapters.SubtitleListAdapter;
//YOUBORA_ANALYTICS: import com.viaccessorca.voplayerdemo.analytics.LVYouboraPlugin;
//CONVIVA_ANALYTICS: import com.viaccessorca.voplayerdemo.analytics.LVConvivaPlugin;
//AGAMA_ANALYTICS: import com.viaccessorca.voplayerdemo.analytics.LVAgamaPlugin;
import com.viaccessorca.voplayerdemo.database.DatabaseInfo;
import com.viaccessorca.voplayerdemo.database.MediaMetaInfoDB;
import com.viaccessorca.voplayerdemo.demo.LocalServerDemo;
import com.viaccessorca.voplayerdemo.demo.FastStartupDemo;
import com.viaccessorca.voplayerdemo.demo.VoDemo;
import com.viaccessorca.voplayerdemo.downloader.DownloadManager;
import com.viaccessorca.voplayerdemo.downloader.DownloaderWrapper;
import com.viaccessorca.voplayerdemo.mediamgt.LocalMediasFinder;
import com.viaccessorca.voplayerdemo.mediamgt.MediaItem;
import com.viaccessorca.voplayerdemo.mediamgt.MediaTypeMapper;
import com.viaccessorca.voplayerdemo.mediamgt.StreamingMediaItem;
import com.viaccessorca.voplayerdemo.mediamgt.StreamingMediaList;
import com.viaccessorca.voplayerdemo.quickaction.*;
import com.viaccessorca.voplayerdemo.quickaction.SliderPopup.OnSliderButtonClickListener;
import com.viaccessorca.voplayerdemo.quickaction.SliderPopup.OnSliderPopupDismissListener;
import com.viaccessorca.voplayerdemo.quickaction.SliderPopup.OnSliderPopupUpdateListener;
import com.viaccessorca.voplayerdemo.quickaction.QuickAction.OnActionItemClickListener;
import com.viaccessorca.voplayerdemo.utils.LVID3MetadataParser;
import com.viaccessorca.voplayerdemo.utils.VORenderingWebView;


import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;

import android.view.GestureDetector.SimpleOnGestureListener;
import static com.viaccessorca.voplayerdemo.downloader.DownloaderWrapper.State.DOWNLOADING;


public class PlayerActivity extends AppCompatActivity implements View.OnClickListener,
        VOPlayer.OnInfoListener,
        VOPlayer.OnPreparedListener,
        VOPlayer.OnSeekCompleteListener,
        VOPlayer.OnCompletionListener,
        VOPlayer.OnVideoSizeChangedListener,
        VOPlayer.OnBufferingUpdateListener,
        VOPlayer.OnDownloadUpdateListener,
        VOPlayer.OnErrorListener,
        OnSeekBarChangeListener,
        SurfaceHolder.Callback,
        OnSliderPopupDismissListener,
        OnSliderPopupUpdateListener,
        OnSliderButtonClickListener,
        OnActionItemClickListener,
        DownloaderWrapper.OnInfoListener,
        AnimationListener,
        View.OnTouchListener {

    /*************************************************************/
    /******************* Enum definitions ************************/
    /*************************************************************/
    // Possible states for the play/pause button
    private enum PlayPauseBtnState {
        PLAY,
        PAUSE,
        DISABLE
    }

    // UI Visibility
    private enum ControlsState {
        UI_READY_TO_SHOW,
        UI_READY_TO_HIDE,
        UI_SHOWING,
        UI_HIDING
    }

    // Types of streams
    private enum StreamType {
        LOCAL_FILE,         // local file
        HTTPSTR_UNKNOWN,    // HTTP Streaming unknown mode
        HTTPSTR_VOD,        // HTTP Streaming in VOD mode
        HTTPSTR_LIVE,       // HTTP Streaming in live mode
        HTTPSTR_EVENT,      // HTTP Streaming in Event mode
        HTTP_PGDL,          // HTTP Progressive Download mode
        UNKNOWN,
    }

    // Side panel sections
    private enum SideSection {
        GENERIC_INFO,
        TRACKS,
        ABR_STREAMING
    }

    /*************************************************************/
    /************** Static variables declaration *****************/
    /*************************************************************/

    private static final String TAG = "PlayerActivity";
    private static final int SUBTITLE_FILES_LIST_DIALOG = 0;
    private static final int AUDIO_TRACKS_LIST_DIALOG = 1;
    private static final int ALTERNATES_LIST_DIALOG = 2;

    private static final String MEMORY_MANAGER_DUMP_FILE = Environment.getExternalStorageDirectory().getAbsolutePath() + "/qp3_medias/QPMemoryLeak.log";

    private static final int HTTPSTR_DOWNLOAD_DATA_DOWNLOADED_DELTA = 2000;

    /**
     * Actions available in quickaction menu
     */
    private static final int ACTION_ID_SELECT_SUBTITLE = 0;
    private static final int ACTION_ID_CLOSE_SUBTITLE = 1;
    private static final int ACTION_ID_SELECT_SUBTITLE_TRACK = 3;
    private static final int ACTION_ID_SELECT_CC_TRACK = 4;
    private static final int ACTION_ID_CLOSE_CC_TRACK = 5;
    private static final int ACTION_ID_SELECT_WEBVTT_TRACK = 6;
    private static final int ACTION_ID_CLOSE_WEBVTT_TRACK = 7;
    private static final int ACTION_ID_SELECT_SMPTE_TRACK = 8;
    private static final int ACTION_ID_CLOSE_SMPTE_TRACK = 9;
    private static final int ACTION_ID_SELECT_TTML_TRACK = 10;
    private static final int ACTION_ID_CLOSE_TTML_TRACK = 11;
    private static final int ACTION_ID_SELECT_DVB_TRACK = 12;
    private static final int ACTION_ID_CLOSE_DVB_TRACK = 13;

    /**
     * Default values used for ABR variant choice
     */
    private static final int TYPICAL_HTTP_STREAMING_BITRATE_WIFI = 2048; // in kbps
    private static final int TYPICAL_HTTP_STREAMING_BITRATE_3G = 256;  // in kbps

    /**
     * Period for playback progression update in ms
     */
    private static final int PROGRESS_UPDATE_TIMER_PERIOD = 500;

    /**
     * delay for the auto-hide of the controls (in ms)
     */
    private static final int AUTO_HIDE_DELAY = 3000;
    private static final long ANIMATION_DURATION = 200;

    /**
     * DRM flags
     */
    private static final int NO_DRM_FLAG = 0;
    private static final int SECURED_ABR_FLAG = 1;
    private static final int DRM_LOCKED_FLAG = 2;
    private static final int DRM_UNLOCKED_FLAG = 3;

    /*************************************************************/
    /***************** Class members declaration *****************/
    /*************************************************************/

    /**************************/
    /****** UI ELEMENTS *******/
    /**************************/
    // system status
    private TextView mBatteryUIText = null;
    private TextView mWifiNetworkText = null;
    private ImageView mCellularNetworkImg = null;
    private ImageButton mWifiToggleBtn = null;

    // playback controls
    private Button mJumpToLiveBtn = null;
    private ImageButton mPlayPauseBtn = null;
    private Button mSkipAdBtn = null;
    private ImageButton mDisplayBtn = null;
    private Drawable mPlayBtnDrawable = null;
    private Drawable mPauseBtnDrawable = null;
    private SeekBar mSeekbar = null;
    private Button mSubtitleActionBtn = null;
    private Button mAudioTrackBtn = null;
    private Button mAbrStreamingMinBRBtn = null;
    private Button mAbrStreamingMaxBRBtn = null;
    private Button mAudioBoosterBtn = null;
    private Button mVideoBoosterBtn = null;
    private Button mVEBoostBtn = null;
    private Button mId3MetadataBtn = null;
    private Button mFastZappingPPlusBtn = null;
    private Button mFastZappingPMinusBtn = null;
    private Button mAbrStreamingCustomBRUpBtn = null;
    private Button mAbrStreamingCustomBRDownBtn = null;
    private Button mSetAlternateBtn = null;

    // playback views
    private VOSurfaceView mDisplaySurface = null; // this is the surface where the video will be displayed
    private TextView mId3TextView = null;
    private ImageView mId3ImageView = null;
    private LinearLayout mId3Layout = null;

    // playback status
    private TextView mMediaNameUIText = null;
    private FrameLayout mNotificationLayout = null;
    private TextView mNotificationText = null;
    private TextView mHwCodecIndicText = null;
    private TextView mElapsedTimeTxt = null;
    private TextView mDurationTxt = null;
    private Drawable mAudioOnlyDrawable = null;
    private TextView mAbrStreamingVariantBRText = null;
    private TextView mAbrStreamingCurrentThroughPutBR = null;
    private TextView mAbrStreamingCurrentBufferLevel = null;
    private TextView mVideoResolutionText = null;
    private TextView mVideoBoosterText = null;
    private TextView mOverlayBitrateText = null;
    private TextView mOverlayVResolutionText = null;

    // others elements
    private LinearLayout mSidePanel = null;
    private ImageButton mGenericOptBtn = null;
    private ImageButton mAbrStreamingOptBtn = null;
    private ImageButton mTracksOptBtn = null;
    private LightingColorFilter mSelectedBtnColorFilter = null;
    private SliderPopup mSliderPopup = null;
    private TextView mAdditionalInfoText = null;
    private View mControlsLayout = null;
    private LinearLayout mOverlayInfosLayout = null;
    private LinearLayout mGenericOptSection = null;
    private ScrollView mAbrStreamingOptSection = null;
    private LinearLayout mTracksOptSection = null;

    /**************************/
    /**** OTHERS VARIABLES ****/
    /**************************/
    // internal events listener
    private GestureDetector mGestureDetector = null;
    private GestureDetector mGestureDetector360 = null;
    private ProgressUpdateCDT mProgressUpdateTimer = null;

    //playback variables
    private VOPlayer mMediaPlayer = null; //instance of the mediaplayer
    private boolean mSurfaceCreated = false;

    private Uri mMediaUri = null;
    private StreamType mMediaType = StreamType.UNKNOWN;
    private String mStoredName = null;

    private boolean mCanSeek = false;
    private boolean mLoopingPlayback = false;
    private double mResumePlaybackPosition = 0;
    private boolean mIsInErrorState = false;
    private int mCurrentStream = 0;
    private boolean mWasPlaying = false;
    private boolean mbBufferingIndicationEnabled = true;
    private int mCurrentDisplayMode;
    private int mJumpAllowedRangeStartTime = -1;
    private int mJumpAllowedRangeEndTime = -1;
    private PlayPauseBtnState mPlayPauseState = PlayPauseBtnState.DISABLE;
    private boolean mbShowPlayerInfo = false;
    private boolean mbSetAdSeeToAllowed = false;
    private boolean mbPreparedCBReceived = false;

    // subtitles
    private String mSubtitleFilePath = null;
    private String mSubtitleTrackName = null;
    private int mSubtitleTrackType = VOSubtitle.Type_Unknown;
    private List<MediaItem> mSubtitleFilesList = null;
    private SubtitleListAdapter mSubtitlesListAdapter = null;
    private boolean mbEnableSubtitleRendering = true;
    private String mCustomSubtitleCSSStyling = null;

    // audio
    private String mCurrentAudioTrackName = null;
    String[] mAudioTrackNameList = null; // returned from player
    private boolean mbPlatformAudioDecodingEnabled = false;

    // secured playback
    private int mDrmType = NO_DRM_FLAG;
    private ImageView mDrmUIImg = null;
    private ImageView mDrmSeparatorUIImg = null;

    private String mVmxBootAddress = null;
    private String mVmxCompanyName = null;
    private boolean mbMarlinProtected = false;
    private String mDrmPersonalizationUrl = null;
    private String mDrmPersonalizationAppVersion = null;
    private String mDrmPersonalizationCustomData = null;
    private String mDrmPersonalizationCustomerID = null;
    private String mDrmLicenseAcquisitionUrl = null;
    private String mDrmLicenseAcquisitionUrlParameters = null;
    private String mDrmLicenseAcquisitionCustomData = null;
    private String mDrmLicenseAcquisitionCookie = null;
    private String mDrmAgentType = null;
    private String mDrmContentIdentifier = null;
    private boolean mDrmUseVOSecurityGateway = false;

    private String mHttpAuthUsername = null;
    private String mHttpAuthPassword = null;

    private String mSSLCACertFilePath = null;
    private int mSSLCACertFileType = Constants.SSL_TYPE_UNKNOWN;
    private String mSSLClientCertFilePath = null;
    private int mSSLClientCertFileType = Constants.SSL_TYPE_UNKNOWN;
    private String mSSLClientKeyFilePath = null;
    private int mSSLClientKeyFileType = Constants.SSL_TYPE_UNKNOWN;
    private String mHttpCustomHeader = null;

    // proxy settings
    private String mProxyUrl = null;
    private int mProxyPort = 0;
    private int mProxyType = 0;

    // custom mode
    private String mDbFaasOffline = null;
    private boolean mFaasOfflineEnabled = false;
    private int mCustomNetworkMode = 0;
    private int mCodecType = VOPlayer.CODECS_TYPE_DEFAULT;
    private boolean mbForceMSSUpdateManifest = false;

    // bitrate control
    private int mMinABRStreamingBitrate = -1;
    private int mMaxABRStreamingBitrate = -1;
    private int mTypicalABRStreamingBitrate = -1;
    private int mCurrentAlternateBitrate = 0;
    private boolean mSettingABRStreamingMinLimit = false;
    private int mAbrStreamingMaxBitrateLimit = 4000;
    private int mAbrStreamingMinBitrateLimit = 0;

    // liveseeking/event
    private int[] mSeekableRange = {0, 0};
    private int mSeekableRangeLength = 0;
    private boolean mForceEventPlaylist = false;
    private boolean mLiveSeekingEnabled = false;
    private boolean mbInEventMode = false;

    // vast / vmap demo
    private String mVastUrl = null;
    private String mVmapUrl = null;

    // secondary content demo
    private String mSecondaryContentUri = null;

    // scrubbing demo
    private boolean mbIsScrubbingAvailable = false;
    private boolean mbIsScrubbingAvailableCache = false;
    private boolean mFirstSeek = true;
    private boolean mPlayingBeforeSeek = false;
    private boolean mLastSeekStep = false;
    private ImageView mThumbnailImageViewOverlay = null;
    private AlphaAnimation mThumbnailFadeOutAnim = null;
    private ThumbnailHoverPopup mThOverlay = null;
    private boolean mScrubbingEnabled = false;
    private boolean mDisableMaxPlayableHeight = false;
    // Scrubbing new parameters
    private String mSrubbingOptionServer = null;
    private String mScrubbingOptionChannel = null;
    private String mScrubbingOptionAsset = null;
    private String mScrubbingOptionServiceType = null;
    private String mScrubbingOptionStart = null;
    private String mScrubbingOptionEnd = null;
    private String mScrubbingOptionSpriteResolution = null;
    private String mScrubbingOptionSpriteColums = null;
    private String mScrubbingOptionSpriteLines = null;
    private String mScrubbingOptionSpriteInterval = null;


    // Audio Booster
    private boolean mbAudioBoosterEnabled = false;

    // Video Booster
    private int mVideoExperienceEffectValue = 50;
    private int mVideoExperienceEffectMode = 0;
    private int mCurrentVideoExperienceMode = VOPlayer.VIDEO_EXPERIENCE_OFF;
    private boolean mbIsVideoExperienceAllowed = false;
    private int mVEBoost = 50;
    private boolean mSettingVEBoost = false;

    // Offline Viewing
    private int mDownloadedMediaDuration = -1;
    private boolean mStreamFromLocalServer = false;
    private String mDlOriginalUri = null;
    private String mCurrentMediaId = null;
    private boolean mUseSmartDelayForDownload = false;
    private int mStartDelayForDownload = -1;
    private long mDlTimeTriggerForPlaybackStart = -1;
    private boolean mDownloadPlaybackStarted = false;
    private long mDownloadedDuration = 0;

    //Analytics
    //YOUBORA_ANALYTICS: private LVYouboraPlugin mYouboraPlugin = null;
    //YOUBORA_ANALYTICS: private HashMap<String, Object> mYouboraOptions = null;

    //CONVIVA_ANALYTICS: private LVConvivaPlugin mConvivaPlugin = null;
    //CONVIVA_ANALYTICS: private HashMap<String, Object> mConvivaSettings = null;
    //CONVIVA_ANALYTICS: private ContentMetadata mConvivaMetadata = null;

    // Flat-360
    private VOFlat360Manager mFlat360Manager = null;
    private boolean mb360Content = false;
    private int m360zoom = 30;
    private float m360ratio = 16 / 9;
    private float mYaw = 0;
    private float mPitch = 0;

    // Chromecast Alternate parameters
    private String mCastAlternateUrl = null;
    private String mCastAlternateDrmAgentType = null;
    private String mCastAlternateDrmLicenserUrl = null;
    private String mCastAlternateDrmCustomData = null;
    private String mCastAlternateDrmCookie = null;

    private boolean isAndroidTV = false;
    private boolean seekingInAndroidTV = false;
    private long startKeyEventTime = -1;
    private int startKeyEventCode = -1;

    private Plugin youboraPlugin;

    private class ABRGraphGestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            toggleABRFullscreen();
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            return super.onSingleTapConfirmed(e);
        }
    }


    private SimpleOnGestureListener dragListener = new SimpleOnGestureListener() {
        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            if (null != mFlat360Manager) {
                if (mFlat360Manager.isEnabled()) {
                    mYaw += (distanceX / 1000);
                    mPitch += (distanceY / 1000);
                    mFlat360Manager.setHeadPosition(mYaw, mPitch, 0);
                }
            }
            return true;
        }
    };

    // variable used for specific demo
    private int mMediaListSize = 0;
    private StreamingMediaItem[] mPlaylistItem;
    private long[] mPlaylistDuration;
    long mTimeAtStart = 0;
    long mCurrentTime = 0;

    private int[] mZeroLagBitrates = null;
    private int mCurrentZeroLagBitrate = -1;
    private String mAbrStreamingSensitivityValue = null;
    private int mLiveLatencyValue = -1;
    private String mIPResolveType = null;
    private boolean mbTemporaryRedirectAsPermanent = false;

    private VoDemo mDemoMode = null;

    //others variables
    private Timer mAutoHideTimer = new Timer("autoHideTimer");
    private AutoHideControlsTask mAutoHideTask = null;
    private Animation mFadeInAnimation = null;
    private Animation mFadeOutAnimation = null;
    private QuickAction mQuickAction = null;
    private Dialog mListDialog = null;
    private int mCurrentDialogType = -1;
    private ListAdapterListener mInternalDialogListListener = new ListAdapterListener();
    private Handler mAsyncHandler = null;
    private LightingColorFilter mHighlightBtnColorFilter = null;
    private boolean mIsActivtyResumed = false;


    private VORenderingWebView abrGraphWebView;
    private ABRGraphUpdateTimer abrGraphUpdateTimer;
    private boolean abrGraphFullscreen = false;
    private boolean abrGraphVisible = true;
    private ImageButton abrGraphBtn = null;
    GestureDetector abrGraphGestureDetector;
    ABRGraphGestureListener abrGraphGestureListener = new ABRGraphGestureListener();
    boolean abrGraphEnabled = false;

    /* GoogleCast custom receiver */
    private SessionManager mSessionManager = null;
    private MediaRouteButton mMediaRouteButton = null;

    private final SessionManagerListener mSessionManagerListener = new SessionManagerListenerImpl();

    private class SessionManagerListenerImpl implements SessionManagerListener {
        @Override
        public void onSessionStarting(Session session) {
        }

        @Override
        public void onSessionStarted(Session session, String sessionId) {
        }

        @Override
        public void onSessionStartFailed(Session session, int error) {
        }

        @Override
        public void onSessionSuspended(Session session, int error) {
        }

        @Override
        public void onSessionResuming(Session session, String sessionId) {
        }

        @Override
        public void onSessionResumed(Session session, boolean wasSuspended) {
        }

        @Override
        public void onSessionResumeFailed(Session session, int error) {
        }

        @Override
        public void onSessionEnding(Session session) {
        }

        @Override
        public void onSessionEnded(Session session, int error) {
        }
    }

    private final RemoteMediaClient.Callback mRemoteMediaClient = new RemoteMediaClientImpl();

    private class RemoteMediaClientImpl extends RemoteMediaClient.Callback {
        public RemoteMediaClientImpl() {
            super();
        }

        public void onStatusUpdated() {

            int playerState = mSessionManager.getCurrentCastSession().getRemoteMediaClient().getPlayerState();

            switch (playerState) {
                case MediaStatus.PLAYER_STATE_UNKNOWN:
                    break;
                case MediaStatus.PLAYER_STATE_IDLE:
                    break;
                case MediaStatus.PLAYER_STATE_BUFFERING:
                    break;
                case MediaStatus.PLAYER_STATE_PAUSED:
                    updateMediaControls();
                    break;
                case MediaStatus.PLAYER_STATE_PLAYING:
                    updateMediaControls();
                    break;
            }
            super.onMetadataUpdated();

        }
    }


    /************************************************************/
    /************* Activity lifecycle methods *******************/
    /************************************************************/
    @Override
    public void onCreate(Bundle savedInstanceState) {
        Log.v(TAG, "onCreate - " + savedInstanceState);
        super.onCreate(savedInstanceState);

        requestWindowFeature(Window.FEATURE_NO_TITLE);

        Window w = this.getWindow();
        if (null != w) {
            w.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
        } else {
            Log.w(TAG, "onCreate - getWindow returns null");
        }

        mAsyncHandler = new Handler();
        loadAppPreferences();

        Intent intent = this.getIntent();
        try {
            extractInfoFromIncomingIntent(intent);
        } catch (Exception e) {
            e.printStackTrace();
        }

        setContentView(R.layout.player_screen);
        // get UI resources
        getAllUIReferences();

        // initialize variables and controls
        mSelectedBtnColorFilter = new LightingColorFilter(0xFFFFFFFF, Color.WHITE);
        mHighlightBtnColorFilter = new LightingColorFilter(0xFFFFFFFF, Color.RED);
        mGenericOptBtn.getBackground().setColorFilter(mSelectedBtnColorFilter);
        mAudioOnlyDrawable = getResources().getDrawable(R.drawable.lvwidget_bg_audio_only);
        mPlayBtnDrawable = getResources().getDrawable(R.drawable.lvwidget_play_btn);
        mPauseBtnDrawable = getResources().getDrawable(R.drawable.lvwidget_pause_btn);

        mFastZappingPPlusBtn.setVisibility(View.GONE);
        mFastZappingPMinusBtn.setVisibility(View.GONE);
        if (mMediaListSize > 0) {
            mFastZappingPPlusBtn.setVisibility(View.VISIBLE);
            mFastZappingPMinusBtn.setVisibility(View.VISIBLE);
        }
        mBatteryUIText = (TextView) findViewById(R.id.battery_level);

        String additionalInfos = "";
        if (mStreamFromLocalServer) {
            additionalInfos += "Local Streaming server\n";
        }
        if (additionalInfos.length() > 0) {
            mAdditionalInfoText.setText(additionalInfos);
            mAdditionalInfoText.setVisibility(View.VISIBLE);
        }

        mSubtitleActionBtn.setOnClickListener(this);
        mAudioTrackBtn.setOnClickListener(this);

        mAudioBoosterBtn.setOnClickListener(this);
        mVideoBoosterBtn.setOnClickListener(this);
        mVEBoostBtn.setOnClickListener(this);
        mId3MetadataBtn.setOnClickListener(this);
        mWifiToggleBtn.setOnClickListener(this);
        mSetAlternateBtn.setOnClickListener(this);

        mAbrStreamingOptBtn.setOnClickListener(this);
        mTracksOptBtn.setOnClickListener(this);
        mGenericOptBtn.setOnClickListener(this);
        mFastZappingPPlusBtn.setOnClickListener(this);
        mFastZappingPMinusBtn.setOnClickListener(this);

        mSliderPopup = new SliderPopup(this);
        mSliderPopup.setOnDismissListener(this);
        mSliderPopup.setOnUpdateListener(this);
        mSliderPopup.setOnBtnClickListener(this);

        mFadeInAnimation = new AlphaAnimation((float) 0.0, (float) 1.1);
        mFadeInAnimation.setDuration(ANIMATION_DURATION);
        mFadeInAnimation.setAnimationListener(this);

        mFadeOutAnimation = new AlphaAnimation((float) 1.0, (float) 0.0);
        mFadeOutAnimation.setDuration(ANIMATION_DURATION);
        mFadeOutAnimation.setAnimationListener(this);

        mThumbnailFadeOutAnim = new AlphaAnimation(1.0f, 0.0f);
        mThumbnailFadeOutAnim.setAnimationListener(new AnimationListener() {
            public void onAnimationStart(Animation animation) {
            }

            public void onAnimationRepeat(Animation animation) {
            }

            public void onAnimationEnd(Animation animation) {
                mThumbnailImageViewOverlay.setVisibility(View.GONE);
            }
        });
        mThumbnailFadeOutAnim.setDuration(300);

        mGestureDetector = new GestureDetector(getApplicationContext(), new GestureListener());
        mThOverlay = new ThumbnailHoverPopup(getApplicationContext());

        computeScreenDepentSizes();
        updateMediaControls();

        //YOUBORA_ANALYTICS: mYouboraOptions = new HashMap<String, Object>();
        //YOUBORA_ANALYTICS: mYouboraOptions.put("accountCode","squadeodev");
        //YOUBORA_ANALYTICS: mYouboraOptions.put("enableAnalytics",true);
        //YOUBORA_ANALYTICS: mYouboraOptions.put("username","");
        //YOUBORA_ANALYTICS: mYouboraPlugin = new LVYouboraPlugin(mYouboraOptions);

        //CONVIVA_ANALYTICS: mConvivaPlugin = new LVConvivaPlugin();
        //CONVIVA_ANALYTICS: mConvivaSettings = new HashMap<String, Object>();
        //CONVIVA_ANALYTICS: //mConvivaSettings.put(LVConvivaPlugin.SETTINGS_GATEWAY_URL, "https://xxxxxxxxx-test.testonly.conviva.com");
        //CONVIVA_ANALYTICS: //mConvivaSettings.put(LVConvivaPlugin.SETTINGS_DEBUG_MODE, true);
        //CONVIVA_ANALYTICS: //mConvivaSettings.put(LVConvivaPlugin.SETTINGS_HEARTBEAT_INTERVAL, 5); //interval at which the Conviva will send available data to the Conviva Platform (in seconds)
        //CONVIVA_ANALYTICS: LVConvivaPlugin.initWithSettings("782e3c4f85799cc3a7de37747304db1733626e91", mConvivaSettings, getApplicationContext()); //Put your own customer key here!!!!

        // GoogleCast
        try {
            mMediaRouteButton = (MediaRouteButton) findViewById(R.id.media_route_button);
            CastButtonFactory.setUpMediaRouteButton(getApplicationContext(), mMediaRouteButton);

            mSessionManager = CastContext.getSharedInstance(this).getSessionManager();
        } catch (RuntimeException e) {
            Toast.makeText(this, "Cast service not available",
                    Toast.LENGTH_SHORT).show();
        }


        // Register video surface callbacks
        VOSurfaceView sv = (VOSurfaceView) findViewById(R.id.VideoSurface);
        SurfaceHolder sh = sv.getHolder();
        sh.addCallback(this);

        // register gesture detector for 360 video
        mGestureDetector360 = new GestureDetector(getApplicationContext(), dragListener);
        sv.setOnTouchListener(this);

        abrGraphBtn = findViewById(R.id.abr_grah_btn);
        if (abrGraphEnabled) {
            abrGraphGestureDetector = new GestureDetector(getApplicationContext(), abrGraphGestureListener);
            abrGraphWebView.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    abrGraphGestureDetector.onTouchEvent(motionEvent);
                    return true;
                }
            });

            abrGraphBtn.setOnClickListener(this);
        } else {

            abrGraphBtn.setVisibility(View.GONE);
        }

        VOSystemInfo sysInfo = VOSystemInfoRetriever.getSystemInfo(getApplicationContext());
        isAndroidTV = sysInfo.isTV;
    }

    @Override
    protected void onPause() {
        Log.v(TAG, "onPause");
        super.onPause();
        mIsActivtyResumed = false;

        if (null != mMediaPlayer) {
            //YOUBORA_ANALYTICS: mYouboraPlugin.pauseMonitoring();
            recordLastPositionIfNecessary();
            mWasPlaying = mMediaPlayer.isPlaying();

            // don't pause when casting is on going
            if (mMediaPlayer.isCasting() == false) {
                mMediaPlayer.pause();
            }
        }

        // disconnect receivers for bettery level and network status events
        unregisterReceiver(mBatteryInfoReceiver);
        unregisterReceiver(mIntentReceiver);
        unregisterReceiver(mPhoneCallInfoReceiver);

        DownloadManager.getInstance(getApplicationContext()).unregisterOnInfoListener(this);

        if (null != mSessionManager) {
            mSessionManager.removeSessionManagerListener(mSessionManagerListener);
        }
    }

    @Override
    protected void onResume() {
        Log.v(TAG, "onResume");
        super.onResume();
        mIsActivtyResumed = true;

        // reconnect receivers for battery level information and network status
        registerReceiver(mBatteryInfoReceiver, new IntentFilter(Intent.ACTION_BATTERY_CHANGED));
        registerReceiver(mIntentReceiver, new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION));
        registerReceiver(mPhoneCallInfoReceiver, new IntentFilter("android.intent.action.PHONE_STATE"));
        registerReceiver(mPhoneCallInfoReceiver, new IntentFilter("android.intent.action.NEW_OUTGOING_CALL"));
        updateConnectivityInformation();

        /*
        mControlsLayout.setTag(ControlsState.UI_READY_TO_HIDE);
        mControlsLayout.setVisibility(View.VISIBLE);
        */

        if (null != mMediaPlayer) {
            //YOUBORA_ANALYTICS: mYouboraPlugin.resumeMonitoring();
            restoreVideoView();

            // don't resume when casting is on going
            if ((mWasPlaying) && (mMediaPlayer.isCasting() == false)) {
                internalResume();
            }
        }

        if (null != mSessionManager) {
            mSessionManager.addSessionManagerListener(mSessionManagerListener);
        }
    }

    @Override
    protected void onDestroy() {
        Log.w(TAG, "onDestroy");
        internalCleanUp();
        //CONVIVA_ANALYTICS: mConvivaPlugin.cleanupSession();
        //CONVIVA_ANALYTICS: LVConvivaPlugin.cleanup();
        //AGAMA_ANALYTICS: LVAgamaPlugin.getInstance().stopPlaybackSession();
        super.onDestroy();
    }

    // Override the default dispatchKeyEvent method to catch remote control keys to better manage UI on Android TV
    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {

        boolean intercept = false;
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            if (startKeyEventCode == -1 && startKeyEventTime == -1) {
                // store keyCode and event time to check for "long press" situation on 'KeyUP' events
                startKeyEventTime = System.currentTimeMillis();
                startKeyEventCode = event.getKeyCode();
            }
        }
        if (event.getAction() == KeyEvent.ACTION_UP) {
            long now = System.currentTimeMillis();
            if (((now - startKeyEventTime) > 450) && event.getKeyCode() == startKeyEventCode) {
                switch (event.getKeyCode()) {
                    case KeyEvent.KEYCODE_DPAD_UP:
                        toggleControlsVisibility();
                        intercept = true;
                        break;
                    case KeyEvent.KEYCODE_DPAD_DOWN:
                        toggleSidePanelVisibility();
                        intercept = true;
                        break;
                }
            }
            startKeyEventCode = -1;
            startKeyEventTime = -1;
            if (seekingInAndroidTV && event.getKeyCode() == KeyEvent.KEYCODE_DPAD_CENTER) {
                applySeekToAtEndOfSeekAction();
                seekingInAndroidTV = false;
                internalResume();
            }
        }
        if (!intercept) {
            return super.dispatchKeyEvent(event);
        } else {
            Log.i(TAG, "Key event caught and processed in PlayerActivity");
            return true;
        }
    }


    /************************************************************/
    /****** Methods used for activity initialization ************/
    /************************************************************/
    private void loadAppPreferences() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        mLoopingPlayback = prefs.getBoolean(Constants.PREF_LOOP_MODE, false);
        String proxyTypePref = prefs.getString(Constants.PREF_PROXY_TYPE, "No proxy");

        if (proxyTypePref.equals("HTTP")) {
            mProxyType = 1;
        } else if (proxyTypePref.equals("SOCKS v4")) {
            mProxyType = 2;
        } else if (proxyTypePref.equals("SOCKS v5")) {
            mProxyType = 3;
        } else {
            mProxyType = 0;
        }
        mProxyUrl = prefs.getString(Constants.PREF_PROXY_URL, null);
        mProxyPort = Integer.parseInt(prefs.getString(Constants.PREF_PROXY_PORT, "0"));

        String DlStartDelayP = prefs.getString(Constants.PREF_DL_START_DELAY, "30 s");
        if (DlStartDelayP.equalsIgnoreCase("1 min")) {
            mUseSmartDelayForDownload = false;
            mStartDelayForDownload = 60000;
        } else if (DlStartDelayP.equalsIgnoreCase("30 s")) {
            mUseSmartDelayForDownload = false;
            mStartDelayForDownload = 30000;
        } else {
            mUseSmartDelayForDownload = true;
            mStartDelayForDownload = -1;
        }
        mForceEventPlaylist = prefs.getBoolean(Constants.PREF_FORCE_EVENT_PLAYLIST, false);
        mLiveSeekingEnabled = prefs.getBoolean(Constants.PREF_ENABLE_LIVE_SEEKING, true);
        mScrubbingEnabled = prefs.getBoolean(Constants.PREF_ENABLE_SCRUBBING, true);
        mDisableMaxPlayableHeight = prefs.getBoolean(Constants.PREF_DISABLE_MAX_PLAYABLE_HEIGHT, false);
        mFaasOfflineEnabled = prefs.getBoolean(Constants.PREF_ENABLE_FAAS_OFFLINE, false);
        mbShowPlayerInfo = prefs.getBoolean(Constants.PREF_SHOW_PLAYER_INFO, false);
        mAbrStreamingSensitivityValue = prefs.getString(Constants.PREF_ENABLE_ABR_SENSITIVITY, "Default");
        mbEnableSubtitleRendering = prefs.getBoolean(Constants.PREF_ENABLE_INT_SUBTITLE_RENDERING, true);
        mbSetAdSeeToAllowed = prefs.getBoolean(Constants.PREF_SET_AD_SEEK_TO_ALLOWED, false);
        mbPlatformAudioDecodingEnabled = prefs.getBoolean(Constants.PREF_ENABLE_PLATFORM_AUDIO_DECODING, false);
        mIPResolveType = prefs.getString(Constants.PREF_IP_RESOLVE_TYPE, "Default");
        mbTemporaryRedirectAsPermanent = prefs.getBoolean(Constants.PREF_ENABLE_TEMPORARY_REDIRECT_AS_PERMANENT, false);

        String codecTypePref = prefs.getString(Constants.PREF_CODEC_TYPE, "DEFAULT");

        String subTextColor = prefs.getString(Constants.PREF_TEXT_SUBTITLE_COLOR, "Default");
        switch (subTextColor) {
            case "Yellow":
                mCustomSubtitleCSSStyling = ".text-sub { color: yellow!important; }";   //!important is required to overide any computed style
                break;
            case "Green":
                mCustomSubtitleCSSStyling = ".text-sub { color: green!important; }";    //!important is required to overide any computed style
                break;
        }

        if (codecTypePref.equals("DEFAULT")) {
            mCodecType = VOPlayer.CODECS_TYPE_DEFAULT;
        } else if (codecTypePref.equals("HW-MC-TU")) {
            mCodecType = VOPlayer.CODECS_TYPE_HW_MC_TU;
        } else if (codecTypePref.equals("HW-MC-NT")) {
            mCodecType = VOPlayer.CODECS_TYPE_HW_MC_NT;
        } else if (codecTypePref.equals("HW-SF-TU")) {
            mCodecType = VOPlayer.CODECS_TYPE_HW_SF_TU;
        } else if (codecTypePref.equals("HW-SF-NT")) {
            mCodecType = VOPlayer.CODECS_TYPE_HW_SF_NT;
        } else if (codecTypePref.equals("SW")) {
            mCodecType = VOPlayer.CODECS_TYPE_SW;
        } else if (codecTypePref.equals("AMLOGIC")) {
            mCodecType = VOPlayer.CODECS_TYPE_AML;
        } else if (codecTypePref.equals("HW-MC-TU-TV")) {
            mCodecType = VOPlayer.CODECS_TYPE_HW_MC_TU_TV;
        } else {
            mCodecType = VOPlayer.CODECS_TYPE_DEFAULT;
        }

        String zoomValue = prefs.getString(Constants.PREF_FLAT_360_ZOOM, Constants.FLAT_360_ZOOM_DEFAULT);
        m360zoom = Integer.parseInt(zoomValue);
        String ratioValue = prefs.getString(Constants.PREF_FLAT_360_RATIO, Constants.FLAT_360_RATIO_DEFAULT);
        if (ratioValue.matches("16/9")) {
            m360ratio = (float) 16 / (float) 9;
        } else if (ratioValue.matches("9/9")) {
            m360ratio = (float) 9 / (float) 9;
        } else {
            m360ratio = (float) 16 / (float) 9;
        }

        abrGraphEnabled = prefs.getBoolean(Constants.PREF_ENABLE_ABR_GRAPH, false);

        Log.d(TAG, "loadAppPreferences: autoloop " + mLoopingPlayback + " mProxyType " + mProxyType + " mProxyUrl " + mProxyUrl + " proxy port " + mProxyPort);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    private void getAllUIReferences() {

        mDisplaySurface = (VOSurfaceView) findViewById(R.id.VideoSurface);
        mSidePanel = (LinearLayout) findViewById(R.id.side_panel_zone);

        mNotificationText = (TextView) findViewById(R.id.player_notification_txt);
        mNotificationLayout = (FrameLayout) findViewById(R.id.player_notification_layout);
        mElapsedTimeTxt = (TextView) findViewById(R.id.player_elapsed_txt);
        mDurationTxt = (TextView) findViewById(R.id.player_duration_txt);

        mControlsLayout = findViewById(R.id.player_controls_layout);
        mControlsLayout.setTag(ControlsState.UI_READY_TO_HIDE);

        mJumpToLiveBtn = (Button) findViewById(R.id.player_jumpToLiveBtn);
        mJumpToLiveBtn.setVisibility(View.GONE);
        mJumpToLiveBtn.setOnClickListener(this);

        mPlayPauseBtn = (ImageButton) findViewById(R.id.player_playpause_btn);
        mPlayPauseBtn.setOnClickListener(this);
        mPlayPauseBtn.setVisibility(View.VISIBLE);
        mPlayPauseBtn.setEnabled(false);

        mSkipAdBtn = (Button) findViewById(R.id.player_skip_ad_btn);
        mSkipAdBtn.setOnClickListener(this);

        mDisplayBtn = (ImageButton) findViewById(R.id.player_display_btn);
        mDisplayBtn.setOnClickListener(this);
        mDisplayBtn.setVisibility(View.VISIBLE);
        mDisplayBtn.setEnabled(true);

        mSeekbar = (SeekBar) findViewById(R.id.player_seekbar);
        mSeekbar.setOnSeekBarChangeListener(this);

        mMediaNameUIText = (TextView) findViewById(R.id.mediaNameText);
        mBatteryUIText = (TextView) findViewById(R.id.battery_level);
        mHwCodecIndicText = (TextView) findViewById(R.id.hw_codec_indic);

        mDrmUIImg = (ImageView) findViewById(R.id.media_DRM_img);
        mDrmSeparatorUIImg = (ImageView) findViewById(R.id.separator);

        RelativeLayout player_layout = (RelativeLayout) findViewById(R.id.mediaplayer_layout);

        if (null == mId3TextView) {
            mId3TextView = new TextView(getApplicationContext());
            mId3TextView.setTextColor(Color.WHITE);
            mId3TextView.setBackgroundColor(Color.TRANSPARENT);
            mId3TextView.setShadowLayer((float) 3.0, (float) 5.0, (float) 5.0, Color.BLACK);
            mId3TextView.setPadding(1, 1, 1, 5);
            mId3TextView.setVisibility(View.GONE);
        }

        if (null == mId3ImageView) {
            mId3ImageView = new ImageView(getApplicationContext());
            mId3ImageView.setBackgroundColor(Color.TRANSPARENT);
            mId3ImageView.setVisibility(View.GONE);
        }

        if (null == mId3Layout) {
            mId3Layout = new LinearLayout(getApplicationContext());
            mId3Layout.setOrientation(LinearLayout.VERTICAL);
            mId3Layout.addView(mId3TextView);

            RelativeLayout.LayoutParams relativeParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
            relativeParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
            relativeParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
            relativeParams.topMargin = 10;
            player_layout.addView(mId3Layout, relativeParams);
            mId3Layout.setVisibility(View.GONE);
        }

        RelativeLayout.LayoutParams fullScreenParams = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        fullScreenParams.addRule(RelativeLayout.ALIGN_PARENT_RIGHT, RelativeLayout.TRUE);
        fullScreenParams.addRule(RelativeLayout.ALIGN_PARENT_TOP, RelativeLayout.TRUE);
        fullScreenParams.width = LayoutParams.MATCH_PARENT;
        fullScreenParams.height = LayoutParams.MATCH_PARENT;

        mThumbnailImageViewOverlay = (ImageView) findViewById(R.id.player_scrubbing_thumbnail_overlay);
        mThumbnailImageViewOverlay.setScaleY(1.01f);
        mThumbnailImageViewOverlay.setVisibility(View.GONE);
        mThumbnailImageViewOverlay.setColorFilter(new LightingColorFilter(Color.rgb(100, 100, 100), Color.BLACK));

        mGenericOptSection = (LinearLayout) findViewById(R.id.genericinfo_section_lyt);
        mAbrStreamingOptSection = (ScrollView) findViewById(R.id.abr_section_lyt);
        mTracksOptSection = (LinearLayout) findViewById(R.id.tracks_section_lyt);

        mGenericOptBtn = findViewById(R.id.generic_section_btn);
        mAbrStreamingOptBtn = findViewById(R.id.abr_section_btn);
        mTracksOptBtn = findViewById(R.id.cc_section_btn);

        mSubtitleActionBtn = (Button) findViewById(R.id.subtitle_track_btn);
        mAudioTrackBtn = (Button) findViewById(R.id.audio_track_btn);

        mSetAlternateBtn = (Button) findViewById(R.id.set_alternate_btn);
        mWifiToggleBtn = (ImageButton) findViewById(R.id.BtnWifiToggle);

        mVideoResolutionText = (TextView) findViewById(R.id.video_res_text);
        mAdditionalInfoText = (TextView) findViewById(R.id.additional_info_text);
        mAudioBoosterBtn = (Button) findViewById(R.id.audio_booster_btn);
        mVideoBoosterText = (TextView) findViewById(R.id.video_experience_text);
        mVideoBoosterBtn = (Button) findViewById(R.id.video_experience_btn);
        mVEBoostBtn = (Button) findViewById(R.id.ve_boost_btn);
        mId3MetadataBtn = (Button) findViewById(R.id.metadata_id3_btn);
        mFastZappingPPlusBtn = (Button) findViewById(R.id.fastzapping_pplus_btn);
        mFastZappingPMinusBtn = (Button) findViewById(R.id.fastzapping_pminus_btn);

        mWifiNetworkText = (TextView) findViewById(R.id.wifi_network_info);
        mCellularNetworkImg = (ImageView) findViewById(R.id.cellular_state_img);

        mOverlayBitrateText = (TextView) findViewById(R.id.overlay_current_br);
        mOverlayVResolutionText = (TextView) findViewById(R.id.overlay_current_display);
        mOverlayInfosLayout = (LinearLayout) findViewById(R.id.stream_info_overlay);

        loadAndSetABRStreamingControls();

        abrGraphWebView = findViewById(R.id.abrGraphWebView);
        abrGraphWebView.setFocusable(false);

        if (abrGraphEnabled) {
            WebSettings webSettings = abrGraphWebView.getSettings();
            webSettings.setJavaScriptEnabled(true);
            webSettings.setBlockNetworkLoads(false);
            webSettings.setBlockNetworkImage(true);
            webSettings.setGeolocationEnabled(false);

            abrGraphWebView.setBackgroundColor(Color.TRANSPARENT);
            abrGraphWebView.loadUrl("file:///android_asset/abr_graph.html");
        } else {
            abrGraphWebView.setVisibility(View.GONE);
        }
    }

    private void updateABRGraph() {

        if (null != mMediaPlayer) {
            int bufferLevel = mMediaPlayer.getCurrentBufferLevel() / 1000;
            int alternate = mMediaPlayer.getHttpStreamingAlternateBitrate() / 1000;
            int bandwidth = mMediaPlayer.getHttpStreamingCurrentBitrate() / 1000;
            abrGraphWebView.loadUrl("javascript:addNewMeasures(" + bufferLevel + ", " + bandwidth + ", " + alternate + ")");
        }
    }

    /**
     * Retrieves all the UI controls for HTTP Streaming information and initializes them
     */
    private void loadAndSetABRStreamingControls() {

        mAbrStreamingVariantBRText = (TextView) findViewById(R.id.http_streaming_alternate);
        mAbrStreamingCurrentThroughPutBR = (TextView) findViewById(R.id.http_streaming_current_br);
        mAbrStreamingCurrentBufferLevel = (TextView) findViewById(R.id.http_streaming_current_buffer);

        // Min bitrate controls
        mAbrStreamingMinBRBtn = (Button) findViewById(R.id.abr_min_limit);
        mAbrStreamingMinBRBtn.setText("Min: 0 kbps");
        mAbrStreamingMinBRBtn.setOnClickListener(this);

        // Max bitrate controls
        mAbrStreamingMaxBRBtn = (Button) findViewById(R.id.abr_max_limit);
        mAbrStreamingMaxBRBtn.setText("Max: - kbps");
        mAbrStreamingMaxBRBtn.setOnClickListener(this);

        if (null != mZeroLagBitrates) {
            mAbrStreamingMaxBRBtn.setVisibility(View.GONE);
            mAbrStreamingMinBRBtn.setVisibility(View.GONE);
            findViewById(R.id.abr_custom_bitrates).setVisibility(View.VISIBLE);
        }

        mAbrStreamingCustomBRDownBtn = (Button) findViewById(R.id.abr_custom_bitrate_down);
        mAbrStreamingCustomBRDownBtn.setOnClickListener(this);
        mAbrStreamingCustomBRDownBtn.setEnabled(false);

        mAbrStreamingCustomBRUpBtn = (Button) findViewById(R.id.abr_custom_bitrate_up);
        mAbrStreamingCustomBRUpBtn.setOnClickListener(this);
    }

    private void extractInfoFromIncomingIntent(Intent intent) throws Exception {
        Uri contentUri = intent.getData();
        Log.d(TAG, "extractInfoFromIncomingIntent - intent URI: " + contentUri);

        if (null != contentUri) {
            String uriScheme = contentUri.getScheme();
            if (null != uriScheme && uriScheme.equalsIgnoreCase("content")) {
                Log.d(TAG, "extractInfoFromIncomingIntent - intent URI: " + contentUri + " => scheme = " + contentUri.getScheme());
                String localFilePath = Utils.getRealPathFromURI(this, contentUri);
                mMediaUri = Uri.parse(localFilePath);
            } else {
                mMediaUri = contentUri;
            }
        } else {
            Log.w(TAG, "intent URI is null");
            throw new Exception("no content uri");
        }

        mCurrentMediaId = intent.getStringExtra(Constants.MEDIA_ID_SKEY);
        mMediaType = getMediaTypeFromUri(mMediaUri);
        Log.d(TAG, "extractInfoFromIncomingIntent - end of media URI parsing");

        mResumePlaybackPosition = intent.getIntExtra(Constants.RESUME_TIME_INTENT_IKEY, -1);

        mSubtitleFilePath = intent.getStringExtra(Constants.SUBTITLE_FILE_INTENT_SKEY);
        mSubtitleTrackName = intent.getStringExtra(Constants.SUBTITLE_TRACK_INTENT_SKEY);
        mSubtitleTrackType = intent.getIntExtra(Constants.SUBTITLE_TRACKTYPE_INTENT_SKEY, 0);
        Log.d(TAG, "extractInfoFromIncomingIntent - subtitle file = " + mSubtitleFilePath + "; trackname = " + mSubtitleTrackName + "; tracktype = " + mSubtitleTrackType);

        mCurrentAudioTrackName = intent.getStringExtra(Constants.AUDIO_LANGUAGE_INTENT_SKEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - audio language = " + mCurrentAudioTrackName);

        mVastUrl = intent.getStringExtra(Constants.VAST_URL_INTENT_SKEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - vast url = " + mVastUrl);
        mVmapUrl = intent.getStringExtra(Constants.VMAP_URL_INTENT_SKEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - vmap url = " + mVmapUrl);
        mSecondaryContentUri = intent.getStringExtra(Constants.SECONDARY_CONTENT_URI_SKEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - secondary content url = " + mSecondaryContentUri);

        mVmxBootAddress = intent.getStringExtra(Constants.VCAS_BOOT_ADDRESS_INTENT_SKEY);
        mVmxCompanyName = intent.getStringExtra(Constants.VCAS_COMPANY_NAME_INTENT_SKEY);
        mDrmAgentType = intent.getStringExtra(Constants.DRM_AGENT_TYPE);
        mDrmPersonalizationUrl = intent.getStringExtra(Constants.DRM_PERSONALIZATION_URL);
        mDrmPersonalizationAppVersion = intent.getStringExtra(Constants.DRM_PERSONALIZATION_APPVERSION);
        mDrmPersonalizationCustomData = intent.getStringExtra(Constants.DRM_PERSONALIZATION_CUSTOM_DATA);
        mDrmPersonalizationCustomerID = intent.getStringExtra(Constants.DRM_PERSONALIZATION_CUSTOMER_ID);
        mDrmLicenseAcquisitionUrl = intent.getStringExtra(Constants.DRM_LICENSE_URL_SKEY);
        mDrmLicenseAcquisitionUrlParameters = intent.getStringExtra(Constants.DRM_LICENSE_URL_PARAMETERS_SKEY);
        mDrmLicenseAcquisitionCustomData = intent.getStringExtra(Constants.DRM_LICENSE_CUSTOM_DATA_SKEY);
        mDrmLicenseAcquisitionCookie = intent.getStringExtra(Constants.DRM_LICENSE_COOKIE_SKEY);
        mDrmContentIdentifier = intent.getStringExtra(Constants.DRM_CONTENT_IDENTIFIER);
        mDrmUseVOSecurityGateway = intent.getBooleanExtra(Constants.DRM_LICENSE_USE_VOSECURITYGATEWAY, false);

        mbMarlinProtected = intent.getBooleanExtra(Constants.MARLIN_PROTECTED_SKEY, false);
        Log.d(TAG, "extractInfoFromIncomingIntent - verimatrix Boot Address = " + mVmxBootAddress + "; verimatrix Company Name = " + mVmxCompanyName);
        Log.d(TAG, "extractInfoFromIncomingIntent - mDrmLicenseAcquisitionUrl = " + mDrmLicenseAcquisitionUrl);
        Log.d(TAG, "extractInfoFromIncomingIntent - mDrmLicenseAcquisitionUrlParameters = " + mDrmLicenseAcquisitionUrlParameters);
        Log.d(TAG, "extractInfoFromIncomingIntent - mDrmPersonalizationUrl = " + mDrmPersonalizationUrl);
        Log.d(TAG, "extractInfoFromIncomingIntent - mDrmAgentType = " + mDrmAgentType);
        Log.d(TAG, "extractInfoFromIncomingIntent - mbMarlinProtected = " + mbMarlinProtected);

        mb360Content = intent.getBooleanExtra(Constants.FLAT_360_PLAYBACK_SUPPORT, false);

        mDbFaasOffline = intent.getStringExtra(Constants.DB_FAAS_OFFLINE_SKEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - dbFaasOffline = " + mDbFaasOffline);

        // extract HTTP auth credentials, SSL CA/Client/Client Key Path and Type, Http Custom Header
        mHttpAuthUsername = intent.getStringExtra(Constants.AUTH_USER_NAME_INTENT_SKEY);
        mHttpAuthPassword = intent.getStringExtra(Constants.AUTH_PASSWORD_INTENT_SKEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - mHttpAuthUsername = " + mHttpAuthUsername + "; mHttpAuthPassword = " + mHttpAuthPassword);

        mSSLCACertFilePath = intent.getStringExtra(Constants.SSL_CA_CERT_FILE_PATH_INTENT_SKEY);
        mSSLCACertFileType = Utils.getIntValueForSSLType(intent.getStringExtra(Constants.SSL_CA_CERT_FILE_TYPE_INTENT_SKEY));
        Log.d(TAG, "extractInfoFromIncomingIntent - mSSLCACertFilePath = " + mSSLCACertFilePath + "; mSSLCACertFileType = " + mSSLCACertFileType);

        mSSLClientCertFilePath = intent.getStringExtra(Constants.SSL_CLIENT_CERT_FILE_PATH_INTENT_SKEY);
        mSSLClientCertFileType = Utils.getIntValueForSSLType(intent.getStringExtra(Constants.SSL_CLIENT_CERT_FILE_TYPE_INTENT_SKEY));
        Log.d(TAG, "extractInfoFromIncomingIntent - mSSLClientCertFilePath = " + mSSLClientCertFilePath + "; mSSLClientCertFileType = " + mSSLClientCertFileType);

        mSSLClientKeyFilePath = intent.getStringExtra(Constants.SSL_CLIENT_KEY_FILE_PATH_INTENT_SKEY);
        mSSLClientKeyFileType = Utils.getIntValueForSSLType(intent.getStringExtra(Constants.SSL_CLIENT_KEY_FILE_TYPE_INTENT_SKEY));
        Log.d(TAG, "extractInfoFromIncomingIntent - mSSLClientKeyFilePath = " + mSSLClientKeyFilePath + "; mSSLClientKeyFileType = " + mSSLClientKeyFileType);

        mHttpCustomHeader = intent.getStringExtra(Constants.HTTP_CUSTOM_HEADER_INTENT_SKEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - mHttpCustomHeader = " + mHttpCustomHeader);

        mLiveLatencyValue = intent.getIntExtra(Constants.ABR_LIVE_LATENCY_IKEY, -1);
        Log.d(TAG, "extractInfoFromIncomingIntent - mLiveLatencyValue = " + mLiveLatencyValue);

        mStreamFromLocalServer = intent.getBooleanExtra(Constants.LOCAL_SERVER_INTENT_ZKEY, false);

        mbForceMSSUpdateManifest = intent.getBooleanExtra(Constants.FORCE_MSS_UPDATE_MANIFEST_ZKEY, false);
        Log.d(TAG, "extractInfoFromIncomingIntent - mbForceMSSUpdateManifest = " + mbForceMSSUpdateManifest);

        if (null == mDemoMode) {
            boolean bFastStartup = intent.getBooleanExtra(Constants.FAST_STARTUP_BKEY, false);
            Log.d(TAG, "extractInfoFromIncomingIntent - bFastStartup = " + bFastStartup);
            if (bFastStartup) {
                mDemoMode = new FastStartupDemo(getApplicationContext());
            }
        }

        if (null == mDemoMode) {
            boolean bLocalServerDemo = intent.getBooleanExtra(Constants.LOCAL_SERVER_DEMO_BKEY, false);
            Log.d(TAG, "extractInfoFromIncomingIntent - bLocalServerDemo = " + bLocalServerDemo);
            if (bLocalServerDemo) {
                mDemoMode = new LocalServerDemo(getApplicationContext());
            }
        }

        int intentProxyType = intent.getIntExtra("proxyType", -1);
        int intentProxyPort = intent.getIntExtra("proxyPort", -1);
        String intentProxyUrl = intent.getStringExtra("proxyUrl");
        Log.d(TAG, "extractInfoFromIncomingIntent - proxy URL = " + intentProxyUrl + "; proxy port = " + intentProxyPort + "; proxy type = " + intentProxyType);
        if (intentProxyType > 0 && intentProxyPort > 0 && null != intentProxyUrl) {
            mProxyType = intentProxyType;
            mProxyUrl = intentProxyUrl;
            mProxyPort = intentProxyPort;
        }

        mDlOriginalUri = intent.getStringExtra(Constants.DL_ORG_URI_INTENT_SKEY);

        mZeroLagBitrates = intent.getIntArrayExtra(Constants.HLS_CUSTOM_BITRATES_ZERO_LAG_AIKEY);
        if (null != mZeroLagBitrates) {
            mCurrentZeroLagBitrate = 0;
        }

        if (null == mAbrStreamingSensitivityValue || mAbrStreamingSensitivityValue.equals("Default")) {
            mAbrStreamingSensitivityValue = intent.getStringExtra(Constants.PREF_ENABLE_ABR_SENSITIVITY);
        }

        mCastAlternateUrl = intent.getStringExtra(Constants.CCAST_ALTERNATE_URL_KEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - mCastAlternateUrl = " + mCastAlternateUrl);
        mCastAlternateDrmAgentType = intent.getStringExtra(Constants.CCAST_ALTERNATE_DRM_KEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - mCastAlternateDrmAgentType = " + mCastAlternateDrmAgentType);
        mCastAlternateDrmLicenserUrl = intent.getStringExtra(Constants.CCAST_ALTERNATE_DRM_LICENSER_URL_KEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - mCastAlternateDrmLicenserUrl = " + mCastAlternateDrmLicenserUrl);
        mCastAlternateDrmCustomData = intent.getStringExtra(Constants.CCAST_ALTERNATE_DRM_CUSTOM_DATA_KEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - mCastAlternateDrmCustomData = " + mCastAlternateDrmCustomData);
        mCastAlternateDrmCookie = intent.getStringExtra(Constants.CCAST_ALTERNATE_DRM_COOKIE_KEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - mCastAlternateDrmCookie = " + mCastAlternateDrmCookie);

        //Set Scrubbing informations
        mSrubbingOptionServer = intent.getStringExtra(Constants.SCRUBBING_OPTION_SERVER);
        mScrubbingOptionChannel = intent.getStringExtra(Constants.SCRUBBING_OPTION_CHANNEL);
        mScrubbingOptionAsset = intent.getStringExtra(Constants.SCRUBBING_OPTION_ASSET);
        mScrubbingOptionServiceType = intent.getStringExtra(Constants.SCRUBBING_OPTION_SERVICE_TYPE);
        mScrubbingOptionStart = intent.getStringExtra(Constants.SCRUBBING_OPTION_START);
        mScrubbingOptionEnd = intent.getStringExtra(Constants.SCRUBBING_OPTION_END);
        mScrubbingOptionSpriteResolution = intent.getStringExtra(Constants.SCRUBBING_OPTION_SPRITE_RESOLUTION);
        mScrubbingOptionSpriteColums = intent.getStringExtra(Constants.SCRUBBING_OPTION_SPRITE_COLUMS);
        mScrubbingOptionSpriteLines = intent.getStringExtra(Constants.SCRUBBING_OPTION_SPRITE_LINES);
        mScrubbingOptionSpriteInterval = intent.getStringExtra(Constants.SCRUBBING_OPTION_SPRITE_INTERVAL);

        mStoredName = intent.getStringExtra(Constants.STORED_NAME_KEY);
        Log.d(TAG, "extractInfoFromIncomingIntent - mStoredName = " + mStoredName);

        byte mediaListByte[] = intent.getByteArrayExtra(Constants.PLAYLIST_KEY);

        if (null != mediaListByte) {
            StreamingMediaList mediaList = StreamingMediaList.deserialize(mediaListByte);
            if (null != mediaList) {
                mMediaListSize = mediaList.getStreamingMediaListLength();
                mPlaylistItem = new StreamingMediaItem[mMediaListSize];
                mPlaylistDuration = new long[mMediaListSize];
                for (int i = 0; i < mMediaListSize; i++) {
                    mPlaylistItem[i] = mediaList.getStreamingMediaItemFromList(i);
                    mPlaylistDuration[i] = 0;
                }
                mTimeAtStart = System.currentTimeMillis();
                updatePrivateValues(0);
            }
        }
    }

    private void computeScreenDepentSizes() {

        DisplayMetrics dm = getResources().getDisplayMetrics();
        int sidePanelWidth = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
                (float) 150, dm);

        LayoutParams params = mSidePanel.getLayoutParams();
        params.width = sidePanelWidth;
        mSidePanel.setLayoutParams(params);

        int fontSize = (int) (dm.heightPixels * 0.1);
        mOverlayBitrateText.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
        mOverlayVResolutionText.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontSize);
    }


    private void updateMediaControls() {
        boolean seekBarVisible = true;
        if (null != mMediaPlayer) {
            if (mMediaPlayer.isPlaying()) {
                setPlayPauseBtnState(PlayPauseBtnState.PAUSE);
            } else {
                setPlayPauseBtnState(PlayPauseBtnState.PLAY);
            }

            int currentPosition = mMediaPlayer.getCurrentPosition();
            int mediaDuration = getMediaDuration();

            if (StreamType.HTTPSTR_LIVE == mMediaType) {
                if (mLiveSeekingEnabled) {
                    int liveSeekingProgress = currentPosition - mSeekableRangeLength;
                    setPlaybackCursorPosition(currentPosition);
                    setPlaybackTime(liveSeekingProgress);
                } else {
                    // if we are not in Download and Play mode, we can hide the seekbar
                    if (null == mDlOriginalUri) {
                        seekBarVisible = false;
                    }
                }
                setMaxPlaybackCursorProgressValue(mSeekableRangeLength);
            } else {
                setMediaDuration(mediaDuration);
                setMaxPlaybackCursorProgressValue(mediaDuration);
                setPlaybackCursorPosition(currentPosition);
                setPlaybackTime(currentPosition);
            }
        } else {
            setPlayPauseBtnState(PlayPauseBtnState.DISABLE);
            setMediaDuration(0);
            setPlaybackCursorPosition(0);
            // if we are not in Download and Play mode, we can hide the seekbar
            if (null == mDlOriginalUri) {
                seekBarVisible = false;
            }
        }
        setSeekBarVisibility(seekBarVisible);
    }

    /************************************************************/
    /****************** Tap events capture **********************/
    /************************************************************/
    private class GestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onDown(MotionEvent e) {
            return true;
        }

        @Override
        public boolean onDoubleTap(MotionEvent e) {
            toggleSidePanelVisibility();
            return true;
        }

        @Override
        public boolean onSingleTapConfirmed(MotionEvent e) {
            toggleControlsVisibility();
            checkAdClickThrough();
            return super.onSingleTapConfirmed(e);
        }
    }

    public void toggleABRFullscreen() {
        RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) abrGraphWebView.getLayoutParams();
        if (abrGraphFullscreen) {
            params.width = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 350, getResources().getDisplayMetrics());
            ;
            params.height = (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 175, getResources().getDisplayMetrics());
            ;
        } else {
            params.width = LayoutParams.MATCH_PARENT;
            params.height = LayoutParams.MATCH_PARENT;
        }
        abrGraphWebView.setLayoutParams(params);
        abrGraphFullscreen = !abrGraphFullscreen;
    }


    @Override
    public boolean onTouch(View var1, MotionEvent var2) {
        mGestureDetector.onTouchEvent(var2);
        mGestureDetector360.onTouchEvent(var2);
        return true;
    }


    /************************************************************/
    /******** Private methods used during playback **************/
    /************************************************************/
    private void updateUIElements() {
        if (null != mDlOriginalUri) {
            mAbrStreamingMaxBRBtn.setText("Max: - kbps");
            mAbrStreamingMaxBRBtn.setEnabled(false);
            mAbrStreamingMinBRBtn.setText("Min: - kbps");
            mAbrStreamingMinBRBtn.setEnabled(false);
        } else {
            mAbrStreamingMaxBRBtn.setText("Max: " + mAbrStreamingMaxBitrateLimit + " kbps");
            mAbrStreamingMinBRBtn.setText("Min: " + mAbrStreamingMinBitrateLimit + " kbps");
        }
        setDRMType();

        switch (mMediaType) {
            case LOCAL_FILE:
            case HTTP_PGDL:
                mId3MetadataBtn.setEnabled(false);
                mSubtitleActionBtn.setEnabled(true);
                break;

            case HTTPSTR_UNKNOWN:
            case HTTPSTR_LIVE:
            case HTTPSTR_VOD:
            case HTTPSTR_EVENT:
                mId3MetadataBtn.setEnabled(true);
                mSubtitleActionBtn.setEnabled(true);
                break;

            case UNKNOWN:
                mId3MetadataBtn.setEnabled(false);
                mSubtitleActionBtn.setEnabled(false);
                break;
            default:
                break;
        }
        updateVideExperienceBtn();
        handleAlternateChange();
    }

    /**
     * Sets the notification UI
     *
     * @param visible true to display the notification area, false otherwise
     * @param text    text to display in the notification area
     */
    private void setNotification(boolean visible, String text) {
        if (null != mNotificationLayout) {
            if (visible) {
                mNotificationLayout.setVisibility(View.VISIBLE);
            } else {
                mNotificationLayout.setVisibility(View.GONE);
            }
        }
        if (null != text && null != mNotificationText) {
            mNotificationText.setText(text);
        }
    }

    private void setJumpRestrictionBoundaries(int start, int end) {
        mJumpAllowedRangeStartTime = start;
        mJumpAllowedRangeEndTime = end;
        if (mJumpAllowedRangeEndTime < mJumpAllowedRangeStartTime) {
            Log.e(TAG, "setJumpRestrictionBoundaries, inverted boundaries!");
        }
        //TODO what if current position is outside of allowed range ?
    }

    private void recordLastPositionIfNecessary() {
        if (StreamType.HTTPSTR_LIVE != mMediaType &&
                StreamType.HTTPSTR_UNKNOWN != mMediaType &&
                StreamType.HTTPSTR_EVENT != mMediaType) {
            writeCurrentPlaybackPositionInDb();
        }
    }

    private void restoreVideoView() {
        if (null != mMediaPlayer && null != mDisplaySurface) {
            mMediaPlayer.setVideoView(mDisplaySurface);
        }
    }

    /**
     * Sets the bitrates limits for HTTP Live Streaming use cases
     *
     * @param minBitrate minimum bitrate limit to consider for alternate switching in ABRStreaming (in kbps)
     * @param maxBitrate maximum bitrate limit to consider for alternate switching in ABRStreaming (in kbps)
     *                   Note: Theses limits are used in best-effort mode.
     *                   Their effect during playback can be delayed to up to 30 seconds due to internal buffering.
     */
    private void setHttpBitratesLimits(int minBitrate, int maxBitrate) {
        mMinABRStreamingBitrate = minBitrate;
        mMaxABRStreamingBitrate = maxBitrate;
        if (null != mMediaPlayer) {
            try {
                mMediaPlayer.setHttpStreamingMinimumBitrate(mMinABRStreamingBitrate * 1000);
                mMediaPlayer.setHttpStreamingMaximumBitrate(mMaxABRStreamingBitrate * 1000);
            } catch (UnsupportedOperationException e) {
                e.printStackTrace();
            }
        }
    }

    /**
     * Sets the duration to display
     *
     * @param duration duration (in ms) that will displayed and used as progression maximum value
     */
    private void setMediaDuration(int duration) {
        if (duration > 0) {
            mSeekbar.setMax(duration);
            mDurationTxt.setText(VOUtils.convertTimeToText(duration));
            mDurationTxt.setVisibility(View.VISIBLE);
        } else if (null == mDlOriginalUri) {
            //We are in live
            mDurationTxt.setVisibility(View.GONE);
            mElapsedTimeTxt.setText("Live Broadcast");
        }
    }

    private int getMediaDuration() {
        if (mDownloadedMediaDuration >= 0) {
            return mDownloadedMediaDuration;
        } else {
            return mSeekableRangeLength;
        }
    }

    private StreamType getMediaTypeFromUri(Uri uri) {
        StreamType type = StreamType.UNKNOWN;

        if (null != uri) {
            String uriScheme = uri.getScheme();
            String uriStr = uri.toString();
            String uriWithoutParams = uriStr;

            Log.i(TAG, "getMediaTypeFromUri - media path: " + uriStr);
            // Remove parameters from uri if presents
            if (uriStr.lastIndexOf("?") != -1) {
                uriWithoutParams = uriStr.substring(0, uriStr.lastIndexOf("?"));
                Log.d(TAG, "getMediaTypeFromUri - Removing parameters from path: " + uriWithoutParams);
            }

            String extension = null;
            if (uriWithoutParams.endsWith("/Manifest") || uriWithoutParams.endsWith("/manifest")) {
                extension = "Manifest";
            } else {
                int lastDotId = uriWithoutParams.lastIndexOf('.');
                if (lastDotId < uriWithoutParams.length() - 1) {
                    extension = uriWithoutParams.substring(lastDotId + 1, uriWithoutParams.length());
                }
            }
            Log.d(TAG, "getMediaTypeFromUri - media extension: " + extension);

            if (null != uriScheme && (uriScheme.equalsIgnoreCase("http") ||
                    uriScheme.equalsIgnoreCase("https"))) {

                // check for an .m3u8 or .event (NXP internal) extension
                if ("m3u8".equalsIgnoreCase(extension) || "event".equalsIgnoreCase(extension)) {
                    Log.i(TAG, "getMediaTypeFromUri - HTTP streaming uri detected");
                    type = StreamType.HTTPSTR_UNKNOWN;
                } else if ("mpd".equalsIgnoreCase(extension) || "Manifest".equalsIgnoreCase(extension)) {
                    type = StreamType.HTTPSTR_UNKNOWN;
                } else if ("3gp".equalsIgnoreCase(extension) ||
                        "mp4".equalsIgnoreCase(extension) ||
                        "mp3".equalsIgnoreCase(extension) ||
                        "avi".equalsIgnoreCase(extension) ||
                        "ogg".equalsIgnoreCase(extension)) {
                    Log.i(TAG, "getMediaTypeFromUri - HTTP progressive download uri detected");
                    type = StreamType.HTTP_PGDL;
                } else if (uriStr.contains("anifest")) {
                    type = StreamType.HTTPSTR_UNKNOWN;
                }
            } else {
                type = StreamType.LOCAL_FILE;
            }
        }
        Log.v(TAG, "getMediaTypeFromUri - found type: " + type.name());
        return type;
    }

    private void internalCleanUp() {

        cancelABRGraphUpdateTimer();
        if (null != mMediaPlayer) {
            //YOUBORA_ANALYTICS: mYouboraPlugin.stopMonitoring();
            //CONVIVA_ANALYTICS: mConvivaPlugin.cleanupSession();
            //AGAMA_ANALYTICS: LVAgamaPlugin.getInstance().stopPlaybackSession();
            mMediaPlayer.release();
            mMediaPlayer = null;
        }

        mAudioOnlyDrawable = null;

        // tear down demo setup if any (VOPlayer specific, not for production)
        if (null != mDemoMode) {
            mDemoMode.tearDown();
            mDemoMode = null;
        }
    }

    private void writeCurrentPlaybackPositionInDb() {
        MediaMetaInfoDB database = MediaMetaInfoDB.getInstance(getApplicationContext());

        int playbackPosition = mMediaPlayer.getCurrentPosition();
        Log.d(TAG, "writeCurrentPlaybackPositionInDb: " + playbackPosition);
        if (null != mDlOriginalUri) {
            database.setResumePositionForMedia(mCurrentMediaId, playbackPosition, mDownloadedMediaDuration);
        } else {
            database.setResumePositionForMedia(mCurrentMediaId, playbackPosition, mMediaPlayer.getDuration());
        }
    }


    /*************************************************************/
    /******************* Events after key press ******************/
    /*************************************************************/
    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP) || (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
            // Use audio booster mode to maximize volume once the AuidoManager max volume is reached.
            AudioManager audiomanager = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
            int maxVolume = audiomanager.getStreamMaxVolume(AudioManager.STREAM_MUSIC);
            int currentVolume = audiomanager.getStreamVolume(AudioManager.STREAM_MUSIC);
            if ((keyCode == KeyEvent.KEYCODE_VOLUME_UP)) {
                if (maxVolume == currentVolume && !mbAudioBoosterEnabled) {
                    activateAudioBooster(true, false); //enable audio booster if not already done
                }
            } else if ((keyCode == KeyEvent.KEYCODE_VOLUME_DOWN)) {
                if (maxVolume == currentVolume && mbAudioBoosterEnabled) {
                    activateAudioBooster(false, false); //disable audio booster if not already done
                }
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    /*************************************************************/
    /******************* Events after click **********************/
    /*************************************************************/
    public void onClick(View v) {

        if (v == mSubtitleActionBtn) {
            displaySubtitleQuickActionMenu();
        } else if (v == mAudioTrackBtn) {
            displayAudioTracksListAlert();
        } else if (v == mSetAlternateBtn) {
            displaySetAlternateListAlert();
        } else if (v == mWifiToggleBtn) {
            toggleWIFI();
        } else if (v == mAbrStreamingMinBRBtn) {
            setABRStreamingBitrateLimitUsingSlider(true);
        } else if (v == mAbrStreamingMaxBRBtn) {
            setABRStreamingBitrateLimitUsingSlider(false);
        } else if (v == mAbrStreamingOptBtn) {
            displaySideSection(SideSection.ABR_STREAMING);
        } else if (v == mTracksOptBtn) {
            displaySideSection(SideSection.TRACKS);
        } else if (v == mGenericOptBtn) {
            displaySideSection(SideSection.GENERIC_INFO);
        } else if (v == mAudioBoosterBtn) {
            toggleAudioBooster();
        } else if (v == mVideoBoosterBtn) {
            switchToNextVideoExperienceMode();
        } else if (v == mVEBoostBtn) {
            if (mCurrentVideoExperienceMode != VOPlayer.VIDEO_EXPERIENCE_OFF)
                setVEBoosttUsingSlider();
        } else if (v == mId3MetadataBtn) {
            toggleId3MetadataRetrieval();
        } else if (v == mAbrStreamingCustomBRDownBtn) {
            switchNextAlternateZeroLag(false);
        } else if (v == mAbrStreamingCustomBRUpBtn) {
            switchNextAlternateZeroLag(true);
        } else if (v == mFastZappingPPlusBtn) {
            switchChannel(true);
        } else if (v == mFastZappingPMinusBtn) {
            switchChannel(false);
        } else if (v == mJumpToLiveBtn) {
            mMediaPlayer.seekTo(mSeekableRangeLength);
        } else if (v == mPlayPauseBtn) {
            togglePlayPause();
        } else if (v == mDisplayBtn) {
            internalDisplayMode();
        } else if (v == mSkipAdBtn) {
            mMediaPlayer.skipMedia();
            mSkipAdBtn.setVisibility(View.GONE);
        } else if (v.getId() == R.id.abr_grah_btn) {
            if (abrGraphVisible) {
                abrGraphWebView.setVisibility(View.GONE);
            } else {
                abrGraphWebView.setVisibility(View.VISIBLE);
            }
            abrGraphVisible = !abrGraphVisible;
        }
    }

    /*************************************************************/
    /******************* Events from slider **********************/
    /*************************************************************/
    @Override
    public void onSliderBtnClick() {
        Log.d(TAG, "onSliderBtnClick");
        if (!mSettingVEBoost) {
            if (mSettingABRStreamingMinLimit) {
                setABRStreamingMinBitrateLimit(0);
            } else {
                int maxBitrate = VODeviceAdaptation.getRecommendedMaxBitrate(getApplicationContext(), true);
                setABRStreamingMaxBitrateLimit(maxBitrate);
            }
        }
        mSliderPopup.userDismiss();
    }

    @Override
    public void onSliderUpdate(int newValue) {
        if (mSettingVEBoost) {
            setVEBoost(newValue);
            Log.v(TAG, "setVideoExperienceConfig with VE Boost= " + mVEBoost);

            /* compute boost value to set to VEHD lib */
            setVideoExperienceConfig(mCurrentVideoExperienceMode, mVEBoost);
        }
    }

    @Override
    public void onSliderDismiss(int newValue) {
        if (!mSettingVEBoost) {
            if (mSettingABRStreamingMinLimit) {
                setABRStreamingMinBitrateLimit(newValue);
            } else {
                setABRStreamingMaxBitrateLimit(newValue);
            }
        } else {
            setVEBoost(newValue);
        }
    }

    /*************************************************************/
    /************* Generic private methods ***********************/
    /*************************************************************/
    /**
     * Internal method to handle click on 'Display mode' button
     */
    private void internalDisplayMode() {
        nextDisplayMode();
        startAutoHideTimer();
    }

    /**
     * Switches to the next video display mode (looping in this order: fit->stretched->cropped)
     */
    private void nextDisplayMode() {
        int nextDisplayMode = 0;
        switch (mCurrentDisplayMode) {
            case VOPlayer.DISPLAY_MODE_CROP:
                nextDisplayMode = VOPlayer.DISPLAY_MODE_FIT;
                mDisplayBtn.setImageResource(R.drawable.aspect_ratio_fit);
                break;
            case VOPlayer.DISPLAY_MODE_FIT:
                nextDisplayMode = VOPlayer.DISPLAY_MODE_STRETCH;
                mDisplayBtn.setImageResource(R.drawable.aspect_ratio_stretch);
                break;
            case VOPlayer.DISPLAY_MODE_STRETCH:
                nextDisplayMode = VOPlayer.DISPLAY_MODE_CROP;
                mDisplayBtn.setImageResource(R.drawable.aspect_ratio_crop);
                break;
        }
        setDisplayMode(nextDisplayMode);
    }

    private void displaySideSection(SideSection section) {
        switch (section) {
            case GENERIC_INFO:
                mGenericOptSection.setVisibility(View.VISIBLE);
                mTracksOptSection.setVisibility(View.GONE);
                mAbrStreamingOptSection.setVisibility(View.GONE);
                mGenericOptBtn.getBackground().setColorFilter(mSelectedBtnColorFilter);
                mTracksOptBtn.getBackground().clearColorFilter();
                mAbrStreamingOptBtn.getBackground().clearColorFilter();
                break;
            case ABR_STREAMING:
                mGenericOptSection.setVisibility(View.GONE);
                mTracksOptSection.setVisibility(View.GONE);
                mAbrStreamingOptSection.setVisibility(View.VISIBLE);
                mGenericOptBtn.getBackground().clearColorFilter();
                mTracksOptBtn.getBackground().clearColorFilter();
                mAbrStreamingOptBtn.getBackground().setColorFilter(mSelectedBtnColorFilter);
                break;
            case TRACKS:
                mGenericOptSection.setVisibility(View.GONE);
                mTracksOptSection.setVisibility(View.VISIBLE);
                mAbrStreamingOptSection.setVisibility(View.GONE);
                mGenericOptBtn.getBackground().clearColorFilter();
                mTracksOptBtn.getBackground().setColorFilter(mSelectedBtnColorFilter);
                mAbrStreamingOptBtn.getBackground().clearColorFilter();
                break;
        }
    }


    /************************************************************/
    /************* Stack controls related methods ***************/
    /************************************************************/

    /**
     * Display an alert popup window to select the Alternate
     * Pressing back cancels the alert.
     */
    private void displaySetAlternateListAlert() {

        // Retrieve list from player if available
        VOAlternateInfo[] AlternatesInfo = mMediaPlayer.getAlternatesInfo();

        if ((null != AlternatesInfo) && (AlternatesInfo.length > 0)) {

            String[] AlternatesInfoList = new String[AlternatesInfo.length + 1];
            int selectedAlternate = 0;
            int trackId = 0;
            for (VOAlternateInfo AlternateInfo : AlternatesInfo) {
                AlternatesInfoList[trackId] = "Resolution: " + AlternateInfo.getVideoWidth() + "x" + AlternateInfo.getVideoHeight() + " bitrate: " + AlternateInfo.getBitrate();
                Log.d(TAG, "displaySetAlternateListAlert Alternate: " + AlternatesInfoList[trackId]);

                if (AlternateInfo.IsSelected()) {
                    selectedAlternate = trackId;
                }
                trackId++;
            }
            AlternatesInfoList[trackId] = "Automatic ABR Streaming mode";
            BasicStringAdapter AlternatesListListAdapter = new BasicStringAdapter(getApplicationContext(), R.layout.list_basic_item, AlternatesInfoList);

            // Highlight the current Alternate
            AlternatesListListAdapter.setSelectedItem(selectedAlternate);
            createAndShowCustomListDialog(ALTERNATES_LIST_DIALOG, R.string.title_select_alternate, AlternatesListListAdapter);
        } else {
            Toast.makeText(getApplicationContext(), "No set alternate option", Toast.LENGTH_SHORT).show();
        }
    }

    private void setAlternateByIndex(int trackId) {
        Log.d(TAG, "setAlternateByIndex: " + trackId);

        VOAlternateInfo[] AlternatesInfo = mMediaPlayer.getAlternatesInfo();
        try {
            if (trackId >= AlternatesInfo.length) {
                mMediaPlayer.setAlternateByIndex(-1);
            } else {
                mMediaPlayer.setAlternateByIndex(trackId);
            }
        } catch (UnsupportedOperationException e) {
            e.printStackTrace();
        }
    }

    private void setABRStreamingBitrateLimitUsingSlider(boolean minLimit) {

        mSettingABRStreamingMinLimit = minLimit;
        mSettingVEBoost = false;
        mSliderPopup.setSliderStepSize(100);
        if (mSettingABRStreamingMinLimit) {
            mSliderPopup.setSliderMinValue(0);
            mSliderPopup.setSliderMaxValue(5000);
            mSliderPopup.setSliderText("Min: ");
            mSliderPopup.show(mAbrStreamingMinBRBtn, mAbrStreamingMinBitrateLimit);
        } else {
            mSliderPopup.setSliderMinValue(100);
            mSliderPopup.setSliderMaxValue(10000);
            mSliderPopup.setSliderText("Max: ");
            mSliderPopup.show(mAbrStreamingMinBRBtn, mAbrStreamingMaxBitrateLimit);
        }
    }

    private void toggleWIFI() {
        WifiManager wifiMgr = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);

        switch (wifiMgr.getWifiState()) {
            case WifiManager.WIFI_STATE_ENABLED:
                if (wifiMgr.setWifiEnabled(false)) {
                    mWifiToggleBtn.setImageResource(R.drawable.wifi_off);
                    Toast.makeText(getApplicationContext(), R.string.disabling_wifi, Toast.LENGTH_SHORT).show();
                }
                break;

            case WifiManager.WIFI_STATE_DISABLED:
                if (wifiMgr.setWifiEnabled(true)) {
                    mWifiToggleBtn.setImageResource(R.drawable.wifi_on);
                    Toast.makeText(getApplicationContext(), R.string.enabling_wifi, Toast.LENGTH_SHORT).show();
                }
                break;

            default:
                Toast.makeText(getApplicationContext(), "Current WIFI state cannot be changed...", Toast.LENGTH_SHORT).show();
                break;
        }
        if (null != mDemoMode) {
            mDemoMode.updateFromEvent(VoDemo.WIFI_TOGGLE_EVENT);
        }
    }


    private void setLiveBtnVisible(boolean visible) {
        if (visible) {
            mJumpToLiveBtn.setVisibility(View.VISIBLE);
        } else {
            mJumpToLiveBtn.setVisibility(View.GONE);
        }
    }

    private void setPlaybackTime(int time) {
        if (null != mElapsedTimeTxt) {
            if (time < 0) {
                mElapsedTimeTxt.setText("-" + VOUtils.convertTimeToText(-time));
            } else {
                mElapsedTimeTxt.setText(VOUtils.convertTimeToText(time));
            }
            if (null != mDemoMode) {
                mDemoMode.updateFromEvent(VoDemo.PROGRESS_CHANGED_EVENT);
            }
        }
    }

    private void setPlayPauseBtnState(PlayPauseBtnState state) {
        if (null == mPlayPauseBtn) {
            Log.w(TAG, "wrong/no reference for play/pause button");
            return;
        }
        if (mStreamFromLocalServer && !mbPreparedCBReceived) {
            state = PlayPauseBtnState.DISABLE;
        }
        switch (state) {
            case PAUSE:
                mPlayPauseBtn.setEnabled(true);
                mPlayPauseBtn.setImageDrawable(mPauseBtnDrawable);
                break;
            case PLAY:
                mPlayPauseBtn.setEnabled(true);
                mPlayPauseBtn.setImageDrawable(mPlayBtnDrawable);
                break;
            case DISABLE:
                mPlayPauseBtn.setEnabled(false);
                break;
        }
        mPlayPauseBtn.invalidate();
        mPlayPauseState = state;
    }

    private void setPlaybackCursorPosition(int position) {
        if (position <= mSeekbar.getMax()) {
            mSeekbar.setProgress(position);
        }
    }

    private void setDebugLevelFromPreference(HashMap<VOPlayer.DebugModule, VOPlayer.DebugLevel> levels,
                                             VOPlayer.DebugModule debugModule, String prefLogLevelName) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
        String prefValue = prefs.getString(prefLogLevelName, "Default");

        switch (prefValue) {
            case "None":
                levels.put(debugModule, VOPlayer.DebugLevel.DBG_LVL_NO);
                break;
            case "Error":
                levels.put(debugModule, VOPlayer.DebugLevel.DBG_LVL_ERROR);
                break;
            case "Warning":
                levels.put(debugModule, VOPlayer.DebugLevel.DBG_LVL_WARNING);
                break;
            case "Entry":
                levels.put(debugModule, VOPlayer.DebugLevel.DBG_LVL_ENTRY);
                break;
            case "Info":
                levels.put(debugModule, VOPlayer.DebugLevel.DBG_LVL_INFO);
                break;
            case "Dev":
                levels.put(debugModule, VOPlayer.DebugLevel.DBG_LVL_DEV);
                break;
            default:
                break;
        }
    }


    /**
     * Creates the internal VOPlayer is needed
     *
     * @return true if the creation went well or if the player was already created, false otherwise
     */
    private boolean createMediaplayer() {

        if (null == mMediaPlayer) {
            Log.v(TAG, "createMediaplayer - VOPlayer is null => create it");
            try {
                mMediaPlayer = new VOPlayer(getApplicationContext());
                mMediaPlayer.adSetRequest("https://pubads.g.doubleclick.net/gampad/ads?sz=640x480&iu=/124319096/external/ad_rule_samples&ciu_szs=300x250&ad_rule=1&impl=s&gdfp_req=1&env=vp&output=vmap&unviewed_position_start=1&cust_params=deployment%3Ddevsite%26sample_ar%3Dpremidpostoptimizedpod&cmsid=496&vid=short_onecue&correlator=",
                        VOPlayer.AdRequestType.AD_REQUEST_VMAP_URI);
                mIsInErrorState = false;
                initializeYoubora();
            } catch (IllegalStateException e) {
                Log.e(TAG, "Impossible to create VOPlayer instance, device is probably rooted");
                Toast.makeText(getApplicationContext(), "Impossible to create MediaPlayer, device is probably rooted", Toast.LENGTH_LONG).show();
                return false;
            } catch (SecurityException e) {
                /* a security exception is thrown when the needed permissions are not granted by the user */
                Log.e(TAG, "Impossible to create VOPlayer instance, read phone state permission probably not granted");
                Toast.makeText(getApplicationContext(), "Impossible to create MediaPlayer, missing READ_PHONE_STATE permission", Toast.LENGTH_LONG).show();
                return false;
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        mFlat360Manager = mMediaPlayer.getFlat360Manager();

        /* Debug Logs */
        HashMap<VOPlayer.DebugModule, VOPlayer.DebugLevel> debugLevels = new HashMap<VOPlayer.DebugModule, VOPlayer.DebugLevel>();

        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_AUDIO_DECODER, Constants.PREF_LOGLEVEL_AUDIO_DECODER);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_AUDIO_PRESENTER, Constants.PREF_LOGLEVEL_AUDIO_PRESENTER);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_AUDIO_SERVICE, Constants.PREF_LOGLEVEL_AUDIO_SERVICE);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_DEPACK, Constants.PREF_LOGLEVEL_DEPACK);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_DRM, Constants.PREF_LOGLEVEL_DRM);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_DRM_AGENT, Constants.PREF_LOGLEVEL_DRM_AGENT);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_HTTP_STACK, Constants.PREF_LOGLEVEL_HTTP_STACK);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_LVMC_AUDIO, Constants.PREF_LOGLEVEL_LVMC_AUDIO);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_LVMC_JNI, Constants.PREF_LOGLEVEL_LVMC_JNI);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_LVMC_VIDEO, Constants.PREF_LOGLEVEL_LVMC_VIDEO);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_LVVTB_VIDEO, Constants.PREF_LOGLEVEL_LVVTB_VIDEO);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_M4TOOL_URI, Constants.PREF_LOGLEVEL_M4TOOL_URI);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_MSH, Constants.PREF_LOGLEVEL_MSH);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_NET_ENG, Constants.PREF_LOGLEVEL_NET_ENG);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_NET_ENG_AUCB, Constants.PREF_LOGLEVEL_NET_ENG_AUCB);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_OFFLINE_VIEWING, Constants.PREF_LOGLEVEL_OFFLINE_VIEWING);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_PGDL_ENG, Constants.PREF_LOGLEVEL_PGDL_ENG);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_PLAYER_GENERIC, Constants.PREF_LOGLEVEL_PLAYER_GENERIC);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_QPPROXY, Constants.PREF_LOGLEVEL_QP_PROXY);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_SSM, Constants.PREF_LOGLEVEL_SSM);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_SUBTITLE_DECODER, Constants.PREF_LOGLEVEL_SUBTITLE_DECODER);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VIDEO_DECODER, Constants.PREF_LOGLEVEL_VIDEO_DECODER);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VIDEO_PRESENTER, Constants.PREF_LOGLEVEL_VIDEO_PRESENTER);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VIDEO_RENDERER, Constants.PREF_LOGLEVEL_VIDEO_RENDERER);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VIDEO_SINK, Constants.PREF_LOGLEVEL_VIDEO_SINK);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VPS, Constants.PREF_LOGLEVEL_VPS);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VPS_API, Constants.PREF_LOGLEVEL_VPS_API);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VPS_SUB, Constants.PREF_LOGLEVEL_VPS_SUB);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VPSIL, Constants.PREF_LOGLEVEL_VPSIL);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VPSIL_CB, Constants.PREF_LOGLEVEL_VPSIL_CB);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VPSIL_OTHERS, Constants.PREF_LOGLEVEL_VPSIL_OTHERS);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VPSIL_UTILS, Constants.PREF_LOGLEVEL_VPSIL_UTILS);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VAST, Constants.PREF_LOGLEVEL_VAST);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VMAP, Constants.PREF_LOGLEVEL_VMAP);
        setDebugLevelFromPreference(debugLevels, VOPlayer.DebugModule.DBG_MODULE_VMX, Constants.PREF_LOGLEVEL_VMX);

        mMediaPlayer.setDebugLevel(debugLevels);

        return true;
    }

    private void initializeYoubora() {
        YouboraLog.setDebugLevel(YouboraLog.Level.VERBOSE);
        Options youboraOptions = YouboraConfigManager.Companion.getInstance().getOptions(this);
        youboraPlugin = new Plugin(youboraOptions);
        youboraPlugin.setActivity(this);
        youboraPlugin.setAdapter(new ViaccessorcaAdapter(mMediaPlayer));
        youboraPlugin.setAdsAdapter(new ViaccessorcaAdsAdapter(mMediaPlayer));
    }

    private void preparePlayback() {

        if (!mSurfaceCreated) {
            Log.e(TAG, "preparePlayback - error, the surface does not exists");
            return;
        }
        if (null == mMediaPlayer) {
            Log.e(TAG, "playerSetup - error, the player does not exists");
            return;
        }
        String currentVersion = "";
        try {
            currentVersion = VOPlayer.getSdkVersion(getApplicationContext());
        } catch (VOException e) {
            e.printStackTrace();
        }

        //check if surface secure is disabled in settings
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
        Boolean bSurfaceSecure = prefs.getBoolean(Constants.PREF_SURFACE_SECURE, true);
        if (false == bSurfaceSecure) {
            mDisplaySurface.setSecure(false);
        }

        setMediaName(mMediaUri.getLastPathSegment());

        /** if the media is ABR Streaming or progressive download, we first check
         * for the availability of a data connection (unless streaming from local server)*/
        String mediaScheme = mMediaUri.getScheme();
        if (null != mediaScheme) {
            if (mediaScheme.startsWith("http") && !mStreamFromLocalServer) {
                boolean dataConnectionAvailable = isDataConnectionAvailable();
                if (!dataConnectionAvailable) {
                    Log.w(TAG, "start - No data connection available !!");
                    Toast.makeText(getApplicationContext(), "Warning, no data connection available.", Toast.LENGTH_SHORT).show();
                }
            }
        }
        mbInEventMode = false;

        // set all the listeners on the player
        mMediaPlayer.setOnInfoListener(this);
        mMediaPlayer.setOnErrorListener(this);
        mMediaPlayer.setOnPreparedListener(this);
        mMediaPlayer.setOnVideoSizeChangedListener(this);
        mMediaPlayer.setOnSeekCompleteListener(this);
        mMediaPlayer.setOnBufferingUpdateListener(this);
        mMediaPlayer.setOnDownloadUpdateListener(this);
        mMediaPlayer.setOnCompletionListener(this);

        try {
            /* Set the cast context first thing because below API calls may require to know */
            if (mSessionManager != null) {
                MediaMetadata movieMetadata = new MediaMetadata(MediaMetadata.MEDIA_TYPE_MOVIE);
                if (null != mStoredName) {
                    movieMetadata.putString(MediaMetadata.KEY_TITLE, mStoredName);
                } else {
                    movieMetadata.putString(MediaMetadata.KEY_TITLE, "my content title");
                }
                movieMetadata.addImage(new WebImage(Uri.parse(Constants.LOGO_VO_URL)));

                mMediaPlayer.setCastContext(CastContext.getSharedInstance(this));
                mMediaPlayer.setCastChannelName(getString(R.string.custom_receiver_channel_urn));
                mMediaPlayer.setCastMetadata(movieMetadata);

                if ((null != mSessionManager) &&
                        (null != mSessionManager.getCurrentCastSession()) &&
                        (null != mSessionManager.getCurrentCastSession().getRemoteMediaClient())) {
                    mSessionManager.getCurrentCastSession().getRemoteMediaClient().registerCallback(mRemoteMediaClient);
                }

                // Set Chromecast alternate parameters, may be null
                DrmAgent.EDrmType castAlternateDrmAgentType;
                if (mCastAlternateDrmAgentType != null) {
                    if (mCastAlternateDrmAgentType.equals(Constants.DRM_AGENT_TYPE_WIDEVINE)) {
                        castAlternateDrmAgentType = DrmAgent.EDrmType.DRM_TYPE_WIDEVINE;
                    } else {
                        if (mCastAlternateDrmAgentType.equals(Constants.DRM_AGENT_TYPE_PLAYREADY)) {
                            castAlternateDrmAgentType = DrmAgent.EDrmType.DRM_TYPE_PLAYREADY;
                        } else {
                            Log.w(TAG, "DRM Agent Type " + mCastAlternateDrmAgentType + " not supported by chromecast");
                            castAlternateDrmAgentType = DrmAgent.EDrmType.DRM_TYPE_UNKNOWN;
                        }
                    }
                } else {
                    castAlternateDrmAgentType = DrmAgent.EDrmType.DRM_TYPE_UNKNOWN;
                }
                mMediaPlayer.setChromeCastAlternateParameters(mCastAlternateUrl,
                        mCastAlternateDrmLicenserUrl,
                        castAlternateDrmAgentType,
                        (mCastAlternateDrmCookie == null) ? null : mCastAlternateDrmCookie.split(";"),
                        mCastAlternateDrmCustomData);
            }

            mMediaPlayer.setProxyType(mProxyType);
            if (mProxyType > 0) {
                mMediaPlayer.setProxyPort(mProxyPort);
                mMediaPlayer.setProxyUrl(mProxyUrl);
            }

            mMediaPlayer.setAdSeekToAllowed(mbSetAdSeeToAllowed);
            mMediaPlayer.setPlatformAudioDecodingEnabled(mbPlatformAudioDecodingEnabled);

            setMarlinDecryptorEnabled(mbMarlinProtected);

            if (null != mDrmAgentType) {
                //check the player is built for WV/PR
                if (currentVersion.contains("vmx_") && (mDrmAgentType.equals(Constants.DRM_AGENT_TYPE_PLAYREADY)
                        || mDrmAgentType.equals(Constants.DRM_AGENT_TYPE_VODRM))) {
                    Log.w(TAG, "Wrong file format for the player");
                    Toast.makeText(getApplicationContext(), "Wrong file format for the player",
                            Toast.LENGTH_SHORT).show();
                    return;
                }

                if (mDrmAgentType.equals(Constants.DRM_AGENT_TYPE_PLAYREADY)) {
                    try {
                        if (VOPlayer.getDRMAgentVersion() == VOPlayer.DRMAgentVersion.VERSION_1) {
                            // specifc code for PlayReady legacy configuration
                            mMediaPlayer.getAgentPlayready().setPersonalizationURL("file:///android_asset/PlayReady/");
                        } else {
                            if (null != mDrmPersonalizationUrl) {
                                mMediaPlayer.getAgentPlayready().setPersonalizationURL(mDrmPersonalizationUrl);
                            } else {
                                mMediaPlayer.getAgentPlayready().setPersonalizationURL("debug://localpersonalization");
                            }
                            if (null != mDrmPersonalizationAppVersion) {
                                mMediaPlayer.getAgentPlayready().setPersonalizationAppVersion(mDrmPersonalizationAppVersion);
                            }
                            if (null != mDrmPersonalizationCustomerID) {
                                mMediaPlayer.getAgentPlayready().setPersonalizationCustomerIdentifier(mDrmPersonalizationCustomerID);
                            }
                            mMediaPlayer.getAgentPlayready().setPersonalizationCustomData(mDrmPersonalizationCustomData);
                        }
                        mMediaPlayer.getAgentPlayready().setLicenseAcquisitionURL(mDrmLicenseAcquisitionUrl);
                        mMediaPlayer.getAgentPlayready().setLicenseAcquisitionURLParameters(mDrmLicenseAcquisitionUrlParameters);
                        mMediaPlayer.getAgentPlayready().setLicenseAcquisitionCustomData(mDrmLicenseAcquisitionCustomData);
                        //mMediaPlayer.getAgentPlayready().setContentIdentifier(mDrmContentIdentifier);
                        //mMediaPlayer.getAgentPlayready().useVOSecurityGateway(mDrmUseVOSecurityGateway);
                        if (null != mDrmLicenseAcquisitionCookie) {
                            String[] cookies = new String[1];
                            cookies[0] = mDrmLicenseAcquisitionCookie;
                            mMediaPlayer.getAgentPlayready().setLicenseAcquisitionCookies(cookies);
                        } else {
                            mMediaPlayer.getAgentPlayready().setLicenseAcquisitionCookies(null);
                        }
                    } catch (VOException e) {
                        Log.e(TAG, "exception during DRM parameters set" + e.getStatus().toString());
                    }
                } else if (mDrmAgentType.equals(Constants.DRM_AGENT_TYPE_WIDEVINE)) {
                    mMediaPlayer.getAgentWidevine().setLicenseAcquisitionURL(mDrmLicenseAcquisitionUrl);
                    mMediaPlayer.getAgentWidevine().setLicenseAcquisitionURLParameters(
                            mDrmLicenseAcquisitionUrlParameters);
                    mMediaPlayer.getAgentWidevine().setLicenseAcquisitionType(
                            VOWidevineAgent.EDrmLicenseAcquisitionType.PERSISTENT);
                    if (null != mDrmLicenseAcquisitionCookie) {
                        String[] cookies = new String[1];
                        cookies[0] = mDrmLicenseAcquisitionCookie;
                        mMediaPlayer.getAgentWidevine().setLicenseAcquisitionCookies(cookies);
                    }

                    try {
                        mMediaPlayer.getAgentWidevine().setContentIdentifier(mDrmContentIdentifier);
                        mMediaPlayer.getAgentWidevine().useVOSecurityGateway(mDrmUseVOSecurityGateway);
                    } catch (VOException e) {
                        Log.e(TAG, "exception during DRM parameters set" + e.getStatus().toString());
                    }
                } else if (mDrmAgentType.equals(Constants.DRM_AGENT_TYPE_VODRM)) {
                    try {
                        mMediaPlayer.getAgentVodrm().setPersonalizationURL(mDrmPersonalizationUrl);
                        mMediaPlayer.getAgentVodrm().setLicenseAcquisitionURL(mDrmLicenseAcquisitionUrl);
                        mMediaPlayer.getAgentVodrm().setLicenseAcquisitionURLParameters(
                                mDrmLicenseAcquisitionUrlParameters);
                        if (null != mDrmLicenseAcquisitionCookie) {
                            String[] cookies = new String[1];
                            cookies[0] = mDrmLicenseAcquisitionCookie;
                            mMediaPlayer.getAgentVodrm().setLicenseAcquisitionCookies(cookies);
                        }
                        //mMediaPlayer.getAgentVodrm().setContentIdentifier(mDrmContentIdentifier);
                        //mMediaPlayer.getAgentVodrm().useVOSecurityGateway(mDrmUseVOSecurityGateway);
                    } catch (VOException e) {
                        Log.e(TAG, "exception during DRM parameters set" + e.getStatus().toString());
                    }
                }
            } else if ((null != mVmxBootAddress) && (null != mVmxCompanyName)) {
                //check the player is built for VMX
                if (!currentVersion.contains("vmx_")) {
                    Log.w(TAG, "Wrong file format for the player");
                    Toast.makeText(getApplicationContext(), "Wrong file format for the player", Toast.LENGTH_SHORT).show();
                    return;
                }

                if (null != mMediaPlayer.getAgentVerimatrix()) {
                    Log.v(TAG, "set verimatrix parameters:" + mVmxBootAddress + ", " + mVmxCompanyName);
                    if (null != mVmxBootAddress) {
                        mMediaPlayer.getAgentVerimatrix().setBootAddress(mVmxBootAddress);
                    }
                    if (null != mVmxCompanyName) {
                        mMediaPlayer.getAgentVerimatrix().setCompanyName(mVmxCompanyName);
                    }
                }
                mDrmType = SECURED_ABR_FLAG;
                setDRMType();
            }

            if (null != mHttpAuthUsername && null != mHttpAuthPassword) {
                mMediaPlayer.setHttpAuthentication(mHttpAuthUsername, mHttpAuthPassword);
            }
            if (null != mSSLCACertFilePath && Constants.SSL_TYPE_UNKNOWN != mSSLCACertFileType) {
                mMediaPlayer.setSSLCACert(mSSLCACertFilePath, mSSLCACertFileType);
            }
            if (null != mSSLClientCertFilePath && Constants.SSL_TYPE_UNKNOWN != mSSLClientCertFileType &&
                    null != mSSLClientKeyFilePath && Constants.SSL_TYPE_UNKNOWN != mSSLClientKeyFileType) {
                mMediaPlayer.setSSLClientCert(mSSLClientCertFilePath, mSSLClientCertFileType, mSSLClientKeyFilePath, mSSLClientKeyFileType);
            }
            if (null != mHttpCustomHeader) {
                mMediaPlayer.setHttpCustomHeader(mHttpCustomHeader);
            }

            // this try/ctach section is here to intercept exceptions that would result from the use of an "unprivileged" license
            try {
                mMediaPlayer.setHttpStreamingEventPlaylistEnabled(mForceEventPlaylist);

                // sets the bitrates for Streaming playback, only if explicitely set by integrator
                if (-1 != mMaxABRStreamingBitrate) {
                    mMediaPlayer.setHttpStreamingMaximumBitrate(mMaxABRStreamingBitrate * 1000);
                }
                if (-1 != mMinABRStreamingBitrate) {
                    mMediaPlayer.setHttpStreamingMinimumBitrate(mMinABRStreamingBitrate * 1000);
                }

                computeABRStreamingTypicalBitrate();
                mMediaPlayer.setHttpStreamingTypicalBitrate(mTypicalABRStreamingBitrate * 1000);

                if (null != mCurrentAudioTrackName) {
                    Log.d(TAG, "preparePlayerForPlayback - audio track: " + mCurrentAudioTrackName);
                    mMediaPlayer.setAudioTrack(mCurrentAudioTrackName);
                }

                if (VOSubtitle.Type_WEBVTT == mSubtitleTrackType && null != mSubtitleTrackName) {
                    mMediaPlayer.setSubtitleTrack(mSubtitleTrackName, mSubtitleTrackType);
                }
                if (VOSubtitle.Type_TTML == mSubtitleTrackType && null != mSubtitleTrackName) {
                    mMediaPlayer.setSubtitleTrack(mSubtitleTrackName, mSubtitleTrackType);
                }
                if (VOSubtitle.Type_DVB == mSubtitleTrackType && null != mSubtitleTrackName) {
                    mMediaPlayer.setSubtitleTrack(mSubtitleTrackName, mSubtitleTrackType);
                }

                if (null != mVastUrl) {
                    try {
                        Log.d(TAG, "preparePlayerForPlayback - set Vast url " + mVastUrl);
                        mMediaPlayer.adSetRequest(mVastUrl, VOPlayer.AdRequestType.AD_REQUEST_VAST_URI);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                if (null != mVmapUrl) {
                    try {
                        Log.d(TAG, "preparePlayerForPlayback - set Vmap url " + mVmapUrl);
                        mMediaPlayer.adSetRequest(mVmapUrl, VOPlayer.AdRequestType.AD_REQUEST_VMAP_URI);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                }
                if (null != mSecondaryContentUri) {
                    Log.d(TAG, "preparePlayerForPlayback - add secondary content " + mSecondaryContentUri);
                    VOSecondaryContent preRollAdvertisement = new VOSecondaryContent(mSecondaryContentUri, 0, false);
                    mMediaPlayer.addSecondaryContent(preRollAdvertisement);
                }
                mMediaPlayer.setVideoExperienceBenchmarkEnabled(true);

                if (null != mAbrStreamingSensitivityValue) {
                    if (mAbrStreamingSensitivityValue.equals("LOW")) {
                        mCustomNetworkMode = 1;
                    } else if (mAbrStreamingSensitivityValue.equals("HIGH")) {
                        mCustomNetworkMode = 2;
                    }
                } else if (null != mZeroLagBitrates) {
                    mCustomNetworkMode = 3;
                    mOverlayInfosLayout.setVisibility(View.VISIBLE);
                    setHttpBitratesLimits(mZeroLagBitrates[mCurrentZeroLagBitrate] - 50, mZeroLagBitrates[mCurrentZeroLagBitrate] + 50);
                }

                //show player info if enabled in settings
                mMediaPlayer.setDebugOption(VOPlayer.DebugOption.DBG_OPT_SHOW_PLAYER_INFO, mbShowPlayerInfo);

                setPlayerCustomModes();
            } catch (IllegalStateException ise) {
                Log.w(TAG, "preparePlayerForPlayback, state exception: " + ise.getMessage());
            } catch (UnsupportedOperationException uoe) {
                Log.w(TAG, "preparePlayerForPlayback, state exception: " + uoe.getMessage());
            }

            mMediaPlayer.setVideoView(mDisplaySurface);

            mMediaPlayer.setDataSource(getApplicationContext(), mMediaUri);
            mFlat360Manager.setEnabled(mb360Content);
            if (mMediaListSize > 0) {
                mMediaPlayer.setClearVideoViewAtReset(true);
            }

        } catch (IllegalArgumentException e) {
        } catch (IOException e) {
        } catch (java.lang.UnsupportedOperationException e) {
            e.printStackTrace();
        }

        // apply Demonstration if any (VOPLAYER_APP ONLY, not for production)
        if (null != mDemoMode) {
            mDemoMode.prepare(mMediaPlayer, mMediaUri, mSeekbar, mAbrStreamingCurrentThroughPutBR);
        }

        try {
            // screen will stay on while playing back
            mMediaPlayer.setScreenOnWhilePlaying(true);

            mDisplaySurface.setSubtitleCustomCSSStyling(mCustomSubtitleCSSStyling);

            setNotification(true, "Opening");

            //YOUBORA_ANALYTICS: mYouboraPlugin.setTitle(mMediaUri.toString());
            //YOUBORA_ANALYTICS: // Set media options here if needed
            //YOUBORA_ANALYTICS: //HashMap<String, Object> mediaOptions = new HashMap<>();
            //YOUBORA_ANALYTICS: //mediaOptions.put("duration", 200.0); //you can set the mediaduration here if you know it in advance - it is required to have the duration displayed on YOUBORA when the playback fails.
            //YOUBORA_ANALYTICS: //mYouboraPlugin.getOptions().put("media", mediaOptions);
            //YOUBORA_ANALYTICS: // Set properties here if needed
            //YOUBORA_ANALYTICS: //HashMap<String, Object> propertiesOptions = new HashMap<>();
            //YOUBORA_ANALYTICS: //propertiesOptions.put("contentId", "ContentTest");
            //YOUBORA_ANALYTICS: //propertiesOptions.put("genre", "Action");
            //YOUBORA_ANALYTICS: //mYouboraPlugin.getOptions().put("properties", propertiesOptions);
            //YOUBORA_ANALYTICS: mYouboraPlugin.startMonitoring(mMediaPlayer);

            //CONVIVA_ANALYTICS: try {
            //CONVIVA_ANALYTICS: Map<String, String> tags = new HashMap<>();
            //CONVIVA_ANALYTICS: mConvivaMetadata = new ContentMetadata();
            //CONVIVA_ANALYTICS: mConvivaMetadata.assetName = mMediaUri.toString();
            //CONVIVA_ANALYTICS: mConvivaMetadata.custom = tags;
            //CONVIVA_ANALYTICS: mConvivaMetadata.defaultResource = "UNKNOWN";
            //CONVIVA_ANALYTICS: mConvivaMetadata.viewerId = VOPlayer.getUniqueIdentifier(getApplicationContext());
            //CONVIVA_ANALYTICS: mConvivaMetadata.applicationName = "SQUADEO ANDROID";
            //CONVIVA_ANALYTICS: mConvivaMetadata.encodedFrameRate = 30;
            //CONVIVA_ANALYTICS: mConvivaMetadata.defaultBitrateKbps = -1;
            //CONVIVA_ANALYTICS: //don't set following metadata, it will be automatically set by the plugin once the video is prepared
            //CONVIVA_ANALYTICS: //mConvivaMetadata.streamUrl = mMediaUri.toString();
            //CONVIVA_ANALYTICS: //mConvivaMetadata.streamType = ContentMetadata.StreamType.VOD;
            //CONVIVA_ANALYTICS: //mConvivaMetadata.duration = 0;
            //CONVIVA_ANALYTICS: mConvivaPlugin.createSession(mMediaPlayer, VOPlayer.getSdkVersion(getApplicationContext()), "DemoPlayer", mConvivaMetadata);
            //CONVIVA_ANALYTICS: }catch (VOException e){
            //CONVIVA_ANALYTICS: e.printStackTrace();
            //CONVIVA_ANALYTICS: }

            //AGAMA_ANALYTICS: LVAgamaPlugin.getInstance().startPlaybackSession(mMediaPlayer,"channelName");

            mMediaPlayer.prepareAsync();
        } catch (IllegalStateException e) {
            e.printStackTrace();
        }
    }

    private void setPlayerCustomModes() {
        HashMap<String, Object> customization = new HashMap<>();

        if (0 != mCustomNetworkMode) {
            if (1 == mCustomNetworkMode) {
                customization.put(CustomMode.ADAPTIVE_BITRATE_SENSITIVITY, CustomMode.LOW);
            } else if (2 == mCustomNetworkMode) {
                customization.put(CustomMode.ADAPTIVE_BITRATE_SENSITIVITY, CustomMode.HIGH);
            } else if (3 == mCustomNetworkMode) {
                customization.put("ZeroLagAlternateSwicthDemo", 1);
            }
            if (mMinABRStreamingBitrate != -1 && mMaxABRStreamingBitrate != 1) {
                mMediaPlayer.setHttpStreamingTypicalBitrate((mMinABRStreamingBitrate + mMaxABRStreamingBitrate) * 500);
            }
        }

        if (0 <= mLiveLatencyValue) {
            customization.put(VOPlayer.CustomMode.LIVE_LATENCY, mLiveLatencyValue);
        }

        if (mbForceMSSUpdateManifest) {
            customization.put(CustomMode.FORCE_MSS_UPDATE_MANIFEST_MODE, CustomMode.ENABLE);
        }

        if (mScrubbingEnabled) {
            customization.put(VOPlayer.CustomMode.SCRUBBING_MODE, VOPlayer.CustomMode.ENABLE);

            if (mScrubbingOptionSpriteResolution != null &&
                    mScrubbingOptionSpriteColums != null &&
                    mScrubbingOptionSpriteLines != null &&
                    mScrubbingOptionSpriteInterval != null) {

                VOPlayer.ScrubbingSpriteInfo spriteInfo = new VOPlayer.ScrubbingSpriteInfo();
                spriteInfo.mResolution = mScrubbingOptionSpriteResolution;
                spriteInfo.mSpriteColums = Integer.parseInt(mScrubbingOptionSpriteColums);
                spriteInfo.mSpriteLines = Integer.parseInt(mScrubbingOptionSpriteLines);
                spriteInfo.mInterval = Integer.parseInt(mScrubbingOptionSpriteInterval);
                customization.put(CustomMode.SCRUBBING_OPTION_SPRITE, spriteInfo);
            }
            if (mScrubbingOptionAsset != null) {
                customization.put(CustomMode.SCRUBBING_OPTION_ASSET, mScrubbingOptionAsset);
            }
            if (mScrubbingOptionChannel != null) {
                customization.put(CustomMode.SCRUBBING_OPTION_CHANNEL, mScrubbingOptionChannel);
            }
            if (mSrubbingOptionServer != null) {
                customization.put(CustomMode.SCRUBBING_OPTION_SERVER, mSrubbingOptionServer);
            }
            if (mScrubbingOptionStart != null) {
                customization.put(CustomMode.SCRUBBING_OPTION_START, mScrubbingOptionStart);
            }
            if (mScrubbingOptionEnd != null) {
                customization.put(CustomMode.SCRUBBING_OPTION_END, mScrubbingOptionEnd);
            }
            if (mScrubbingOptionServiceType != null) {
                if (mScrubbingOptionServiceType.contentEquals("VOD")) {
                    customization.put(CustomMode.SCRUBBING_OPTION_SERVICE_TYPE, VOPlayer.ScrubbingSpriteServiceType.VOD);
                } else if (mScrubbingOptionServiceType.contentEquals("CATCHUP")) {
                    customization.put(CustomMode.SCRUBBING_OPTION_SERVICE_TYPE, VOPlayer.ScrubbingSpriteServiceType.CATCHUP);
                } else {
                    customization.put(CustomMode.SCRUBBING_OPTION_SERVICE_TYPE, VOPlayer.ScrubbingSpriteServiceType.NPVR);
                }
            }
        }

        if (mDisableMaxPlayableHeight) {
            customization.put(VOPlayer.CustomMode.DISABLE_MAX_PLAYABLE_HEIGHT_MODE, VOPlayer.CustomMode.ENABLE);
        }

        if (mIPResolveType.equals("IPv4")) {
            customization.put(VOPlayer.CustomMode.PREFERRED_IP_RESOLVE_TYPE, VOPlayer.CustomMode.IPV4);
        } else if (mIPResolveType.equals("IPv6")) {
            customization.put(VOPlayer.CustomMode.PREFERRED_IP_RESOLVE_TYPE, VOPlayer.CustomMode.IPV6);
        }

        if (mbTemporaryRedirectAsPermanent) {
            customization.put(VOPlayer.CustomMode.FORCE_TEMPORARY_REDIRECT_AS_PERMANENT, VOPlayer.CustomMode.ENABLE);
        }

        mMediaPlayer.enableSubtitlesOSD(mbEnableSubtitleRendering);

        if (mFaasOfflineEnabled || null != mDbFaasOffline) {
            String pathDataBase;
            Log.d(TAG, "FaasOffline enabled");
            if (null != mDbFaasOffline) {
                pathDataBase = mDbFaasOffline;
            } else {
                pathDataBase = Environment.getExternalStorageDirectory().getAbsolutePath() + "/qp3_medias/dbFaasOffline.zip";
            }
            customization.put(VOPlayer.CustomMode.FAAS_OFFLINE_MODE, pathDataBase);
        }
        if (mCodecType > VOPlayer.CODECS_TYPE_DEFAULT) {
            customization.put(VOPlayer.CustomMode.CODEC_TYPE, mCodecType);
        }
        if (!customization.isEmpty()) {
            try {
                mMediaPlayer.setCustomMode(customization);
            } catch (UnsupportedOperationException e) {
                Log.w(TAG, "Custom mode feature not available");
            }
        }
    }

    private void togglePlayPause() {
        if (PlayPauseBtnState.PAUSE == mPlayPauseState) {
            internalPause();
        } else if (PlayPauseBtnState.PLAY == mPlayPauseState) {
            internalResume();
        }
        startAutoHideTimer();
    }

    private void internalPause() {
        if (null != mMediaPlayer) {
            mMediaPlayer.pause();
            setPlayPauseBtnState(PlayPauseBtnState.PLAY);
        }
    }

    private void internalResume() {
        if (null != mMediaPlayer) {
            try {
                mFlat360Manager.setZoom(m360zoom);
                mFlat360Manager.setAspectRatio(m360ratio);
            } catch (java.lang.UnsupportedOperationException e) {
                e.printStackTrace();
            }
            mMediaPlayer.start();
            startProgressTimer();
            setPlayPauseBtnState(PlayPauseBtnState.PAUSE);
        }
    }

    private void internalStart() {
        Log.v(TAG, "internalStart START");
        if (null != mMediaPlayer) {

            if (mResumePlaybackPosition > 0) {
                Log.v(TAG, "internalStart - mediaplayer startAt " + mResumePlaybackPosition);
                mMediaPlayer.startAt(mResumePlaybackPosition);
                mResumePlaybackPosition = 0;
            } else {
                Log.v(TAG, "internalStart - mediaplayer start");
                try {
                    mFlat360Manager.setZoom(m360zoom);
                    mFlat360Manager.setAspectRatio(m360ratio);
                } catch (java.lang.UnsupportedOperationException e) {
                    e.printStackTrace();
                }
                mMediaPlayer.start();
            }
            if (mVideoExperienceEffectMode > 0) {
                Message msg = Message.obtain(null, 0, 0, 0);
                mVideoExpHandler.sendMessageDelayed(msg, 50);
            }
            mCanSeek = true;
            setNotification(false, null);
            startAutoHideTimer();
            updateMediaControls();
            startProgressTimer();
        }
        Log.v(TAG, "internalStart END");
    }

    /**
     * Sets the video display mode (fit, stretched, cropped)
     *
     * @param displayMode video display mode to use
     */
    private void setDisplayMode(int displayMode) {
        if (null != mMediaPlayer) {
            mMediaPlayer.changeDisplayMode(displayMode);
            mCurrentDisplayMode = displayMode;
        }
    }

    private final Handler mHideThumbnailHandler = new Handler(Looper.getMainLooper()) {
        public void handleMessage(Message msg) {
            mThumbnailImageViewOverlay.startAnimation(mThumbnailFadeOutAnim);
        }
    };

    private void setABRStreamingMinBitrateLimit(int newLimit) {
        Log.v(TAG, "setABRStreamingMinBitrateLimit");
        mAbrStreamingMinBitrateLimit = newLimit;

        mAbrStreamingMinBRBtn.setText("Min: " + mAbrStreamingMinBitrateLimit + " kbps");
        setHttpBitratesLimits(mAbrStreamingMinBitrateLimit, mAbrStreamingMaxBitrateLimit);
        if (mAbrStreamingMaxBitrateLimit <= mAbrStreamingMinBitrateLimit) {
            setABRStreamingMaxBitrateLimit(mAbrStreamingMaxBitrateLimit + 100);
        }
    }

    private void setABRStreamingMaxBitrateLimit(int newValue) {
        Log.v(TAG, "changeHTTPStreamingUpperLimit");
        mAbrStreamingMaxBitrateLimit = newValue;
        mAbrStreamingMaxBRBtn.setText("Max: " + mAbrStreamingMaxBitrateLimit + " kbps");
        setHttpBitratesLimits(mAbrStreamingMinBitrateLimit, mAbrStreamingMaxBitrateLimit);
        if (mAbrStreamingMinBitrateLimit >= mAbrStreamingMaxBitrateLimit) {
            setABRStreamingMinBitrateLimit(mAbrStreamingMaxBitrateLimit - 100);
        }
    }

    private void updateCurrentHTTPBitrateTxt() {
        if (null == mDlOriginalUri) {
            if (null != mDemoMode) {
                mDemoMode.updateFromEvent(VoDemo.BITRATE_CHANGE_EVENT);
            } else {
                int currentBitrate = mMediaPlayer.getHttpStreamingCurrentBitrate();
                mAbrStreamingCurrentThroughPutBR.setText("Cur: " + (currentBitrate / 1000) + " kbps");
                mAbrStreamingCurrentBufferLevel.setText("Buffer: " + mMediaPlayer.getCurrentBufferLevel() + " ms");
            }
        }
    }

    /**
     * Computes the ABR Streaming typical bitrate based on the type of data network available
     */
    private void computeABRStreamingTypicalBitrate() {
        ConnectivityManager connectMgr = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        final android.net.NetworkInfo wifi = connectMgr.getNetworkInfo(ConnectivityManager.TYPE_WIFI);
        final android.net.NetworkInfo mobile = connectMgr.getNetworkInfo(ConnectivityManager.TYPE_MOBILE);
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());

        if (wifi != null && wifi.isAvailable()) {
            mTypicalABRStreamingBitrate = Integer.parseInt(prefs.getString(Constants.PREF_TYPICAL_BITRATE, String.valueOf(TYPICAL_HTTP_STREAMING_BITRATE_WIFI)));
        } else if (mobile != null && mobile.isAvailable()) {
            mTypicalABRStreamingBitrate = TYPICAL_HTTP_STREAMING_BITRATE_3G;
        }
        // by default, we assume the worst and use the 3G typical bitrate
        else {
            mTypicalABRStreamingBitrate = TYPICAL_HTTP_STREAMING_BITRATE_3G;
        }
    }

    /*************************************************************/
    /************* Surface event handling  ***********************/
    /*************************************************************/
    @Override
    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surfaceCreated" + holder);
        mSurfaceCreated = true;

        if (null == mDisplaySurface) {
            mDisplaySurface = (VOSurfaceView) findViewById(R.id.VideoSurface);
        }
        if (null != mMediaPlayer) {
            // update the video surface here in case of change
            mMediaPlayer.setVideoView(mDisplaySurface);
        } else {
            createMediaplayer();
            boolean doPrepare = true;
            if (null != mDlOriginalUri) {
                doPrepare = checkForDownloadUseCaseBeforePlayback();
                updateMediaControls();
            }
            if (doPrepare) {
                preparePlayback();
            }
        }
    }


    @Override
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
        // nothing to be done here
    }

    @Override
    public void surfaceDestroyed(SurfaceHolder holder) {
        mSurfaceCreated = false;
    }

    /*************************************************************/
    /********** Event handling from mediaplayer ******************/
    /*************************************************************/
    @Override
    public void onPrepared(VOPlayer player) {
        if(mMediaPlayer.isPrimaryContentPlaying()){
            Log.d(TAG, "Content will be the primary");
        } else  {
            Log.d(TAG, "Content will be an Advertisement");
        }
        mbPreparedCBReceived = true;
        int streamDuration = mMediaPlayer.getDuration();
        Log.d(TAG, "onPrepared - streamDuration: " + streamDuration);

        if (null != mDisplaySurface) {
            mDisplaySurface.setBackgroundColor(Color.TRANSPARENT);
        }
        setDisplayMode(mCurrentDisplayMode);
        mMediaPlayer.setLooping(mLoopingPlayback);
        setSubtitlesOnPrepared();

        startABRGraphUpdateTimer();

        if (StreamType.HTTPSTR_UNKNOWN == mMediaType) {
            if (streamDuration > 0) {
                mMediaType = StreamType.HTTPSTR_VOD;
            } else {
                mMediaType = StreamType.HTTPSTR_LIVE;
            }
        }

        //initialize seekable range before starting playback
        mSeekableRange = mMediaPlayer.getHttpStreamingSeekableRange();
        mSeekableRangeLength = mSeekableRange[1] - mSeekableRange[0];

        // start playback if necessary
        if (mIsActivtyResumed) {
            if (mbInEventMode) {
                mMediaType = StreamType.HTTPSTR_EVENT;
                if (null != mDlOriginalUri) {
                    internalStart();
                } else {
                    askForStartPositionForHTTPStrEvent();
                }
            } else {
                internalStart();
            }
        }

        setNotification(false, null);

        //check for availability of AudioBooster Feature in the SDK
        try {
            activateAudioBooster(false, true);
            mAudioBoosterBtn.setVisibility(View.VISIBLE);
            mAudioBoosterBtn.setEnabled(true);
        } catch (UnsupportedOperationException e) {
            mAudioBoosterBtn.setVisibility(View.GONE);
            Log.w(TAG, "onPrepared - AudioBooster feature not available");
        }

        //check for availability of VideoBooster Feature in the SDK
        try {
            setVideoExperienceConfig(VOPlayer.VIDEO_EXPERIENCE_OFF, 0);
            mVideoBoosterBtn.setVisibility(View.GONE);
            mVideoBoosterText.setVisibility(View.GONE);
        } catch (UnsupportedOperationException e) {
            Log.w(TAG, "onPrepared - VideoBooster feature not available");
        }
        mbIsVideoExperienceAllowed = canVideoExperienceBeEnabled();

        switch (mMediaPlayer.getCodecsType()) {
            case VOPlayer.CODECS_TYPE_HW_MC_TU:
                mHwCodecIndicText.setText("HW-MC-TU");
                break;
            case VOPlayer.CODECS_TYPE_HW_MC_NT:
                mHwCodecIndicText.setText("HW-MC-NT");
                break;
            case VOPlayer.CODECS_TYPE_HW_SF_TU:
                mHwCodecIndicText.setText("HW-SF-TU");
                break;
            case VOPlayer.CODECS_TYPE_HW_SF_NT:
                mHwCodecIndicText.setText("HW-SF-NT");
                break;
            case VOPlayer.CODECS_TYPE_SW:
                mHwCodecIndicText.setText("SW");
                break;
            case VOPlayer.CODECS_TYPE_AML:
                mHwCodecIndicText.setText("HW-AML");
            case VOPlayer.CODECS_TYPE_HW_MC_TU_TV:
                mHwCodecIndicText.setText("HW-MC-TU-TV");
                break;
        }
        updateUIElements();
    }

    /**
     * Handles the info messages from the media player
     *
     * @param what  info description
     * @param extra addition info description
     */
    public boolean onInfo(VOPlayer mp, int what, int extra) {
        switch (what) {
            case VOPlayer.MEDIA_INFO_HTTP_STREAMING:
                handleHTTPStreamingEvent(extra);
                break;

            case VOPlayer.MEDIA_INFO_NETWORK:
                if (VOPlayer.MEDIA_INFO_NETWORK_DOWN == extra) {
                    Toast.makeText(getApplicationContext(), "Can not connect to server, trying to recover Url...", Toast.LENGTH_SHORT).show();
                } else if (VOPlayer.MEDIA_INFO_NETWORK_NO_FILE == extra) {
                    Toast.makeText(getApplicationContext(), "File not found on server, trying to recover Url...", Toast.LENGTH_SHORT).show();
                } else if (VOPlayer.MEDIA_INFO_NETWORK_UP == extra) {
                    Toast.makeText(getApplicationContext(), "Url recovered !", Toast.LENGTH_SHORT).show();
                }
                break;

            case VOPlayer.MEDIA_INFO_GENERIC:
                if (VOPlayer.MEDIA_INFO_FIRST_FRAME_DISPLAYED == extra) {
                    if (mbIsScrubbingAvailable && mLastSeekStep) {
                        mLastSeekStep = false;
                        mHideThumbnailHandler.removeMessages(10000);
                        mHideThumbnailHandler.sendEmptyMessageDelayed(10000, 650);
                    }
                } else if (VOPlayer.MEDIA_INFO_SCRUBBING_AVAILABLE == extra) {
                    mbIsScrubbingAvailable = true;
                }
                break;
            case VOPlayer.MEDIA_INFO_BENCHMARK:
                handleBenchmarkInfoEvent(extra);
                break;
            case VOPlayer.MEDIA_INFO_DRM:
                handleDRMInfoEvent(extra);
                break;
            case VOPlayer.MEDIA_INFO_VAST:
                handleVastInfoEvent(extra);
                break;
            case VOPlayer.MEDIA_INFO_VAST_SKIPPABLE_IN_SEC:
                handleVastSkippableEvent(extra);
                break;
            case VOPlayer.MEDIA_INFO_AD_SWITCH_POINT_DETECTED:
                Log.d(TAG, "MEDIA_INFO_AD_SWITCH_POINT_DETECTED with duration = " + extra);
                break;
            case VOPlayer.MEDIA_INFO_CONTENT_INSERTION:
                //use same controls/messages than Vast
                if (VOPlayer.MEDIA_INFO_PRIMARY_CONTENT_STARTED == extra) {
                    handleVastInfoEvent(VOPlayer.MEDIA_INFO_VAST_NOT_ADVERTISEMENT);
                } else if (VOPlayer.MEDIA_INFO_SECONDARY_CONTENT_STARTED == extra) {
                    handleVastInfoEvent(VOPlayer.MEDIA_INFO_VAST_ADVERTISEMENT);
                }
                break;
            case VOPlayer.MEDIA_INFO_VIDEOEXPERIENCEHD:
                if (VOPlayer.MEDIA_INFO_VIDEOEXPERIENCEHD_ENABLE == extra) {
                    mbIsVideoExperienceAllowed = true;
                } else if (VOPlayer.MEDIA_INFO_VIDEOEXPERIENCEHD_DISABLE == extra) {
                    mbIsVideoExperienceAllowed = false;
                }
                updateVideExperienceBtn();
                break;
            case VOPlayer.MEDIA_INFO_NEW_CHUNK_DOWNLOADED:
                handleDownloadedFragment(extra);
                break;
            case VOPlayer.MEDIA_INFO_SUBTITLE:
                if (VOPlayer.MEDIA_INFO_NEW_SUBTITLE_TRACK == extra) {
                    handleSubtitleTrackUpdate();
                }
                break;
            default:
                // we don't use other information from the player
                break;
        }
        ;
        return true;
    }

    private void handleSubtitleTrackUpdate() {
        // if we had a track type to set and it a 'discoverable one' (dvb, smpte, CEA-608), we need to set the track now
        if (null != mSubtitleTrackName &&
                (VOSubtitle.Type_SUB_FILE != mSubtitleTrackType) &&
                (VOSubtitle.Type_WEBVTT != mSubtitleTrackType) &&
                (VOSubtitle.Type_TTML != mSubtitleTrackType)) {
            mMediaPlayer.setSubtitleTrack(mSubtitleTrackName, mSubtitleTrackType);
        }
    }

    private void handleDownloadedFragment(int mediaType) {
        String type;
        if (null != mMediaPlayer) {

            VODownloadedFragment fragmentInfo = mMediaPlayer.getDownloadedFragment(mediaType);
            switch (mediaType) {
                case VOPlayer.MEDIA_INFO_FRAGMENT_TYPE_VIDEO:
                    type = "Video";
                    break;
                case VOPlayer.MEDIA_INFO_FRAGMENT_TYPE_AUDIO:
                    type = "Audio";
                    break;
                case VOPlayer.MEDIA_INFO_FRAGMENT_TYPE_TEXT:
                    type = "Text";
                    break;
                case VOPlayer.MEDIA_INFO_FRAGMENT_TYPE_DATA:
                    type = "Data";
                    break;
                case VOPlayer.MEDIA_INFO_FRAGMENT_TYPE_UNKNOWN:
                default:
                    type = "Unknown";
                    break;
            }
            if (fragmentInfo != null) {
                Log.d(TAG, "Downloaded " + type + "_" + fragmentInfo.getBitrate() +
                        " (" + fragmentInfo.getFragmentDuration() + " ms) fragment: " + fragmentInfo.getUrl() +
                        ". Downloaded " + fragmentInfo.getDownloadSize() + " bytes during " + fragmentInfo.getDownloadDuration() +
                        " ms");
            } else {
                Log.e(TAG, "getDownloadedFragment error");
            }

        }

    }

    private void handleAlternateChange() {
        if (null == mMediaPlayer) {
            mAbrStreamingVariantBRText.setText("Alt: - kbps");
            mOverlayBitrateText.setText("Alt: - kbps");
            return;
        }
        int newAlternateBitrate = mMediaPlayer.getHttpStreamingAlternateBitrate();

        if (mCurrentAlternateBitrate != newAlternateBitrate) {
            String txt = "Alt: " + (newAlternateBitrate / 1000) + " kbps";
            mAbrStreamingVariantBRText.setText(txt);
            mOverlayBitrateText.setText(txt);
            mCurrentAlternateBitrate = newAlternateBitrate;

            mAbrStreamingVariantBRText.setTextColor(Color.RED);
            mOverlayBitrateText.setTextColor(Color.RED);
            mAsyncHandler.postDelayed(new ResetABRStreamingAlternateTextColorTask(), 1500);
        } else {
            Log.i(TAG, "handleAlternateChange - new alternate with same bitrate..." + mCurrentAlternateBitrate);
        }
        mAbrStreamingCustomBRDownBtn.getBackground().clearColorFilter();
        mAbrStreamingCustomBRUpBtn.getBackground().clearColorFilter();
        updateUiAudioOnly();
    }

    private void updateUiAudioOnly() {
        if ((null != mMediaPlayer) && mMediaPlayer.isHttpStreamingAlternateAudioOnly()) {
            Log.d(TAG, "audio only alternate");
            mDisplaySurface.setBackgroundDrawable(mAudioOnlyDrawable);
        } else {
            Log.d(TAG, "audio/video alternate");
            mDisplaySurface.setBackgroundDrawable(null);
        }
    }

    private class ResetABRStreamingAlternateTextColorTask implements Runnable {
        public void run() {
            mAbrStreamingVariantBRText.setTextColor(Color.WHITE);
            mOverlayBitrateText.setTextColor(Color.WHITE);
        }
    }

    private void handleSeekableRangeUpdate() {
        mSeekableRange = mMediaPlayer.getHttpStreamingSeekableRange();
        mSeekableRangeLength = mSeekableRange[1] - mSeekableRange[0];
        //update seekbar max (except when downloader is downloading)
        if (mDownloadedMediaDuration < 0) {
            if (mSeekableRangeLength > 0 && StreamType.HTTPSTR_LIVE != mMediaType && StreamType.HTTPSTR_UNKNOWN != mMediaType) {
                setMaxPlaybackCursorProgressValue(mSeekableRangeLength);
                //update media duration for EVENT streams
                setMediaDuration(mSeekableRangeLength);
            }
        }
    }

    private void handleHTTPStreamingEvent(int event) {
        if (VOPlayer.MEDIA_INFO_ALTERNATE_CHANGE == event) {
            handleAlternateChange();
        } else if (VOPlayer.MEDIA_INFO_CURRENT_BITRATE == event) {
            updateCurrentHTTPBitrateTxt();
        } else if (VOPlayer.MEDIA_INFO_EVENT_PLAYLIST_START == event) {
            mbInEventMode = true;
            setLiveBtnVisible(true);
        } else if (VOPlayer.MEDIA_INFO_EVENT_PLAYLIST_STOP == event) {
            mbInEventMode = false;
            setLiveBtnVisible(false);
            handleSeekableRangeUpdate();
        } else if (VOPlayer.MEDIA_INFO_SEEKABLE_RANGE_CHANGED == event) {
            handleSeekableRangeUpdate();
        } else if (VOPlayer.MEDIA_INFO_NEW_METADATA_ID3 == event) {
            Message msg = Message.obtain(null, 0, 0, 0);
            //remove pending commands and post the new one
            mId3UpdatesHandler.removeMessages(0);
            mId3UpdatesHandler.sendMessage(msg);
        }
    }

    private void handleBenchmarkInfoEvent(int extra) {
        if (VOPlayer.MEDIA_INFO_BENCHMARK_STARTED == extra) {
            setNotification(true, "Benchmark running");
        } else if (VOPlayer.MEDIA_INFO_BENCHMARK_DONE == extra) {
            setNotification(true, "Opening");
        } else {
            Log.w(TAG, "handleBenchmarkInfoEvent - unknown extra value: " + extra);
        }
    }

    @Override
    public void onDownloadUpdate(VOPlayer mp, int percent) {
        mSeekbar.setSecondaryProgress(percent * getMediaDuration() / 100);
    }

    @Override
    public void onVideoSizeChanged(VOPlayer mp, int width, int height) {
        mVideoResolutionText.setText("Res: " + width + "x" + height);
        mOverlayVResolutionText.setText("Res: " + width + "x" + height);
        abrGraphWebView.loadUrl("javascript:setVideoResolution(" + width + ", " + height + ")");
    }

    public void onSeekComplete(VOPlayer mp) {
        Log.d(TAG, "internalOnSeekComplete");
        updateUiAudioOnly();
    }

    @Override
    public void onBufferingUpdate(VOPlayer player, int bufferPercentage) {
        updateBufferingIndication(bufferPercentage);
    }

    public void updateBufferingIndication(int bufferPercentage) {
        StreamType type = mMediaType;
        if (mbBufferingIndicationEnabled) {
            mNotificationText.setText("Buffering " + bufferPercentage + " %");
            if (bufferPercentage < 100) {
                mNotificationLayout.setVisibility(View.VISIBLE);
            } else {
                mNotificationLayout.setVisibility(View.GONE);
            }
        }
        if (StreamType.HTTPSTR_VOD == type || StreamType.HTTPSTR_LIVE == type) {
            if (100 == bufferPercentage) {
                // at the end of a buffering period, the alternate might have changed
                handleAlternateChange();
            }
            updateCurrentHTTPBitrateTxt();
        }
    }

    private String getErrorMessage(int error, int extra) {
        String errorStr = null;
        switch (extra) {
            case VOPlayer.MEDIA_INFO_NETWORK_NO_FILE:
                errorStr = "MEDIA_INFO_NETWORK_NO_FILE";
                break;
            case VOPlayer.MEDIA_INFO_NETWORK_DOWN:
                errorStr = "MEDIA_INFO_NETWORK_DOWN";
                break;
            case VOPlayer.MEDIA_ERROR_NETWORK_BAD_URL:
                errorStr = "MEDIA_ERROR_NETWORK_BAD_URL";
                break;
            case VOPlayer.MEDIA_INFO_GENERIC_HACKING_DETECTED:
                errorStr = "MEDIA_INFO_GENERIC_HACKING_DETECTED";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_FAIL:
                errorStr = "MEDIA_ERROR_DRM_FAIL";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_BAD_ARGUMENTS:
                errorStr = "MEDIA_ERROR_DRM_BAD_ARGUMENTS";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_MISSING_PERMISSION:
                errorStr = "MEDIA_ERROR_DRM_MISSING_PERMISSION";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_NETWORK_COMMUNICATION_FAILURE:
                errorStr = "MEDIA_ERROR_DRM_NETWORK_COMMUNICATION_FAILURE";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_TRUSTED_TIME_NOT_SET:
                errorStr = "MEDIA_ERROR_DRM_TRUSTED_TIME_NOT_SET";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_NOT_SUPPORTED:
                errorStr = "MEDIA_ERROR_DRM_NOT_SUPPORTED";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_INIT_PARAMS_NOT_SET:
                errorStr = "MEDIA_ERROR_DRM_INIT_PARAMS_NOT_SET";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_NOT_AUTHORIZED:
                errorStr = "MEDIA_ERROR_DRM_NOT_AUTHORIZED";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_SERVER_NOT_REACHABLE:
                errorStr = "MEDIA_ERROR_DRM_SERVER_NOT_REACHABLE";
                break;
            case VOPlayer.MEDIA_ERROR_DRM_NOT_INITIALIZED:
                errorStr = "MEDIA_ERROR_DRM_NOT_INITIALIZED";
                break;
            default:
                errorStr = "Error: 0x" + Integer.toHexString(error) + ", 0x" + Integer.toHexString(extra);
                break;
        }
        return errorStr;
    }

    @Override
    public boolean onError(VOPlayer player, int error, int extra) {
        Toast.makeText(getApplicationContext(), R.string.playback_error, Toast.LENGTH_SHORT).show();
        String errorStr = getErrorMessage(error, extra);
        setNotification(true, errorStr);
        Log.w(TAG, "onError player: " + errorStr);
        return true;
    }

    @Override
    public void onCompletion(VOPlayer arg0) {
        cancelProgressTimer();
        resetID3Views();
        setPlaybackCursorPosition(0);
        setPlaybackTime(0);
        setPlayPauseBtnState(PlayPauseBtnState.PLAY);
    }

    private void updatePrivateValues(int index){

        mMediaUri = Uri.parse(mPlaylistItem[index].getMediaUri());
        mCurrentMediaId = mPlaylistItem[index].getMediaId();
        mMediaType = getMediaTypeFromUri(mMediaUri);

        mVastUrl = mPlaylistItem[index].getVastUrl();
        mVmapUrl = mPlaylistItem[index].getVmapUrl();
        mSecondaryContentUri = mPlaylistItem[index].getSecondaryContentUri();
        mVmxBootAddress = mPlaylistItem[index].getBootAddress();
        mVmxCompanyName = mPlaylistItem[index].getCompanyName();
        mDrmAgentType = mPlaylistItem[index].getDrmAgentType();
        mDrmPersonalizationUrl = mPlaylistItem[index].getDrmPersonalizationUrl();
        mDrmPersonalizationAppVersion = mPlaylistItem[index].getDrmPersonalizationAppVersion();
        mDrmPersonalizationCustomData = mPlaylistItem[index].getDrmPersonalizationCustomData();
        mDrmLicenseAcquisitionUrl = mPlaylistItem[index].getDRMLicenseAcquisitionUrl();
        mDrmLicenseAcquisitionUrlParameters = mPlaylistItem[index].getDRMLicenseAcquisitionUrlParameters();
        mDrmLicenseAcquisitionCustomData = mPlaylistItem[index].getDRMLicenseAcquisitionCustomData();
        mDrmLicenseAcquisitionCookie = mPlaylistItem[index].getDRMLicenseAcquisitionCookie();
        mbMarlinProtected = mPlaylistItem[index].getMarlinProtected();
        mb360Content = mPlaylistItem[index].get360Content();
        mDrmUseVOSecurityGateway = mPlaylistItem[index].useVOSecurityGateway();
        mDrmContentIdentifier = mPlaylistItem[index].getDrmContentIdentifier();
        mCastAlternateUrl = mPlaylistItem[index].getChromeCastAlternateUrl();
        mCastAlternateDrmAgentType = mPlaylistItem[index].getChromeCastAlternateDrmAgentType();
        mCastAlternateDrmLicenserUrl = mPlaylistItem[index].getChromeCastAlternateDrmLicenserUrl();
        mCastAlternateDrmCustomData = mPlaylistItem[index].getChromeCastAlternateDrmCustomData();
        mCastAlternateDrmCookie = mPlaylistItem[index].getChromeCastAlternateDrmCookie();
        mSrubbingOptionServer = mPlaylistItem[index].getScrubbingOptionServer();
        mScrubbingOptionChannel = mPlaylistItem[index].getScrubbingOptionChannel();
        mScrubbingOptionAsset = mPlaylistItem[index].getScrubbingOptionAsset();
        mScrubbingOptionServiceType = mPlaylistItem[index].getScrubbingOptionServiceType();
        mScrubbingOptionStart = mPlaylistItem[index].getScrubbingOptionStart();
        mScrubbingOptionEnd = mPlaylistItem[index].getScrubbingOptionEnd();
        mScrubbingOptionSpriteResolution = mPlaylistItem[index].getScrubbingOptionSpriteResolution();
        mScrubbingOptionSpriteColums = mPlaylistItem[index].getScrubbingOptionSpriteColums();
        mScrubbingOptionSpriteLines = mPlaylistItem[index].getScrubbingOptionSpriteLines();
        mScrubbingOptionSpriteInterval = mPlaylistItem[index].getScrubbingOptionSpriteInterval();
        mStoredName = mPlaylistItem[index].getStoredName();
    }

    private void askForStartPositionForHTTPStrEvent() {
        final CharSequence[] posItems = {
                "Begin",
                "Live"
        };
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.select_start_pos);
        builder.setCancelable(false);
        builder.setItems(posItems, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
                dialog.dismiss();
                if (0 == item) {
                    // we can't use 0 (ruled out by internalStart) so we skip just 1 ms
                    mResumePlaybackPosition = 1;
                }
                internalStart();
            }
        });

        AlertDialog alert = builder.create();
        alert.show();
    }

    private void switchChannel(boolean channelUp) {
        //check if a switch stream has been set
        if (mMediaListSize <= 0) {
            Log.v(TAG, "switch channel will not be possible");
            Toast.makeText(getApplicationContext(), "Channel Switch not available for this media", Toast.LENGTH_SHORT).show();
            return;
        }
        mPlaylistDuration[mCurrentStream] = mMediaPlayer.getDuration();

        //YOUBORA_ANALYTICS: mYouboraPlugin.stopMonitoring();
        //CONVIVA_ANALYTICS: mConvivaPlugin.cleanupSession();

        mbPreparedCBReceived = false;
        if (mIsInErrorState) {
            Log.w(TAG, "switchChannel - player is is error state, we need to create a new one");
            mMediaPlayer.release();
            mMediaPlayer = null;
        } else {
            mMediaPlayer.reset();
        }

        if (channelUp) {
            mCurrentStream++;
            if (mCurrentStream >= mMediaListSize) {
                mCurrentStream = 0;
            }
        } else {
            mCurrentStream--;
            if (mCurrentStream < 0) {
                mCurrentStream = mMediaListSize - 1;
            }
        }

        updatePrivateValues(mCurrentStream);
        Log.d(TAG, "NEW URI : " + mMediaUri);
        Log.d(TAG, "Stream Type  : " + mMediaType);
        // compute restart time for ABR streams
        if ((StreamType.HTTPSTR_UNKNOWN == mMediaType)) {
            mCurrentTime = System.currentTimeMillis();
            if (mPlaylistDuration[mCurrentStream] != 0) {
                mResumePlaybackPosition = (int) ((mCurrentTime - mTimeAtStart) % mPlaylistDuration[mCurrentStream]);
            } else {
                mResumePlaybackPosition = (int) ((mCurrentTime - mTimeAtStart));
            }
        }
        setSeekBarVisibility(false);
        createMediaplayer();
        preparePlayback();
    }

    private void setMediaName(String name) {
        mMediaNameUIText.setText(name);
    }

    private void createAndShowCustomListDialog(int dialogType, int titleRes, ListAdapter adapter) {

        mListDialog = new Dialog(this, R.style.dialog_qp_fullscreen_notitle);

        mListDialog.setContentView(R.layout.custom_list_dialog);
        mListDialog.setCancelable(true);

        ((TextView) mListDialog.findViewById(R.id.custom_dialog_title)).setText(titleRes);

        ListView dialogListView = (ListView) mListDialog.findViewById(R.id.custom_dialog_listview);
        dialogListView.setAdapter(adapter);

        mCurrentDialogType = dialogType;
        dialogListView.setOnItemClickListener(mInternalDialogListListener);
        mListDialog.setTitle(titleRes);
        mListDialog.show();
    }

    private class ListAdapterListener implements AdapterView.OnItemClickListener {
        public void onItemClick(AdapterView<?> dialog, View currenView, int item, long value) {
            Log.d(TAG, "ListAdapterListener.onItemClick: " + item);
            if (null != mListDialog) {
                mListDialog.dismiss();

                if (SUBTITLE_FILES_LIST_DIALOG == mCurrentDialogType) {
                    setSubtitleFromDialogList(item);
                } else if (AUDIO_TRACKS_LIST_DIALOG == mCurrentDialogType) {
                    setAudioTrack(item);
                } else if (ALTERNATES_LIST_DIALOG == mCurrentDialogType) {
                    setAlternateByIndex(item);
                }
                mCurrentDialogType = -1;
            }
        }

        ;
    }

    /************************************************************/
    /*************** Subtitle related methods *******************/
    /************************************************************/

    private void displaySubtitleQuickActionMenu() {
        boolean bShowSubFiles = false;
        boolean bShowSubTracks = false;
        mQuickAction = new QuickAction(this, QuickAction.VERTICAL);

        if (mMediaType == StreamType.LOCAL_FILE || mMediaType == StreamType.HTTP_PGDL || mMediaType == StreamType.HTTPSTR_VOD) {
            //Subtitle file button
            ActionItem selectSubtitleItem = new ActionItem(ACTION_ID_SELECT_SUBTITLE, getString(R.string.select_subtitle_action));
            selectSubtitleItem.setIcon(getResources().getDrawable(R.drawable.list_action_icon));
            selectSubtitleItem.setSticky(true);
            mQuickAction.addActionItem(selectSubtitleItem);
            bShowSubFiles = true;
        }

        if (mMediaType == StreamType.HTTPSTR_UNKNOWN || mMediaType == StreamType.HTTPSTR_LIVE || mMediaType == StreamType.HTTPSTR_VOD || mMediaType == StreamType.HTTPSTR_EVENT) {
            //CEA608 button
            ActionItem selectCCTrackItem = new ActionItem(ACTION_ID_SELECT_CC_TRACK, getString(R.string.select_closedcaption_channel_action));
            selectCCTrackItem.setIcon(getResources().getDrawable(R.drawable.list_action_icon));
            selectCCTrackItem.setSticky(true);
            mQuickAction.addActionItem(selectCCTrackItem);

            //WebVTT button (show the button only if webvtt tracks are found)
            String tracks[] = getAvailableSubtitleTracks(VOSubtitle.Type_WEBVTT);
            if (null != tracks && 0 < tracks.length) {
                ActionItem selectWebVTTTrackItem = new ActionItem(ACTION_ID_SELECT_WEBVTT_TRACK, getString(R.string.select_webvtt_track_action));
                selectWebVTTTrackItem.setIcon(getResources().getDrawable(R.drawable.list_action_icon));
                selectWebVTTTrackItem.setSticky(true);
                mQuickAction.addActionItem(selectWebVTTTrackItem);
            }

            //TTML button (show the button only if ttml tracks are found)
            tracks = getAvailableSubtitleTracks(VOSubtitle.Type_TTML);
            if (null != tracks && 0 < tracks.length) {
                ActionItem selectTtmlTrackItem = new ActionItem(ACTION_ID_SELECT_TTML_TRACK, getString(R.string.select_ttml_track_action));
                selectTtmlTrackItem.setIcon(getResources().getDrawable(R.drawable.list_action_icon));
                selectTtmlTrackItem.setSticky(true);
                mQuickAction.addActionItem(selectTtmlTrackItem);
            }

            //SMPTE button (show the button only if smpte tracks are found)
            tracks = getAvailableSubtitleTracks(VOSubtitle.Type_SMPTE);
            if (null != tracks && 0 < tracks.length) {
                ActionItem selectSMPTETrackItem = new ActionItem(ACTION_ID_SELECT_SMPTE_TRACK, getString(R.string.select_smpte_track_action));
                selectSMPTETrackItem.setIcon(getResources().getDrawable(R.drawable.list_action_icon));
                selectSMPTETrackItem.setSticky(true);
                mQuickAction.addActionItem(selectSMPTETrackItem);
            }

            //DVB Sub button (show the button only if DVB tracks are found)
            tracks = getAvailableSubtitleTracks(VOSubtitle.Type_DVB);
            if (null != tracks && 0 < tracks.length) {
                ActionItem selectDVBTrackItem = new ActionItem(ACTION_ID_SELECT_DVB_TRACK, getString(R.string.select_dvb_track_action));
                selectDVBTrackItem.setIcon(getResources().getDrawable(R.drawable.list_action_icon));
                selectDVBTrackItem.setSticky(true);
                mQuickAction.addActionItem(selectDVBTrackItem);
            }
            bShowSubTracks = true;
        }

        //Show button to deactivate subtitles
        String subtitleFile = mSubtitleFilePath;
        int subTrackType = mSubtitleTrackType;
        Log.v(TAG, "displaySubtitleQuickActionMenu subtitleFile=" + subtitleFile + ", subTrackType=" + subTrackType + ", bShowSubTracks=" + bShowSubTracks + ", bShowSubFiles=" + bShowSubFiles);
        if (null == subtitleFile && bShowSubTracks) {

            if (VOSubtitle.Type_CC_CEA608 == subTrackType) {
                ActionItem closeCCItem = new ActionItem(ACTION_ID_CLOSE_CC_TRACK, getString(R.string.close_closedcaption_action));
                closeCCItem.setIcon(getResources().getDrawable(R.drawable.remove_action_icon));
                closeCCItem.setSticky(true);
                mQuickAction.addActionItem(closeCCItem);
            } else if (VOSubtitle.Type_WEBVTT == subTrackType) {
                ActionItem webvttItem = new ActionItem(ACTION_ID_CLOSE_WEBVTT_TRACK, getString(R.string.close_webvtt_action));
                webvttItem.setIcon(getResources().getDrawable(R.drawable.remove_action_icon));
                webvttItem.setSticky(true);
                mQuickAction.addActionItem(webvttItem);
            } else if (VOSubtitle.Type_TTML == subTrackType) {
                ActionItem ttmlItem = new ActionItem(ACTION_ID_CLOSE_TTML_TRACK, getString(R.string.close_ttml_action));
                ttmlItem.setIcon(getResources().getDrawable(R.drawable.remove_action_icon));
                ttmlItem.setSticky(true);
                mQuickAction.addActionItem(ttmlItem);
            } else if (VOSubtitle.Type_SMPTE == subTrackType) {
                ActionItem smpteItem = new ActionItem(ACTION_ID_CLOSE_SMPTE_TRACK, getString(R.string.close_smpte_action));
                smpteItem.setIcon(getResources().getDrawable(R.drawable.remove_action_icon));
                smpteItem.setSticky(true);
                mQuickAction.addActionItem(smpteItem);
            } else if (VOSubtitle.Type_DVB == subTrackType) {
                ActionItem smpteItem = new ActionItem(ACTION_ID_CLOSE_SMPTE_TRACK, getString(R.string.close_dvb_action));
                smpteItem.setIcon(getResources().getDrawable(R.drawable.remove_action_icon));
                smpteItem.setSticky(true);
                mQuickAction.addActionItem(smpteItem);
            } else if (VOSubtitle.Type_SUB_FILE == subTrackType) {
                ActionItem closeSubtitleItem = new ActionItem(ACTION_ID_CLOSE_SUBTITLE, getString(R.string.close_subtitle_action));
                closeSubtitleItem.setIcon(getResources().getDrawable(R.drawable.remove_action_icon));
                closeSubtitleItem.setSticky(true);
                mQuickAction.addActionItem(closeSubtitleItem);

                String tracks[] = getAvailableSubtitleTracks(VOSubtitle.Type_SUB_FILE);
                if (null != tracks && tracks.length > 1) {
                    ActionItem selectSubtitleTrackItem = new ActionItem(ACTION_ID_SELECT_SUBTITLE_TRACK, getString(R.string.select_subtitle_track_action));
                    selectSubtitleTrackItem.setIcon(getResources().getDrawable(R.drawable.list_action_icon));
                    selectSubtitleTrackItem.setSticky(true);
                    mQuickAction.addActionItem(selectSubtitleTrackItem);
                }
            }
        }
        mQuickAction.setOnActionItemClickListener(this);
        mQuickAction.show(mSubtitleActionBtn, 0);
    }

    private void setSubtitlesOnPrepared() {
        Log.d(TAG, "setSubtitlesOnPrepared " + mSubtitleFilePath);
        if (null != mSubtitleFilePath) {
            try {
                mMediaPlayer.openSubtitleFile(mSubtitleFilePath);
                mMediaPlayer.setSubtitleVisibility(true);
                //call setsubtitletrack except for webvtt/ttml. The tracks must be selected before prepare().
                if (VOSubtitle.Type_WEBVTT != mSubtitleTrackType && VOSubtitle.Type_TTML != mSubtitleTrackType && null != mSubtitleTrackName) {
                    mMediaPlayer.setSubtitleTrack(mSubtitleTrackName, mSubtitleTrackType);
                }
            } catch (IllegalStateException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (UnsupportedOperationException e) {
                e.printStackTrace();
            }
        }
    }

    private void setSubtitleFromDialogList(int itemId) {
        String subtitleFile = mSubtitleFilesList.get(itemId).getMediaUri();
        Log.d(TAG, "setSubtitleFile - " + subtitleFile);
        setSubtitleFile(subtitleFile);
    }

    /**
     * Sets the path of the file to use to read subtitles.<br/>
     * The file will be used directly is a media is already playing back;
     * otherwise, the path will be memorized and used for next playback.<br/>
     * To stop reading from a subtitle file, just call the method with a null parameter.
     *
     * @param filePath path of the file containing subtitles
     */
    private void setSubtitleFile(String filePath) {
        mSubtitleFilePath = filePath;
        mSubtitleTrackName = null;
        mSubtitleTrackType = VOSubtitle.Type_SUB_FILE;
        if (null != mMediaPlayer) {
            // clear current track if any
            setSubtitleTrack(null, VOSubtitle.Type_Unknown);
            // set subtitle file
            internalSetSubtitleFile();
        }
        writeSubtitleFileInDb(filePath);
    }

    private void setSubtitleTrack(String trackName, int trackType) {
        if ((mSubtitleTrackName != trackName) || (mSubtitleTrackType != trackType)) {
            mSubtitleTrackName = trackName;
            mSubtitleTrackType = trackType;
            if ((null != mMediaPlayer) && (null != mSubtitleTrackName)) {
                try {
                    mMediaPlayer.setSubtitleTrack(mSubtitleTrackName, mSubtitleTrackType);
                    mMediaPlayer.setSubtitleVisibility(true);
                } catch (UnsupportedOperationException e) {
                    e.printStackTrace();
                }
            }
        }
        writeSubtitleTrackInDb(trackName, trackType);
    }

    private void internalSetSubtitleFile() {
        if (null != mMediaPlayer) {
            Log.d(TAG, "internalSetSubtitle - mediaplayer exists");
            if (null != mSubtitleFilePath) {
                mMediaPlayer.closeSubtitleFile();
            }

            if (null != mSubtitleFilePath) {
                try {
                    Log.d(TAG, "internalSetSubtitle - open file");
                    try {
                        mMediaPlayer.openSubtitleFile(mSubtitleFilePath);
                    } catch (UnsupportedOperationException e) {
                        e.printStackTrace();
                    }
                    mMediaPlayer.setSubtitleVisibility(true);
                    // clear all previously displayed subtitles
                } catch (IllegalStateException e) {
                    e.printStackTrace();
                } catch (IOException e) {
                    Log.w(TAG, "internalSetSubtitle - incorrect subtitle file");
                }
            } else {
                mMediaPlayer.setSubtitleVisibility(false);
            }
        }
    }

    private void writeSubtitleFileInDb(String filePath) {
        MediaMetaInfoDB database = MediaMetaInfoDB.getInstance(getApplicationContext());

        Log.d(TAG, "writeSubtitleFileInDb: " + filePath);
        database.setAssociateSubtitleFileForMedia(mCurrentMediaId, filePath);
    }

    private void writeSubtitleTrackInDb(String track, int type) {
        MediaMetaInfoDB database = MediaMetaInfoDB.getInstance(getApplicationContext());
        Log.d(TAG, "writeSubtitleTrackInDb: " + track + ", " + type);
        database.setAssociatedSubtitleTrackForMedia(mCurrentMediaId, track, type);
    }

    /**
     * Display an alert popup window to select a new subtitle file
     * Pressing back cancels the alert.
     */
    private void displaySubtitleListAlert() {
        MediaTypeMapper mediaMapper = new MediaTypeMapper(LocalMediasFinder.getInstance(getApplicationContext()));
        if (null == mSubtitleFilesList) {
            // allocate and fill subtitles list
            mSubtitleFilesList = mediaMapper.getMappedMedias().get(MediaTypeMapper.SUBTITLE_FILES_MAP_KEY);

            // create an array adapter containing subtitle display names
            mSubtitlesListAdapter = new SubtitleListAdapter(getApplicationContext(), R.layout.list_basic_item, mSubtitleFilesList);
        } else {
            // check selected subtitle
            checkSubtitleList();
        }
        createAndShowCustomListDialog(SUBTITLE_FILES_LIST_DIALOG, R.string.title_subtitle_list, mSubtitlesListAdapter);
    }

    /**
     * display a popup to choose the subtitle language
     */
    private void displaySubtitleTrackPopup() {

        final CharSequence[] langItems = getAvailableSubtitleTracks(VOSubtitle.Type_SUB_FILE);

        if (null == langItems) {
            return;
        }
        if (langItems.length < 1) {
            return;
        }

        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        builder.setTitle(R.string.title_subtitle_lang);
        builder.setCancelable(true);
        builder.setItems(langItems, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
                dialog.dismiss();
                setSubtitleTrack((String) langItems[item], VOSubtitle.Type_SUB_FILE);
            }
        });
        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
            public void onCancel(DialogInterface dialog) {
                dialog.dismiss();
            }
        });

        AlertDialog alert = builder.create();
        alert.show();
    }

    /**
     * check list of subtitle to detect selected one
     */
    private void checkSubtitleList() {

        int selected = -1;
        int i = 0;
        for (MediaItem item : mSubtitleFilesList) {
            boolean isSelected = item.getMediaUri().equalsIgnoreCase(mSubtitleFilePath);
            if (isSelected) {
                selected = i;
                break;
            }
            i++;
        }
        if (selected >= 0) {
            mSubtitlesListAdapter.setSelectedItem(selected);
        }
    }

    private String[] getAvailableSubtitleTracks(int subtitleType) {
        String[] subtitleTracks = null;
        if (null != mMediaPlayer && VOSubtitle.Type_Unknown != subtitleType) {
            VOSubtitleTrack[] allTracks = mMediaPlayer.getSubtitleTracks();
            if (null != allTracks) {
                int nbSubFileTracks = 0;
                for (int i = 0; i < allTracks.length; i++) {
                    if (subtitleType == allTracks[i].getSubtitleType()) {
                        nbSubFileTracks++;
                    }
                }
                if (0 < nbSubFileTracks) {
                    int index = 0;
                    subtitleTracks = new String[nbSubFileTracks];
                    for (int i = 0; i < allTracks.length; i++) {
                        if (subtitleType == allTracks[i].getSubtitleType()) {
                            subtitleTracks[index++] = allTracks[i].getName();
                        }
                    }
                }
            }
        }
        return subtitleTracks;
    }

    /**
     * display a popup to choose the subtitle language
     *
     * @param subtitleType type of the subtitle
     */
    private void displaySubtracksPopup(int subtitleType) {

        final CharSequence[] tracknameItems = getAvailableSubtitleTracks(subtitleType);
        if (null == tracknameItems) {
            return;
        }
        if (tracknameItems.length < 1) {
            return;
        }

        final int subtype = subtitleType;
        AlertDialog.Builder builder = new AlertDialog.Builder(this);
        if (VOSubtitle.Type_WEBVTT == subtitleType) {
            builder.setTitle(R.string.title_select_webvtt_track);
        } else if (VOSubtitle.Type_TTML == subtitleType) {
            builder.setTitle(R.string.title_select_ttml_track);
        } else if (VOSubtitle.Type_DVB == subtitleType) {
            builder.setTitle(R.string.title_select_dvb_track);
        } else if (VOSubtitle.Type_SMPTE == subtitleType) {
            builder.setTitle(R.string.title_select_smpte_track);
        } else {
            builder.setTitle(R.string.title_select_channel);
        }
        builder.setCancelable(true);
        builder.setItems(tracknameItems, new DialogInterface.OnClickListener() {
            public void onClick(DialogInterface dialog, int item) {
                dialog.dismiss();
                String trackname = (String) tracknameItems[item];
                setSubtitleFile(null);
                setSubtitleTrack(trackname, subtype);
            }
        });
        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
            public void onCancel(DialogInterface dialog) {
                dialog.dismiss();
            }
        });

        AlertDialog alert = builder.create();
        alert.show();
    }

    @Override
    public void onItemClick(QuickAction source, int pos, int actionId) {
        switch (actionId) {

            case ACTION_ID_SELECT_SUBTITLE:
                displaySubtitleListAlert();
                break;

            case ACTION_ID_CLOSE_SUBTITLE:
                setSubtitleFile(null);
                break;

            case ACTION_ID_SELECT_SUBTITLE_TRACK:
                displaySubtitleTrackPopup();
                break;

            case ACTION_ID_SELECT_CC_TRACK:
                displaySubtracksPopup(VOSubtitle.Type_CC_CEA608);
                break;

            case ACTION_ID_SELECT_WEBVTT_TRACK:
                displaySubtracksPopup(VOSubtitle.Type_WEBVTT);
                break;

            case ACTION_ID_SELECT_SMPTE_TRACK:
                displaySubtracksPopup(VOSubtitle.Type_SMPTE);
                break;

            case ACTION_ID_SELECT_TTML_TRACK:
                displaySubtracksPopup(VOSubtitle.Type_TTML);
                break;

            case ACTION_ID_SELECT_DVB_TRACK:
                displaySubtracksPopup(VOSubtitle.Type_DVB);
                break;

            case ACTION_ID_CLOSE_TTML_TRACK:
            case ACTION_ID_CLOSE_WEBVTT_TRACK:
            case ACTION_ID_CLOSE_SMPTE_TRACK:
            case ACTION_ID_CLOSE_CC_TRACK:
            case ACTION_ID_CLOSE_DVB_TRACK:
                setSubtitleFile(null);
                setSubtitleTrack(null, VOSubtitle.Type_Unknown);
                break;

        }
        mQuickAction.dismiss();
    }


    /************************************************************/
    /****************** ID3 related methods *********************/
    /************************************************************/
    private void resetID3Views() {
        mId3TextView.setText(null);
        mId3TextView.setVisibility(View.GONE);
        mId3ImageView.setImageBitmap(null);
        mId3ImageView.setVisibility(View.GONE);
    }

    private final Handler mId3UpdatesHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "id3UpdatesHandler - handleMessage");
            handleID3Metadata();
        }
    };

    private void handleID3Metadata() {
        String ConcatenatedText = null;
        LVID3MetadataParser id3Parser = new LVID3MetadataParser();
        boolean success = id3Parser.parse(mMediaPlayer.getHttpStreamingMetadataId3());
        if (!success) {
            //show an error message in RED if the parsing fails
            //ConcatenatedText = "PARSING ERROR";
            //id3TextView.setTextColor(Color.RED);
            Log.e(TAG, "handleID3Metadata parsing error!!");
            return;
        } else {
            ConcatenatedText = getID3ConcatenatedText(id3Parser);
            mId3TextView.setTextColor(Color.WHITE);
        }

        if (null != ConcatenatedText) {
            if (ConcatenatedText.startsWith("type:dvbs")) {
                /* Display nothing, keep last ID3 APIC frame displayed */
                mId3TextView.setVisibility(View.GONE);
            } else {
                mId3TextView.setText(ConcatenatedText);
                mId3TextView.setVisibility(View.VISIBLE);
            }
        } else {
            mId3TextView.setVisibility(View.GONE);
            mId3ImageView.setImageBitmap(id3Parser.getEmbeddedPicture());
            if (null != id3Parser.getEmbeddedPicture()) {
                mId3ImageView.setVisibility(View.VISIBLE);
            } else {
                mId3ImageView.setVisibility(View.GONE);
            }
        }
    }

    private void toggleId3MetadataRetrieval() {
        if (0 == mId3MetadataBtn.getText().toString().compareTo(getString(R.string.metadata_id3_off))) {
            setId3MetadataRetrieval(true);
        } else {
            setId3MetadataRetrieval(false);
        }
    }

    private void setId3MetadataRetrieval(boolean enabled) {
        if (null != mMediaPlayer) {
            mMediaPlayer.setHttpStreamingMetadataId3Enabled(enabled);
            if (enabled) {
                mId3Layout.setVisibility(View.VISIBLE);
                mId3MetadataBtn.setText(R.string.metadata_id3_on);
            } else {
                mId3Layout.setVisibility(View.GONE);
                mId3TextView.setVisibility(View.GONE);
                mId3ImageView.setVisibility(View.GONE);
                mId3TextView.setText(null);
                mId3ImageView.setImageBitmap(null);
                mId3MetadataBtn.setText(R.string.metadata_id3_off);
            }
            mDisplaySurface.clearAllSubtitles();
        }
    }

    private String getID3ConcatenatedText(LVID3MetadataParser id3Parser) {
        String concatenatedText = null;
        String concatSeparator = "/";

        if (null != id3Parser.getArtist()) {
            concatenatedText = id3Parser.getArtist() + concatSeparator;
        }
        if (null != id3Parser.getTitle()) {
            concatenatedText = (concatenatedText != null ? concatenatedText : "") + id3Parser.getTitle() + concatSeparator;
        }
        if (null != id3Parser.getTextInfo()) {
            concatenatedText = (concatenatedText != null ? concatenatedText : "") + id3Parser.getTextInfo() + concatSeparator;
        }
        if (null != id3Parser.getPrivOwnerIdentifier()) {
            concatenatedText = (concatenatedText != null ? concatenatedText : "") + id3Parser.getPrivOwnerIdentifier() + concatSeparator;
        }
        if (null != id3Parser.getGeobSubtitle()) {
            concatenatedText = (concatenatedText != null ? concatenatedText : "") + id3Parser.getGeobSubtitle() + concatSeparator;
        }
        if (null != concatenatedText) {
            //remove last separator
            concatenatedText = concatenatedText.substring(0, concatenatedText.length() - concatSeparator.length());
        }
        return concatenatedText;
    }

    /************************************************************/
    /********** Multi audio tracks related methods **************/
    /************************************************************/
    private VOAudioTrack[] getAudioTracks() {
        if (null != mMediaPlayer) {
            return mMediaPlayer.getAudioTracks();
        } else
            return null;
    }

    /**
     * Display an alert popup window to select the audio track
     * Pressing back cancels the alert.
     */
    private void displayAudioTracksListAlert() {

        // Retrieve list from player if available
        VOAudioTrack[] audioTracks = getAudioTracks();
        mAudioTrackNameList = null;

        if ((null != audioTracks) && (audioTracks.length > 0)) {

            String[] audioTrackList = new String[audioTracks.length];
            mAudioTrackNameList = new String[audioTracks.length];
            int selectedTrack = 0;
            int trackId = 0;
            for (VOAudioTrack audioTrack : audioTracks) {

                mAudioTrackNameList[trackId] = audioTrack.getName();
                audioTrackList[trackId] = audioTrack.getName();
                Log.d(TAG, "displayAudioTracksListAlert track: " + audioTrackList[trackId]);

                if (null != audioTrack.getLanguage()) {
                    audioTrackList[trackId] += " - " + audioTrack.getLanguage();
                }
                if (audioTrack.IsDefault()) {
                    audioTrackList[trackId] += " *";
                }
                if (audioTrack.IsSelected()) {
                    selectedTrack = trackId;
                }
                trackId++;
            }
            BasicStringAdapter audioTracksListAdapter = new BasicStringAdapter(getApplicationContext(), R.layout.list_basic_item, audioTrackList);

            // Highlight the current audio track
            audioTracksListAdapter.setSelectedItem(selectedTrack);
            createAndShowCustomListDialog(AUDIO_TRACKS_LIST_DIALOG, R.string.title_select_audio, audioTracksListAdapter);
        } else // nothing to do, there is no audio track info
        {
            Toast.makeText(getApplicationContext(), "No audio track option", Toast.LENGTH_SHORT).show();
        }
    }

    private void setAudioTrack(int trackId) {
        Log.d(TAG, "setAudioTrack: " + trackId);
        if ((null != mAudioTrackNameList) && (0 < mAudioTrackNameList.length)) {
            setAudioTrack(mAudioTrackNameList[trackId]);
        } else {
            Toast.makeText(getApplicationContext(), "Invalid language", Toast.LENGTH_SHORT).show();
        }
    }

    private void setAudioTrack(String trackName) {
        Log.d(TAG, "setAudioTrack: " + trackName);
        if ((null == mCurrentAudioTrackName) || (!mCurrentAudioTrackName.equals(trackName))) {
            mCurrentAudioTrackName = trackName;
            if (null != mMediaPlayer) {
                try {
                    mMediaPlayer.setAudioTrack(trackName);

                    MediaMetaInfoDB database = MediaMetaInfoDB.getInstance(getApplicationContext());
                    database.setAssociatedAudioTrackForMedia(mCurrentMediaId, mCurrentAudioTrackName);
                } catch (UnsupportedOperationException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /************************************************************/
    /******************** DRM related methods *******************/
    /************************************************************/
    private void handleDRMInfoEvent(int extra) {

        if (VOPlayer.MEDIA_INFO_DRM_PERSONALIZATION_BEGIN == extra) {
            setNotification(true, "Personalization ongoing...");
        } else if (VOPlayer.MEDIA_INFO_DRM_PERSONALIZATION_END == extra) {
            setNotification(true, "Opening");
        } else if (VOPlayer.MEDIA_INFO_DRM_RIGHTS_ACQUISITION_BEGIN == extra) {
            setNotification(true, "License acquisition ongoing...");
        } else if (VOPlayer.MEDIA_INFO_DRM_RIGHTS_ACQUISITION_END == extra) {
            setNotification(true, "Opening");
        } else {
            Log.w(TAG, "handleDRMInfoEvent - unknown extra value: " + extra);
        }
    }

    private void setDRMType() {
        switch (mDrmType) {
            case SECURED_ABR_FLAG:
                mDrmUIImg.setImageResource(R.drawable.secured_httpstr_flag);
                break;
            case DRM_UNLOCKED_FLAG:
                mDrmUIImg.setImageResource(R.drawable.lock_open_flag);
                break;
            case DRM_LOCKED_FLAG:
                mDrmUIImg.setImageResource(R.drawable.lock_closed_flag);
                break;
        }
        if (NO_DRM_FLAG == mDrmType) {
            mDrmUIImg.setVisibility(View.GONE);
            mDrmSeparatorUIImg.setVisibility(View.GONE);
        } else {
            mDrmUIImg.setVisibility(View.VISIBLE);
            mDrmSeparatorUIImg.setVisibility(View.VISIBLE);
        }
    }


    private void setMarlinDecryptorEnabled(boolean bEnabled) {
        mbMarlinProtected = bEnabled;
        if (null != mMediaPlayer) {
            try {
                mMediaPlayer.getAgentMarlin().setDecryptor(bEnabled, mMediaUri.toString().contains("content-access"));
            } catch (UnsupportedOperationException e) {
                Log.v(TAG, "Marlin not supported");
            }
        }
    }

    /************************************************************/
    /******************** VAST related methods *******************/
    /***********************************************************/
    private void handleVastInfoEvent(int extra) {
        if (VOPlayer.MEDIA_INFO_VAST_ADVERTISEMENT == extra) {
            // Disabled seekBar if setAdSeeTo is not allowed
            if (!mbSetAdSeeToAllowed) {
                mSeekbar.setSecondaryProgress(0);
                mSeekbar.setEnabled(false);
            }
            // Disabled Scrubbing during advertisement playback and save state to
            // recover it when advertisement playback finishes
            mbIsScrubbingAvailableCache = mbIsScrubbingAvailable;
            mbIsScrubbingAvailable = false;

            Toast.makeText(getApplicationContext(), getText(R.string.ad_playing_advertisment), Toast.LENGTH_SHORT).show();
        } else if (VOPlayer.MEDIA_INFO_VAST_NOT_ADVERTISEMENT == extra) {
            mSkipAdBtn.setEnabled(false);
            mSkipAdBtn.setVisibility(View.GONE);

            // Restore Scrubbing configuration
            mbIsScrubbingAvailable = mbIsScrubbingAvailableCache;

            mSeekbar.setSecondaryProgress(0);
            mSeekbar.setEnabled(true);
            Toast.makeText(getApplicationContext(), getText(R.string.ad_playing_primary_content), Toast.LENGTH_SHORT).show();
        }
    }

    private void handleVastSkippableEvent(int extra) {
        Log.i(TAG, "Ad skippable in " + extra + "second(s).");

        mSkipAdBtn.setVisibility(View.VISIBLE);

        if (extra == 0) {
            // Display the skip button
            mSkipAdBtn.setEnabled(true);
            mSkipAdBtn.setText(null);
        } else {
            // The advertisement can be skipped after extra s
            // Display a counter until this limit
            mSkipAdBtn.setEnabled(false);
            String messageSkipable = "You can skip ad\n in " + extra + " seconds";
            mSkipAdBtn.setText(messageSkipable);
        }
    }

    /************************************************************/
    /******************* Seek bar callbacks *********************/
    /************************************************************/
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
        if (fromUser) {
            if (isAndroidTV) {
                if (!seekingInAndroidTV) {
                    seekingInAndroidTV = true;
                    Log.e(TAG, "first progress found, pause player");
                    internalPause();
                    cancelProgressTimer();
                }
            }
            if (null != mElapsedTimeTxt) {
                if (StreamType.HTTPSTR_LIVE == mMediaType) {
                    mElapsedTimeTxt.setText("-" + VOUtils.convertTimeToText(mSeekableRangeLength - progress));
                } else {
                    mElapsedTimeTxt.setText(VOUtils.convertTimeToText(progress));
                }
            }
            if ((mJumpAllowedRangeStartTime > 0 && progress < mJumpAllowedRangeStartTime)
                    || (mJumpAllowedRangeEndTime > 0 && progress > mJumpAllowedRangeEndTime)) {
                Log.w(TAG, "Trying to jump outside authorized range");
            } else {
                seekTo(progress, false);
            }
        }
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
        Log.d(TAG, "onStartTrackingTouch");
        mbBufferingIndicationEnabled = false;
        if (null != mAutoHideTask) {
            mAutoHideTask.cancel();
            mAutoHideTask = null;
        }
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
        applySeekToAtEndOfSeekAction();
    }

    private void applySeekToAtEndOfSeekAction() {
        int progress = mSeekbar.getProgress();
        Log.d(TAG, "internalStopTracking: " + progress);
        mbBufferingIndicationEnabled = true;
        startAutoHideTimer();
        if ((mJumpAllowedRangeStartTime > 0 && progress < mJumpAllowedRangeStartTime)
                || (mJumpAllowedRangeEndTime > 0 && progress > mJumpAllowedRangeEndTime)) {
            Log.w(TAG, "Trying to jump outside authorized range");
        } else {
            seekTo(progress, true);
        }
    }

    /************************************************************/
    /************** Seek bar related methods ********************/
    /************************************************************/
    /**
     * Progress bar update count down class.
     * This timer is used to regularly update the playback progression.
     */
    private class ProgressUpdateCDT extends CountDownTimer {
        public ProgressUpdateCDT(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onFinish() {
            //Force UI Completion
            internalUpdateProgress();
            updateMediaControls();
        }

        @Override
        public void onTick(long millisUntilFinished) {
            internalUpdateProgress();
        }
    }

    private class ABRGraphUpdateTimer extends CountDownTimer {
        public ABRGraphUpdateTimer(long msInFuture, long cdi) {
            super(msInFuture, cdi);
        }

        @Override
        public void onTick(long l) {
            updateABRGraph();
        }

        @Override
        public void onFinish() {
            updateABRGraph();
        }
    }

    /**
     * Internal method: update progression of the playback on the associated controller interface if any
     */
    private void internalUpdateProgress() {
        if (null != mMediaPlayer) {
            int currentPosition = mMediaPlayer.getCurrentPosition();
            if ((StreamType.HTTPSTR_LIVE == mMediaType) && mLiveSeekingEnabled) {
                setPlaybackCursorPosition(currentPosition);
                setLiveBtnVisible(true);
                setPlaybackTime(currentPosition - mSeekableRangeLength);
            } else {
                setPlaybackCursorPosition(currentPosition);
                setLiveBtnVisible(false);
                setPlaybackTime(currentPosition);
            }
        }
    }

    private void setMaxPlaybackCursorProgressValue(int maxCursorValue) {
        mSeekbar.setMax(maxCursorValue);
    }

    private void setProgressBarDrawable(Drawable d) {
        mSeekbar.setProgressDrawable(d);
    }

    private void setSeekBarVisibility(boolean visible) {
        if (visible) {
            mSeekbar.setVisibility(View.VISIBLE);
        } else {
            mSeekbar.setVisibility(View.GONE);
        }
    }

    private void startABRGraphUpdateTimer() {
        if (null == abrGraphUpdateTimer) {
            abrGraphUpdateTimer = new ABRGraphUpdateTimer(Long.MAX_VALUE, 2000);
            abrGraphUpdateTimer.start();
        }
    }

    private void cancelABRGraphUpdateTimer() {
        if (null != abrGraphUpdateTimer) {
            abrGraphUpdateTimer.onFinish();
            abrGraphUpdateTimer.cancel();
            abrGraphUpdateTimer = null;
        }
    }


    private void startProgressTimer() {
        if (null == mProgressUpdateTimer) {
            mProgressUpdateTimer = new ProgressUpdateCDT(Long.MAX_VALUE, PROGRESS_UPDATE_TIMER_PERIOD);
            mProgressUpdateTimer.start();
        }
    }

    private void cancelProgressTimer() {
        if (null != mProgressUpdateTimer) {
            mProgressUpdateTimer.onFinish();
            mProgressUpdateTimer.cancel();
            mProgressUpdateTimer = null;
        }
    }

    private void seekTo(int pos, boolean lastStep) {
        resetID3Views();
        Log.d(TAG, "seekTo " + pos + ", " + lastStep);
        if (null != mMediaPlayer && mCanSeek) {
            mLastSeekStep = lastStep;
            boolean doPlayerSeekTo = false;
            if (mbIsScrubbingAvailable) {
                if (lastStep) {
                    mFirstSeek = true; // the next seek will be a first step
                    mThOverlay.dismiss(); // dismiss the thumbnail overlay
                    if (mPlayingBeforeSeek) { // resume playback if we were playing before scrubbing
                        internalResume();
                    }
                    doPlayerSeekTo = true; // actually call the seekTo on the player
                }
                // if this is our first seek step, pause the player to avoid in-between callback to onInfo with "MEDIA_INFO_FIRST_FRAME_DISPLAYED" during the seeks...
                else if (mFirstSeek) {
                    mPlayingBeforeSeek = mMediaPlayer.isPlaying();
                    mFirstSeek = false;
                    internalPause();
                } else {
                    // here we are in a scrubbing mode
                    int duration = mMediaPlayer.getDuration();
                    Bitmap frameBmp = mMediaPlayer.getFrameAt(pos); // get thumbail for the current position
                    mThOverlay.setThumbnailBitmap(frameBmp);
                    mThOverlay.show(mSeekbar, (int)((double)pos/(double)duration*100), 50);

                    if (frameBmp != null) {
                        mThumbnailImageViewOverlay.setImageBitmap(frameBmp);
                        mThumbnailImageViewOverlay.setVisibility(View.VISIBLE);
                    }
                }
            } else {
                doPlayerSeekTo = true;
            }

            if (doPlayerSeekTo) {
                mMediaPlayer.seekTo(pos);
            }
        }
    }

    /************************************************************/
    /******* Audio Booster related methods **********************/
    /************************************************************/
    private void activateAudioBooster(boolean bEnabled, boolean bForceUpdate) {
        if (null != mMediaPlayer) {
            if (bForceUpdate || bEnabled != mbAudioBoosterEnabled) {
                int audioBoosterMode = VOPlayer.AUDIO_BOOSTER_OFF;
                if (bEnabled) {
                    audioBoosterMode = VOPlayer.AUDIO_BOOSTER_EXTREME;
                    DrawableCompat.setTint(mAudioBoosterBtn.getBackground().mutate(), getResources().getColor(R.color.green));
                } else {
                    audioBoosterMode = VOPlayer.AUDIO_BOOSTER_OFF;
                    DrawableCompat.setTint(mAudioBoosterBtn.getBackground().mutate(), getResources().getColor(R.color.red));
                }
                mbAudioBoosterEnabled = bEnabled;
                mMediaPlayer.setAudioBoosterMode(audioBoosterMode);
            }
        }
    }

    private void toggleAudioBooster() {
        if (mbAudioBoosterEnabled) {
            activateAudioBooster(false, false);
        } else {
            activateAudioBooster(true, false);
        }
    }

    /************************************************************/
    /******* VideoExperience/VideoBooster related methods ********/
    /************************************************************/
    private void setVEBoosttUsingSlider() {
        mSettingVEBoost = true;
        mSliderPopup.setSliderMinValue(0);
        mSliderPopup.setSliderMaxValue(100);
        mSliderPopup.setSliderText("Boost: ");
        mSliderPopup.setSliderStepSize(2);
        mSliderPopup.setSliderBtn(false, "");
        mSliderPopup.show(mVEBoostBtn, mVEBoost);
    }

    private void updateVideExperienceBtn() {
        if (!mbIsVideoExperienceAllowed) {
            mVEBoostBtn.setVisibility(View.GONE);
            mVideoBoosterBtn.setVisibility(View.GONE);
            mVideoBoosterText.setVisibility(View.GONE);
        } else {
            mVEBoostBtn.setVisibility(View.VISIBLE);
            mVideoBoosterBtn.setVisibility(View.VISIBLE);
            mVideoBoosterText.setVisibility(View.VISIBLE);
            // set the value to 40
            setVEBoost(mVEBoost);
        }
    }

    private void switchToNextVideoExperienceMode() {
        int buttonTextArrayId = 0;
        int nextMode = 0;
        switch (mCurrentVideoExperienceMode) {
            case VOPlayer.VIDEO_EXPERIENCE_OFF:
                nextMode = VOPlayer.VIDEO_EXPERIENCE_HD;
                buttonTextArrayId = 1;
                break;
            case VOPlayer.VIDEO_EXPERIENCE_HD:
                nextMode = VOPlayer.VIDEO_EXPERIENCE_SIDE_BY_SIDE;
                buttonTextArrayId = 2;
                break;
            case VOPlayer.VIDEO_EXPERIENCE_SIDE_BY_SIDE:
                nextMode = VOPlayer.VIDEO_EXPERIENCE_OFF;
                buttonTextArrayId = 0;
                break;
        }

        /* compute boost value to set to VEHD lib */
        boolean switched = setVideoExperienceConfig(nextMode, mVEBoost);
        if (switched) {
            mCurrentVideoExperienceMode = nextMode;
            String HDExperienceStrings[] = getResources().getStringArray(R.array.hd_experience_choice);
            mVideoBoosterText.setText(HDExperienceStrings[buttonTextArrayId]);
            if (VOPlayer.VIDEO_EXPERIENCE_OFF == mCurrentVideoExperienceMode) {
                mVideoBoosterText.setBackgroundColor(getResources().getColor(R.color.red));
            } else {
                mVideoBoosterText.setBackgroundColor(getResources().getColor(R.color.green));
            }
        }
    }

    private void setVEBoost(int newValue) {
        Log.v(TAG, "change VE Boost: new value " + newValue);
        mVEBoost = newValue;
        mVEBoostBtn.setText("Video Boost:\n" + mVEBoost + "/100");
    }

    private boolean canVideoExperienceBeEnabled() {
        if (null != mMediaPlayer) {
            try {
                Log.v(TAG, "canVideoExperienceBeEnabled");
                return mMediaPlayer.canVideoExperienceBeEnabled();
            } catch (UnsupportedOperationException e) {
                return false;
            } catch (IllegalStateException e) {
                return false;
            }
        } else {
            return false;
        }
    }

    private boolean setVideoExperienceConfig(int mode, int value) throws UnsupportedOperationException {
        if (null != mMediaPlayer) {
            try {
                //Automatic settings with a boost factor
                Log.v(TAG, "setVideoExperienceConfig: + " + mode + ", value=" + value);
                mMediaPlayer.setVideoExperienceConfig(mode, value);
                mVideoExperienceEffectValue = value;
                mVideoExperienceEffectMode = mode;
            } catch (UnsupportedOperationException e) {
                Log.w(TAG, "setVideoExperienceConfig - setVideoExperienceConfig not supported");
                return false;
            }
            return true;
        } else {
            return false;
        }
    }

    private final Handler mVideoExpHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            Log.d(TAG, "videoExpHandler - handleMessage");
            if (null != mMediaPlayer) {
                try {
                    // Use automatic settings
                    mMediaPlayer.setVideoExperienceConfig(mVideoExperienceEffectMode, mVideoExperienceEffectValue);
                    Log.v(TAG, "setVideoExperienceConfig: + " + mVideoExperienceEffectMode + ", value=" + mVideoExperienceEffectValue);
                } catch (UnsupportedOperationException e) {
                }
            }
        }
    };

    /************************************************************/
    /******************* VODownloader Methods **********************/
    /************************************************************/
    @Override
    public boolean onInfo(DownloaderWrapper downloader, int what, int extra) {

        String uri = downloader.getUri();
        // in this listener, we are only interested in info about the stream currently in playback
        // other streams will not be considered
        if (uri.equalsIgnoreCase(mDlOriginalUri)) {

            switch (what) {
                case DownloadManager.DOWNLOADER_INFO_DOWNLOAD_COMPLETED:
                    mSeekbar.setSecondaryProgress(downloader.getDuration());
                    setJumpRestrictionBoundaries(-1, -1);
                    mDownloadedDuration = -1;
                    startPlaybackIfDownloadTriggerTimeIsReached();
                    Toast.makeText(getApplicationContext(), "VODownloader, download finished", Toast.LENGTH_SHORT).show();
                    break;

                case DownloadManager.DOWNLOADER_INFO_DOWNLOAD_PROGRESS:
                    mDownloadedDuration = downloader.getDownloadedDuration();
                    mSeekbar.setSecondaryProgress((int) mDownloadedDuration);
                    setJumpRestrictionBoundaries(0, (int) mDownloadedDuration);
                    if (!mDownloadPlaybackStarted) {
                        setNotification(true, "Downloading " + (mDownloadedDuration * 100 / downloader.getDuration()) + "%");
                        startPlaybackIfDownloadTriggerTimeIsReached();
                    }
                    break;

                case DownloadManager.DOWNLOADER_INFO_DOWNLOAD_SPEED:
                    // in this case, extra is the input bandwidth
                    mAbrStreamingCurrentThroughPutBR.setText("Cur: " + extra / 1000 + " kbps");
                    if (!mDownloadPlaybackStarted && mUseSmartDelayForDownload && extra > 0) {
                        //compute the duration to download for a smooth playback without rebuffering
                        int streamBitrate = downloader.getCurrentBitrate();
                        int streamDuration = downloader.getDuration();
                        if (extra > streamBitrate) {
                            mDlTimeTriggerForPlaybackStart = 40000; //You wonder why? Because HLD stack need at least 30s of existing chunk if server ...
                        } else if (streamBitrate > 0) {
                            mDlTimeTriggerForPlaybackStart = (((streamBitrate - extra) / 1000) * streamDuration) / (streamBitrate / 1000);
                        }
                        mDlTimeTriggerForPlaybackStart += mResumePlaybackPosition + HTTPSTR_DOWNLOAD_DATA_DOWNLOADED_DELTA;
                        Log.d(TAG, "Download speed update > new start trigger time (ms): " + mDlTimeTriggerForPlaybackStart);
                    }
                    break;

                case DownloaderWrapper.DOWNLOADER_INFO_OFFLINE_NOT_ALLOWED:
                    Toast.makeText(getApplicationContext(), "Offline viewing not allowed on this stream", Toast.LENGTH_SHORT).show();
                    break;

                case DownloaderWrapper.DOWNLOADER_INFO_OFFLINE_KEYS_RENEWAL_STARTED:
                    setNotification(true, getString(R.string.dl_renewing_keys));
                    break;

                case DownloaderWrapper.DOWNLOADER_INFO_OFFLINE_KEYS_RENEWAL_COMPLETED:
                    if (null == mMediaPlayer && downloader.getState() == DOWNLOADING) {
                        setNotification(true, "Downloading " + (downloader.getDownloadedDuration() * 100 / downloader.getDuration()) + "%");
                    } else {
                        setNotification(false, "");
                    }
                    break;
            }
        }
        return false;
    }

    private void startPlaybackIfDownloadTriggerTimeIsReached() {
        if (!mDownloadPlaybackStarted) {
            // -1 as downloaded duration means the stream is completely downloaded, so we can start playback
            if ((mDownloadedDuration < 0)
                    || ((mDlTimeTriggerForPlaybackStart >= 0) && (mDlTimeTriggerForPlaybackStart <= mDownloadedDuration))) {
                preparePlayback();
                mDownloadPlaybackStarted = true;
            }
        }
    }

    private boolean checkForDownloadUseCaseBeforePlayback() {
        if (null != mDlOriginalUri) {

            mAbrStreamingCurrentThroughPutBR.setTextColor(getResources().getColor(R.color.download_speed));
            mAbrStreamingVariantBRText.setTextColor(getResources().getColor(R.color.download_speed));

            DownloadManager dlManager = DownloadManager.getInstance(getApplicationContext());
            DownloaderWrapper Downloader = dlManager.getDownloaderForMediaId(mCurrentMediaId);
            // if there is not downloader existing for the current uri,
            // the stream is currently not downloading, so we use the database info instead
            if (null == Downloader) {
                MediaMetaInfoDB db = MediaMetaInfoDB.getInstance(getApplicationContext());
                DatabaseInfo dbInfo = db.getAssociatedDataForMedia(mCurrentMediaId);

                mDownloadedDuration = dbInfo.DLProgress;
                if (dbInfo.DlStatus != DatabaseInfo.DL_STATUS_FULLY_LOCAL) {

                    setJumpRestrictionBoundaries(-1, (int) mDownloadedDuration);
                    mDownloadedMediaDuration = dbInfo.mediaDurationInMs;
                    setMediaDuration(dbInfo.mediaDurationInMs);
                    mSeekbar.setSecondaryProgress((int) mDownloadedDuration);
                } else {
                    Drawable backgroundSeekBar = mSeekbar.getBackground();
                    if (null != backgroundSeekBar) {
                        DrawableCompat.setTint(backgroundSeekBar.mutate(), Color.WHITE);
                    }
                    mDownloadedMediaDuration = -1;
                    mSeekbar.setSecondaryProgress(0);
                }

                mAbrStreamingCurrentThroughPutBR.setText("Cur: - kbps");
                mAbrStreamingVariantBRText.setText("Alt: " + dbInfo.DLBitrate / 1000 + " kbps");

                // the stream is currently not downloading, so there is no need for smart delay
                mUseSmartDelayForDownload = false;
                mDlTimeTriggerForPlaybackStart = 0;

                // since we are not downloading we cannot reach the resume position (if any) if it is
                // outside of what has already been downloaded, so we discard it and start from 0
                if ((dbInfo.DlStatus != DatabaseInfo.DL_STATUS_FULLY_LOCAL) && (mResumePlaybackPosition >= mDownloadedDuration)) {
                    Toast.makeText(getApplicationContext(), "Resume playback position not reachable.\nStart from beginning", Toast.LENGTH_SHORT).show();
                    mResumePlaybackPosition = 0;
                }
                return true;
            } else {
                int streamDuration;
                try {
                    streamDuration = Downloader.getDuration();
                } catch (IllegalStateException ise) {
                    streamDuration = 0;
                }
                if (!mUseSmartDelayForDownload) {
                    mDlTimeTriggerForPlaybackStart = (long) (mResumePlaybackPosition + mStartDelayForDownload);
                    Log.d(TAG, "checkForDownloadUseCaseBeforePlayback - no smart delay > trigger start time = " + mDlTimeTriggerForPlaybackStart);
                } else {
                    mDlTimeTriggerForPlaybackStart = streamDuration;
                    Log.d(TAG, "checkForDownloadUseCaseBeforePlayback - use smart delay");
                }

                boolean dataConnectionAvailable = isDataConnectionAvailable();
                if (!dataConnectionAvailable) {
                    Log.d(TAG, "checkForDownloadUseCaseBeforePlayback - no data connection");
                    // Not use smart delay
                    mDlTimeTriggerForPlaybackStart = 0;
                }

                dlManager.registerOnInfoListener(this);

                switch (Downloader.getState()) {
                    case DOWNLOADING:
                        setNotification(true, "Downloading");
                        mAbrStreamingVariantBRText.setText("Alt: " + Downloader.getCurrentBitrate() / 1000 + " kpbs");
                        setMediaDuration(streamDuration);
                        mDownloadedMediaDuration = streamDuration;
                        mSeekbar.setSecondaryProgress(Downloader.getDownloadedDuration());
                        setJumpRestrictionBoundaries(0, Downloader.getDownloadedDuration());
                        break;
                    default:
                        Log.w(TAG, "checkForDownloadUseCaseBeforePlayback - Here we have a problem...");
                        break;
                }
            }
        }
        return false;
    }

    private void checkAdClickThrough() {
        if (null != mMediaPlayer) {
            String clickThroughUrl = mMediaPlayer.vastClickThrough();
            if (clickThroughUrl.length() > 0) {
                Toast.makeText(getApplicationContext(), "Ad click trough URL:\n" + clickThroughUrl, Toast.LENGTH_SHORT).show();
            }
        }
    }

    /************************************************************/
    /*********** Side Panel visibility handling *****************/
    /************************************************************/
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        toggleSidePanelVisibility();
        return false;
    }

    private void toggleSidePanelVisibility() {
        Log.i(TAG, "toggleSidePanelVisibility");
        if (View.VISIBLE == mSidePanel.getVisibility()) {
            mSidePanel.setVisibility(View.GONE);
            if (null != mMediaPlayer) {
                mMediaPlayer.signalFullScreen(true);
            }
        } else {
            mSidePanel.setVisibility(View.VISIBLE);
            if (null != mMediaPlayer) {
                mMediaPlayer.signalFullScreen(false);
            }
        }
    }

    /************************************************************/
    /************* Controls visibility handling *****************/
    /************************************************************/
    private void toggleControlsVisibility() {
        Log.d(TAG, "toggleControlsVisibility - " + mControlsLayout);
        if (mControlsLayout.getTag() == ControlsState.UI_READY_TO_SHOW) {
            Log.d(TAG, "showHideControls - start show");
            mControlsLayout.startAnimation(mFadeInAnimation);
            if (null != mDisplaySurface) {
                mDisplaySurface.setUIOnTop(true);
            }
        } else if (mControlsLayout.getTag() == ControlsState.UI_READY_TO_HIDE) {
            Log.d(TAG, "showHideControls - start hide");
            mControlsLayout.startAnimation(mFadeOutAnimation);
            if (null != mDisplaySurface) {
                mDisplaySurface.setUIOnTop(false);
            }
        }
    }

    /**
     * Hide controls after a delay.
     */
    private void startAutoHideTimer() {
        // on Android TV, the UI does not hide automatically for better user experience
        if (!isAndroidTV) {
            if (null != mAutoHideTask) {
                mAutoHideTask.cancel();
                mAutoHideTask = null;
            }
            mAutoHideTimer.purge();
            mAutoHideTask = new AutoHideControlsTask();
            mAutoHideTimer.schedule(mAutoHideTask, AUTO_HIDE_DELAY);
        }
    }

    private class AutoHideControlsTask extends TimerTask {
        @Override
        public void run() {
            if (null != mAsyncHandler) {
                mAsyncHandler.post(new AutoHideRunnable());
            }
        }
    }

    /**
     * Runnable task helper for controls hiding.
     * This makes sure the animations are triggered from the main UI thread.
     */
    private class AutoHideRunnable implements Runnable {
        @Override
        public void run() {
            if (mControlsLayout.getTag() == ControlsState.UI_READY_TO_HIDE) {
                Log.d(TAG, "showHideControls - start hide");
                mControlsLayout.startAnimation(mFadeOutAnimation);
                if (null != mDisplaySurface) {
                    mDisplaySurface.setUIOnTop(false);
                }
            }
            if (null != mAutoHideTask) {
                mAutoHideTask.cancel();
                mAutoHideTask = null;
            }
        }
    }

    @Override
    public void onAnimationStart(Animation animation) {
        if (animation == mFadeInAnimation) {
            mControlsLayout.setVisibility(View.VISIBLE);
            mControlsLayout.setTag(ControlsState.UI_SHOWING);
        } else if (animation == mFadeOutAnimation) {
            mControlsLayout.setTag(ControlsState.UI_HIDING);
        }
    }

    @Override
    public void onAnimationEnd(Animation animation) {
        if (animation == mFadeInAnimation) {
            mControlsLayout.setTag(ControlsState.UI_READY_TO_HIDE);
        } else if (animation == mFadeOutAnimation) {
            mControlsLayout.setVisibility(View.INVISIBLE);
            mControlsLayout.setTag(ControlsState.UI_READY_TO_SHOW);
        }
    }

    @Override
    public void onAnimationRepeat(Animation animation) {
    }

    /*************************************************************/
    /*************** Phone call related elements******************/
    /*************************************************************/
    private BroadcastReceiver mPhoneCallInfoReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent intent) {
            TelephonyManager tm = (TelephonyManager) getApplicationContext().getSystemService(Service.TELEPHONY_SERVICE);
            switch (tm.getCallState()) {
                case TelephonyManager.CALL_STATE_RINGING:
                    if (null != mMediaPlayer) {
                        //YOUBORA_ANALYTICS: mYouboraPlugin.stopMonitoring();
                        //CONVIVA_ANALYTICS: mConvivaPlugin.cleanupSession();
                        //AGAMA_ANALYTICS: LVAgamaPlugin.getInstance().stopPlaybackSession();
                        mbPreparedCBReceived = false;
                        mMediaPlayer.reset();
                    }
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    break;
                case TelephonyManager.CALL_STATE_IDLE:
                    preparePlayback();
                    break;
                default:
                    break;
            }
        }
    };

    /*************************************************************/
    /************* Battery level related elements*****************/
    /*************************************************************/
    private BroadcastReceiver mBatteryInfoReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent intent) {
            int batteryLevel = intent.getIntExtra("level", 0);
            mBatteryUIText.setText(batteryLevel + "%");
        }
    };

    /*************************************************************/
    /*********** Connectivity related elements *******************/
    /*************************************************************/
    // broadcast receiver for connectivity watching
    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {

            String action = intent.getAction();
            if (action.equals(ConnectivityManager.CONNECTIVITY_ACTION)) {
                updateConnectivityInformation();
                if (null != mDemoMode) {
                    mDemoMode.updateFromEvent(VoDemo.WIFI_TOGGLE_EVENT);
                }
            }
        }
    };

    private void updateConnectivityInformation() {
        ConnectivityManager connectivityMgr = (ConnectivityManager) getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo networksInfo[] = connectivityMgr.getAllNetworkInfo();
        for (NetworkInfo network : networksInfo) {
            switch (network.getType()) {
                case ConnectivityManager.TYPE_WIFI:
                    WifiManager wifiMgr = (WifiManager) getApplicationContext().getSystemService(Context.WIFI_SERVICE);
                    if (network.getState() == NetworkInfo.State.CONNECTED) {
                        WifiInfo wifiInfo = wifiMgr.getConnectionInfo();
                        Log.v(TAG, "WIFI connected");
                        String name = wifiInfo.getSSID();

                        mWifiNetworkText.setText("WIFI SSID: " + name);
                        mWifiToggleBtn.setImageResource(R.drawable.wifi_on);
                    } else {
                        Log.v(TAG, "WIFI disconnected");
                        if (wifiMgr.isWifiEnabled()) {
                            mWifiNetworkText.setText("WIFI SSID: None");
                            mWifiToggleBtn.setImageResource(R.drawable.wifi_on);
                        } else {
                            mWifiNetworkText.setText("WIFI OFF");
                            mWifiToggleBtn.setImageResource(R.drawable.wifi_off);
                        }
                    }
                    break;
                case ConnectivityManager.TYPE_MOBILE:
                    if (network.getState() == NetworkInfo.State.CONNECTED) {
                        Log.v(TAG, "Cellular connected");
                        mCellularNetworkImg.setImageResource(R.drawable.cellular_on);
                    } else {
                        Log.v(TAG, "Cellular disconnected");
                        mCellularNetworkImg.setImageResource(R.drawable.cellular_off);
                    }
                    break;
            }
        }
    }

    public boolean isDataConnectionAvailable() {
        ConnectivityManager connectivityMgr = (ConnectivityManager) getApplicationContext().getSystemService(Context.CONNECTIVITY_SERVICE);
        NetworkInfo[] networks = connectivityMgr.getAllNetworkInfo();
        if (null != networks) {
            for (NetworkInfo network : networks) {
                if (NetworkInfo.State.CONNECTED == network.getState()) {
                    return true;
                }
            }
        }
        return false;
    }

    /************************************************************/
    /********** Private methods for specific demos **************/
    /************************************************************/
    private class HighlightZeroLagBitrateBtn implements Runnable {
        private Button target;

        public HighlightZeroLagBitrateBtn(Button target) {
            this.target = target;
        }

        @Override
        public void run() {
            target.getBackground().setColorFilter(mHighlightBtnColorFilter);
        }
    }

    private void switchNextAlternateZeroLag(boolean up) {
        if (up && mCurrentZeroLagBitrate < mZeroLagBitrates.length - 1) {
            mCurrentZeroLagBitrate++;
            HighlightZeroLagBitrateBtn highlightTask = new HighlightZeroLagBitrateBtn(mAbrStreamingCustomBRUpBtn);
            mAsyncHandler.postDelayed(highlightTask, 3000);
            mAbrStreamingCustomBRDownBtn.setEnabled(true);
            if (mCurrentZeroLagBitrate == mZeroLagBitrates.length - 1) {
                mAbrStreamingCustomBRUpBtn.setEnabled(false);
            }
        } else if (!up && mCurrentZeroLagBitrate > 0) {
            mCurrentZeroLagBitrate--;
            HighlightZeroLagBitrateBtn highlightTask = new HighlightZeroLagBitrateBtn(mAbrStreamingCustomBRDownBtn);
            mAsyncHandler.postDelayed(highlightTask, 3000);
            mAbrStreamingCustomBRUpBtn.setEnabled(true);
            if (mCurrentZeroLagBitrate == 0) {
                mAbrStreamingCustomBRDownBtn.setEnabled(false);
            }
        }
        setHttpBitratesLimits(mZeroLagBitrates[mCurrentZeroLagBitrate] - 50, mZeroLagBitrates[mCurrentZeroLagBitrate] + 50);
    }
}
