0

If we open our app the news fragment is loaded and the news automatically starts to play. We are using Exoplayer for playing the news. Notification is created by Exoplayer via PlayerNotificationManager. The problem is that the play pause button doesn't follow the notification. We have implemented eventbus & it only changes the drawable. So, if we click on pause button on the notification, the button on fragment is seen as paused but when we click on it works as a pause button (instead of working as a play button).

What we intend to do is: If the notification says paused the button on fragment will be paused & then if we click on it the content will start to play.

Codes:

NewsReaderFragment.java

     public static NewsReaderFragment newInstance() {
        return new NewsReaderFragment();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        rootView = inflater.inflate(R.layout.fragment_news_reader, container, false);
        bPlay = rootView.findViewById(R.id.button_play);
        unbinder = ButterKnife.bind(this, rootView);
        NetworkInfoUtility networkInfoUtility = new NetworkInfoUtility();
        boolean net = networkInfoUtility.isNetWorkAvailableNow(getContext());
        if (!net) {
            Toast.makeText(getContext(), CHECKNET, Toast.LENGTH_LONG).show();
        }
        exoHelper = new ExoHelper(getContext(), new Player.EventListener() {
            @Override
            public void onPlayerError(ExoPlaybackException error) {
                Crashlytics.logException(error);
                Toast.makeText(getContext(), rootView.getContext().getString(R.string.server_off), Toast.LENGTH_LONG).show();
                exoHelper.ToggleButton(false);
            }
        }, bPlay, "NewsReader");

        isPlaying = true;
        setButton();
        exoHelper.startExo(rootView.getContext().getString(R.string.bbc_news));

        return rootView;
    }

    private void setButton() {
        bPlay.setOnClickListener(view -> {

            if (!isPlaying) {
                exoHelper.startExo(rootView.getContext().getString(R.string.bbc_news));
            } else {
                exoHelper.stopExo();
            }

            isPlaying = !isPlaying;
        });

    } 

 @Override
    public void onStart() {
        super.onStart();

        EventBus.getDefault().register(this);
    }

    @Override
    public void onStop() {
        
        EventBus.getDefault().unregister(this);
        super.onStop();
    }


    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onEvent(ButtonEvent event)
    {

      exoHelper.ToggleButton(event.isState());

    }

Exophelper.java

    public void stopExo() {
        if (exoPlayer != null) { //if exo is running
            Log.d(TAG, "Stopping exo....");
            exoPlayer.stop();
            //playerNotificationManager.setPlayer(null);
            customPlayerNotificationManager.setPlayer(null);
            exoPlayer.release();
            exoPlayer = null;
            ToggleButton(false);

        } else {
            Log.d(TAG, "Can't stop because No exoplayer is running");
        }
    }

    public void startExo(String newUrl) {
        if (newUrl == null || newUrl.isEmpty()) {
            Log.d(TAG, "startExo: empty url");
            Toast.makeText(context, R.string.server_off, Toast.LENGTH_SHORT).show();
            ToggleButton(false); // show pause button
            return;
        }

        if (exoPlayer != null) {
            Log.d(TAG, "startExo: Exo is already running now");
            return;
        }

        iceURL = newUrl;

        BandwidthMeter bandwidthMeter = new DefaultBandwidthMeter();
        final ExtractorsFactory extractorsFactory = new DefaultExtractorsFactory();

        TrackSelection.Factory trackSelectionFactory =
                new AdaptiveTrackSelection.Factory(bandwidthMeter);
        TrackSelector defaultTrackSelector =
                new DefaultTrackSelector(trackSelectionFactory);
        DefaultBandwidthMeter defaultBandwidthMeter = new DefaultBandwidthMeter();
        DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(
                context,
                Util.getUserAgent(context, context.getApplicationInfo().name),
                defaultBandwidthMeter);

        MediaSource mediaSource = new ExtractorMediaSource(
                Uri.parse(iceURL),
                dataSourceFactory,
                extractorsFactory,
                new Handler(), error -> {
        });

        exoPlayer = ExoPlayerFactory.newSimpleInstance(context, defaultTrackSelector);

        if (eventListener != null) {
            exoPlayer.addListener(eventListener);
        }
        exoPlayer.prepare(mediaSource);
        exoPlayer.setPlayWhenReady(true);

        //EventStatus(true);
          ToggleButton(true);
        //setPlayerNotificationManager(exoPlayer);
        createCustomPlayerNotificationManger(exoPlayer);


    }

public void ToggleButton(boolean state) {
        if (state) {
            Drawable img = button.getContext().getResources().getDrawable(R.drawable.play_button);
            button.setCompoundDrawablesWithIntrinsicBounds(img, null, null, null);
            button.setText(R.string.now_playing);
        } else {
            Drawable img1 = button.getContext().getResources().getDrawable(R.drawable.pause_button);
            button.setCompoundDrawablesWithIntrinsicBounds(img1, null, null, null);
            button.setText(R.string.server_off);
        }
    }

PlayerNotificationManager.java


It's a big file, so we are attaching only the receiver where we use eventbus:

public class NotificationBroadcastReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Player player = PlayerNotificationManager.this.player;
        if (player == null
                || !isNotificationStarted
                || intent.getIntExtra(EXTRA_INSTANCE_ID, instanceId) != instanceId) {
            return;
        }
        String action = intent.getAction();
        if (ACTION_PLAY.equals(action)) {
            if (player.getPlaybackState() == Player.STATE_IDLE) {
                if (playbackPreparer != null) {
                    playbackPreparer.preparePlayback();
                }
            } else if (player.getPlaybackState() == Player.STATE_ENDED) {
                controlDispatcher.dispatchSeekTo(player, player.getCurrentWindowIndex(), C.TIME_UNSET);
            }
            controlDispatcher.dispatchSetPlayWhenReady(player, /* playWhenReady= */ true);

            EventBus.getDefault().post(new ButtonEvent(true));
            Log.d(TAG, "ButtonEvent: True");


        } else if (ACTION_PAUSE.equals(action)) {
            controlDispatcher.dispatchSetPlayWhenReady(player, /* playWhenReady= */ false);

            if (player.getPlaybackState() == Player.STATE_READY)
            {
                EventBus.getDefault().post(new ButtonEvent(false));
                Log.d(TAG, "ButtonEvent: False");
            }


        } else if (ACTION_STOP.equals(action)) {
            controlDispatcher.dispatchStop(player, /* reset= */ true);
        } else if (ACTION_DISMISS.equals(action)) {
            stopNotification(/* dismissedByUser= */ true);
        } else if (action != null
                && customActionReceiver != null
                && customActions.containsKey(action)) {
            customActionReceiver.onCustomAction(player, action, intent);
        }
    }
}

ButtonEvent.java

public class ButtonEvent {

public boolean state;

public ButtonEvent(boolean state) {
    this.state = state;
}

public boolean isState() {
    return state;
}

public void setState(boolean state) {
    this.state = state;
}

}

null_override
  • 467
  • 3
  • 10
  • 30

1 Answers1

1

Is it correct that isPlaying is true in this scenario, executing a second pause?

private void setButton() {
        bPlay.setOnClickListener(view -> {

            if (!isPlaying) {
                exoHelper.startExo(rootView.getContext().getString(R.string.bbc_news));
            } else {
                exoHelper.stopExo();
            }
            isPlaying = !isPlaying;
        });
    } 

Is there a reason why the state of whether it is playing it tied to the Fragment and not ExoHelper?

Maybe this can be remedied easily by re-assigning isPlaying with exoplayers event listener...

player.addListener(eventListener);

...

@Override
public void onIsPlayingChanged(boolean isPlaying) {
  if (isPlaying) {
      // player is playing
  } else {
      // player is paused
  }
}

UPDATE

Or query exoplayer directly for state:

private void setButton() {
        bPlay.setOnClickListener(view -> {
            if (!isPlaying()) {
        exoHelper.startExo(rootView.getContext().getString(R.string.bbc_news));
            } else {
                exoHelper.stopExo();
            }
        });
    } 

// this method might belong in ExoHelper?
private boolean isPlaying() {
    return player.getPlaybackState() == Player.STATE_READY && player.getPlayWhenReady();
}

Further reading https://exoplayer.dev/listening-to-player-events.html

JakeB
  • 2,043
  • 3
  • 12
  • 19
  • thanks for your answer! Yes, you're correct. `isPlaying` is true in this case, executing a second phase. – null_override Nov 16 '20 at 15:44
  • `Is there a reason why the state of whether it is playing it tied to the Fragment and not ExoHelper?` We have another fragment & it has similar codes. That's why we tied the state to fragment. – null_override Nov 16 '20 at 15:45
  • So we should assign `isPlaying` again inside `ExoHelper`? How can we retrieve that state in fragments? I mean get the value? If you can explain some more, then that would really help :) – null_override Nov 16 '20 at 15:49
  • Update the isPlaying variable with a listener, dont do it manually in ```onCreateView``` or ```bPlay.setOnClickListener```. This way exoplayer can tell you when it's playing and when it isnt and you dont need to care about controlling that state yourself. Or call ```player.getPlaybackState()``` instead, it will return the state to you. Added an update to my answer. – JakeB Nov 17 '20 at 00:44