Search code examples
androidexoplayerexoplayer2.ximasdk

Save and restore ad progress in Exoplayer IMA extension


I'm trying to load a VAST ad using Exoplayer IMA extension (using tutorials 1, 2). I want to keep ad and content progress, so if app goes background or screen rotates, user continues from the point he/she was watching. You can see my code here and main codes below for convenience. (Duo to some limitations, I'm stuck with version 2.9.6 of Exoplayer library)

public class MainActivity extends AppCompatActivity {

    private PlayerView playerView;
    private SimpleExoPlayer player;


    private static final String KEY_WINDOW = "window";
    private int currentWindow;
    private static final String KEY_POSITION = "position";
    private long playbackPosition;
    private static final String KEY_AUTO_PLAY = "auto_play";
    private boolean playWhenReady;
    private ImaAdsLoader adsLoader;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        playerView = findViewById(R.id.exo_video_view);
        if (adsLoader == null)
            adsLoader = new ImaAdsLoader(this, Uri.parse(getString(R.string.ad_tag_url)));

        if (savedInstanceState != null) {
            currentWindow = savedInstanceState.getInt(KEY_WINDOW);
            playbackPosition = savedInstanceState.getLong(KEY_POSITION);
            playWhenReady = savedInstanceState.getBoolean(KEY_AUTO_PLAY, true);
        }
    }

    private MediaSource buildMediaSource(Uri uri, DataSource.Factory dataSourceFactory) {
        return new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(uri);
    }

    private MediaSource buildAdMediaSource(MediaSource contentMediaSource, DataSource.Factory dataSourceFactory){
        // Create the AdsMediaSource using the AdsLoader and the MediaSource.
        return new AdsMediaSource(contentMediaSource, dataSourceFactory, adsLoader, playerView);
    }

    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
    }

    private void initializePlayer() {
        player = ExoPlayerFactory.newSimpleInstance(this);
        playerView.setPlayer(player);

        if (adsLoader != null)
            adsLoader.setPlayer(player);

        Uri contentUri = Uri.parse(getString(R.string.media_url_mp4));
        DataSource.Factory dataSourceFactory =
                new DefaultDataSourceFactory(this, "exoplayer-codelab");
        MediaSource contentMediaSource = buildMediaSource(contentUri, dataSourceFactory);
        MediaSource adMediaSource = buildAdMediaSource(contentMediaSource, dataSourceFactory);

        player.setPlayWhenReady(playWhenReady);
        player.seekTo(currentWindow, playbackPosition);
        player.prepare(adMediaSource, false, false);
    }

    @Override
    public void onStart() {
        super.onStart();
        if (Util.SDK_INT >= 24) {
            initializePlayer();
            if (playerView != null) {
                playerView.onResume();
            }
        }
    }

    @Override
    public void onResume() {
        super.onResume();
        hideSystemUi();
        if ((Util.SDK_INT < 24 || player == null)) {
            initializePlayer();
            if (playerView != null) {
                playerView.onResume();
            }
        }
    }

    private void hideSystemUi() {
        playerView.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LOW_PROFILE
                | View.SYSTEM_UI_FLAG_FULLSCREEN
                | View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY
                | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);
    }

    @Override
    public void onPause() {
        super.onPause();
        if (Util.SDK_INT < 24) {
            if (playerView != null) {
                playerView.onPause();
            }
            releasePlayer();
        }
    }

    @Override
    public void onStop() {
        super.onStop();
        if (Util.SDK_INT >= 24) {
            if (playerView != null) {
                playerView.onPause();
            }
            releasePlayer();
        }
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseAdsLoader();
    }

    private void releasePlayer() {
        if (player != null) {
            updateStartPosition();
            player.release();
            player = null;
        }
        if (adsLoader != null) {
            adsLoader.setPlayer(null);
        }
    }

    private void releaseAdsLoader() {
        if (adsLoader != null) {
            adsLoader.release();
            adsLoader = null;
            if (playerView.getOverlayFrameLayout() != null)
                playerView.getOverlayFrameLayout().removeAllViews();
        }
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        updateStartPosition();
        outState.putBoolean(KEY_AUTO_PLAY, playWhenReady);
        outState.putInt(KEY_WINDOW, currentWindow);
        outState.putLong(KEY_POSITION, playbackPosition);
    }

    private void updateStartPosition() {
        if (player != null) {
            playWhenReady = player.getPlayWhenReady();
            currentWindow = player.getCurrentWindowIndex();
            playbackPosition = Math.max(0, player.getContentPosition());
        }
    }
}

Problem is, when screen rotates, activity fields like ImaAdsLoader becomes null and ad and content video play from start. I've saved player progress and can successfully restore it's position but that's not case for ImaAdsLoader since I couldn't find a way to restore its state. Am I doing anything wrong? How Ad progress state should be saved and restored?


Solution

  • A common pattern with video players is to prevent your activity from being recreated with orientation changes so you should add these flags to your manifest.

    android:configChanges="keyboardHidden|orientation|screenSize"
    

    This would also prevent the surface view that shows your video from being destroyed with orientation changes too.