aboutsummaryrefslogblamecommitdiff
path: root/app/src/main/java/ml/docilealligator/infinityforreddit/videoautoplay/ExoPlayerViewHelper.java
blob: 6ba68217ef08aced6df1309394555dabe929e36d (plain) (tree)
























                                                                                       



                                               

















                                                                                                
              












                                                                                                 
                                         





                                                                                              
                                         










                                                                                                    
                                         


                                                                                         
                                                                                            







                                                                        
                                         





                                                                           
                                                                

   
                                         












































                                                                             
                                         




                                                                          
                                         




                                                                      
              


















                                                                                                
/*
 * Copyright (c) 2018 Nam Nguyen, nam@ene.im
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *        http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package ml.docilealligator.infinityforreddit.videoautoplay;

import static ml.docilealligator.infinityforreddit.videoautoplay.ToroExo.with;
import static ml.docilealligator.infinityforreddit.videoautoplay.ToroUtil.checkNotNull;

import android.net.Uri;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.OptIn;
import androidx.media3.common.util.UnstableApi;
import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.ui.PlayerView;

import ml.docilealligator.infinityforreddit.videoautoplay.annotations.RemoveIn;
import ml.docilealligator.infinityforreddit.videoautoplay.helper.ToroPlayerHelper;
import ml.docilealligator.infinityforreddit.videoautoplay.media.PlaybackInfo;
import ml.docilealligator.infinityforreddit.videoautoplay.media.VolumeInfo;
import ml.docilealligator.infinityforreddit.videoautoplay.widget.Container;

/**
 * An implementation of {@link ToroPlayerHelper} where the actual Player is an {@link ExoPlayer}
 * implementation. This is a bridge between ExoPlayer's callback and ToroPlayerHelper behaviors.
 *
 * @author eneim (2018/01/24).
 * @since 3.4.0
 */

public class ExoPlayerViewHelper extends ToroPlayerHelper {

  @NonNull private final ExoPlayable playable;
  @UnstableApi
  @NonNull private final MyEventListeners listeners;
  private final boolean lazyPrepare;

  // Container is no longer required for constructing new instance.
  @SuppressWarnings("unused") @RemoveIn(version = "3.6.0") @Deprecated  //
  public ExoPlayerViewHelper(Container container, @NonNull ToroPlayer player, @NonNull Uri uri) {
    this(player, uri);
  }

  public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri) {
    this(player, uri, null);
  }

  @OptIn(markerClass = UnstableApi.class)
  public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri,
      @Nullable String fileExt) {
    this(player, uri, fileExt, with(player.getPlayerView().getContext()).getDefaultCreator());
  }

  /** Config instance should be kept as global instance. */
  @OptIn(markerClass = UnstableApi.class)
  public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri, @Nullable String fileExt,
      @NonNull Config config) {
    this(player, uri, fileExt,
        with(player.getPlayerView().getContext()).getCreator(checkNotNull(config)));
  }

  public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull Uri uri, @Nullable String fileExt,
      @NonNull ExoCreator creator) {
    this(player, new ExoPlayable(creator, uri, fileExt));
  }

  @OptIn(markerClass = UnstableApi.class)
  public ExoPlayerViewHelper(@NonNull ToroPlayer player, @NonNull ExoPlayable playable) {
    super(player);
    //noinspection ConstantConditions
    if (player.getPlayerView() == null || !(player.getPlayerView() instanceof PlayerView)) {
      throw new IllegalArgumentException("Require non-null PlayerView");
    }

    listeners = new MyEventListeners();
    this.playable = playable;
    this.lazyPrepare = true;
  }

  @OptIn(markerClass = UnstableApi.class)
  @Override protected void initialize(@NonNull PlaybackInfo playbackInfo) {
    playable.setPlaybackInfo(playbackInfo);
    playable.addEventListener(listeners);
    playable.addErrorListener(super.getErrorListeners());
    playable.addOnVolumeChangeListener(super.getVolumeChangeListeners());
    playable.prepare(!lazyPrepare);
    playable.setPlayerView((PlayerView) player.getPlayerView());
  }

  @OptIn(markerClass = UnstableApi.class)
  @Override public void release() {
    super.release();
    playable.setPlayerView(null);
    playable.removeOnVolumeChangeListener(super.getVolumeChangeListeners());
    playable.removeErrorListener(super.getErrorListeners());
    playable.removeEventListener(listeners);
    playable.release();
  }

  @Override public void play() {
    playable.play();
  }

  @Override public void pause() {
    playable.pause();
  }

  @Override public boolean isPlaying() {
    return playable.isPlaying();
  }

  @Override public void setVolume(float volume) {
    playable.setVolume(volume);
  }

  @Override public float getVolume() {
    return playable.getVolume();
  }

  @Override public void setVolumeInfo(@NonNull VolumeInfo volumeInfo) {
    playable.setVolumeInfo(volumeInfo);
  }

  @Override @NonNull public VolumeInfo getVolumeInfo() {
    return playable.getVolumeInfo();
  }

  @NonNull @Override public PlaybackInfo getLatestPlaybackInfo() {
    return playable.getPlaybackInfo();
  }

  @Override public void setPlaybackInfo(@NonNull PlaybackInfo playbackInfo) {
    this.playable.setPlaybackInfo(playbackInfo);
  }

  @OptIn(markerClass = UnstableApi.class)
  public void addEventListener(@NonNull Playable.EventListener listener) {
    //noinspection ConstantConditions
    if (listener != null) this.listeners.add(listener);
  }

  @OptIn(markerClass = UnstableApi.class)
  public void removeEventListener(Playable.EventListener listener) {
    this.listeners.remove(listener);
  }

  // A proxy, to also hook into ToroPlayerHelper's state change event.
  @UnstableApi
  private class MyEventListeners extends Playable.EventListeners {

    MyEventListeners() {
    }

    @Override public void onPlayerStateChanged(boolean playWhenReady, int playbackState) {
      ExoPlayerViewHelper.super.onPlayerStateUpdated(playWhenReady, playbackState); // important
      super.onPlayerStateChanged(playWhenReady, playbackState);
    }

    @Override public void onRenderedFirstFrame() {
      super.onRenderedFirstFrame();
      internalListener.onFirstFrameRendered();
      for (ToroPlayer.EventListener listener : ExoPlayerViewHelper.super.getEventListeners()) {
        listener.onFirstFrameRendered();
      }
    }
  }
}