diff --git a/build.gradle b/build.gradle index c8c4b56f..09c7c002 100644 --- a/build.gradle +++ b/build.gradle @@ -1,5 +1,5 @@ allprojects { - version = '4.5.2' + version = '4.5.3' } def teamPropsFile(propsFile) { @@ -14,7 +14,7 @@ buildscript { } dependencies { - classpath 'com.android.tools.build:gradle:3.3.0' + classpath 'com.android.tools.build:gradle:3.3.1' classpath 'com.novoda:bintray-release:0.9' classpath 'com.novoda:gradle-static-analysis-plugin:0.8' classpath 'com.novoda:gradle-build-properties-plugin:0.4.1' diff --git a/core/build.gradle b/core/build.gradle index c83db9f0..be57baa5 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -59,12 +59,12 @@ android { } dependencies { - implementation 'com.google.android.exoplayer:exoplayer:2.9.4' + implementation 'com.google.android.exoplayer:exoplayer:2.9.6' implementation 'com.android.support:support-annotations:28.0.0' testImplementation 'junit:junit:4.12' - testImplementation 'org.mockito:mockito-core:2.23.4' + testImplementation 'org.mockito:mockito-core:2.24.0' testImplementation 'org.easytesting:fest-assert-core:2.0M10' } diff --git a/core/src/main/java/com/novoda/noplayer/NoPlayerCreator.java b/core/src/main/java/com/novoda/noplayer/NoPlayerCreator.java index 7c757d81..b7577eeb 100644 --- a/core/src/main/java/com/novoda/noplayer/NoPlayerCreator.java +++ b/core/src/main/java/com/novoda/noplayer/NoPlayerCreator.java @@ -32,23 +32,32 @@ class NoPlayerCreator { this.drmSessionCreatorFactory = drmSessionCreatorFactory; } - NoPlayer create(DrmType drmType, DrmHandler drmHandler, boolean downgradeSecureDecoder) { + NoPlayer create(DrmType drmType, DrmHandler drmHandler, boolean downgradeSecureDecoder, boolean allowCrossProtocolRedirects) { for (PlayerType player : prioritizedPlayerTypes) { if (player.supports(drmType)) { - return createPlayerForType(player, drmType, drmHandler, downgradeSecureDecoder); + return createPlayerForType(player, drmType, drmHandler, downgradeSecureDecoder, allowCrossProtocolRedirects); } } throw UnableToCreatePlayerException.unhandledDrmType(drmType); } - private NoPlayer createPlayerForType(PlayerType playerType, DrmType drmType, DrmHandler drmHandler, boolean downgradeSecureDecoder) { + private NoPlayer createPlayerForType(PlayerType playerType, + DrmType drmType, + DrmHandler drmHandler, + boolean downgradeSecureDecoder, + boolean allowCrossProtocolRedirects) { switch (playerType) { case MEDIA_PLAYER: return noPlayerMediaPlayerCreator.createMediaPlayer(context); case EXO_PLAYER: try { DrmSessionCreator drmSessionCreator = drmSessionCreatorFactory.createFor(drmType, drmHandler); - return noPlayerExoPlayerCreator.createExoPlayer(context, drmSessionCreator, downgradeSecureDecoder); + return noPlayerExoPlayerCreator.createExoPlayer( + context, + drmSessionCreator, + downgradeSecureDecoder, + allowCrossProtocolRedirects + ); } catch (DrmSessionCreatorException exception) { throw new UnableToCreatePlayerException(exception); } diff --git a/core/src/main/java/com/novoda/noplayer/PlayerBuilder.java b/core/src/main/java/com/novoda/noplayer/PlayerBuilder.java index 7019866d..681ad689 100644 --- a/core/src/main/java/com/novoda/noplayer/PlayerBuilder.java +++ b/core/src/main/java/com/novoda/noplayer/PlayerBuilder.java @@ -26,7 +26,9 @@ public class PlayerBuilder { private DrmType drmType = DrmType.NONE; private DrmHandler drmHandler = DrmHandler.NO_DRM; private List prioritizedPlayerTypes = Arrays.asList(PlayerType.EXO_PLAYER, PlayerType.MEDIA_PLAYER); - private boolean downgradeSecureDecoder; + private boolean downgradeSecureDecoder; /* initialised to false by default */ + private boolean allowCrossProtocolRedirects; /* initialised to false by default */ + private String userAgent = "user-agent"; /** * Sets {@link PlayerBuilder} to build a {@link NoPlayer} which supports Widevine classic DRM. @@ -102,6 +104,24 @@ public PlayerBuilder withDowngradedSecureDecoder() { return this; } + /** + * @param userAgent The application's user-agent value + * @return {@link PlayerBuilder} + */ + public PlayerBuilder withUserAgent(String userAgent) { + this.userAgent = userAgent; + return this; + } + + /** + * Network connections will be allowed to perform redirects between HTTP and HTTPS protocols + * @return {@link PlayerBuilder} + */ + public PlayerBuilder allowCrossProtocolRedirects() { + allowCrossProtocolRedirects = true; + return this; + } + /** * Builds a new {@link NoPlayer} instance. * @@ -122,11 +142,11 @@ public NoPlayer build(Context context) throws UnableToCreatePlayerException { NoPlayerCreator noPlayerCreator = new NoPlayerCreator( applicationContext, prioritizedPlayerTypes, - NoPlayerExoPlayerCreator.newInstance(handler), + NoPlayerExoPlayerCreator.newInstance(userAgent, handler), NoPlayerMediaPlayerCreator.newInstance(handler), drmSessionCreatorFactory ); - return noPlayerCreator.create(drmType, drmHandler, downgradeSecureDecoder); + return noPlayerCreator.create(drmType, drmHandler, downgradeSecureDecoder, allowCrossProtocolRedirects); } } diff --git a/core/src/main/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreator.java b/core/src/main/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreator.java index 02845fe2..9e8eccf5 100644 --- a/core/src/main/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreator.java +++ b/core/src/main/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreator.java @@ -20,13 +20,13 @@ public class NoPlayerExoPlayerCreator { private final InternalCreator internalCreator; - public static NoPlayerExoPlayerCreator newInstance(Handler handler) { - InternalCreator internalCreator = new InternalCreator(handler, Optional.absent()); + public static NoPlayerExoPlayerCreator newInstance(String userAgent, Handler handler) { + InternalCreator internalCreator = new InternalCreator(userAgent, handler, Optional.absent()); return new NoPlayerExoPlayerCreator(internalCreator); } - public static NoPlayerExoPlayerCreator newInstance(Handler handler, DataSource.Factory dataSourceFactory) { - InternalCreator internalCreator = new InternalCreator(handler, Optional.of(dataSourceFactory)); + public static NoPlayerExoPlayerCreator newInstance(String userAgent, Handler handler, DataSource.Factory dataSourceFactory) { + InternalCreator internalCreator = new InternalCreator(userAgent, handler, Optional.of(dataSourceFactory)); return new NoPlayerExoPlayerCreator(internalCreator); } @@ -34,8 +34,11 @@ public static NoPlayerExoPlayerCreator newInstance(Handler handler, DataSource.F this.internalCreator = internalCreator; } - public NoPlayer createExoPlayer(Context context, DrmSessionCreator drmSessionCreator, boolean downgradeSecureDecoder) { - ExoPlayerTwoImpl player = internalCreator.create(context, drmSessionCreator, downgradeSecureDecoder); + public NoPlayer createExoPlayer(Context context, + DrmSessionCreator drmSessionCreator, + boolean downgradeSecureDecoder, + boolean allowCrossProtocolRedirects) { + ExoPlayerTwoImpl player = internalCreator.create(context, drmSessionCreator, downgradeSecureDecoder, allowCrossProtocolRedirects); player.initialise(); return player; } @@ -44,14 +47,25 @@ static class InternalCreator { private final Handler handler; private final Optional dataSourceFactory; + private final String userAgent; - InternalCreator(Handler handler, Optional dataSourceFactory) { + InternalCreator(String userAgent, Handler handler, Optional dataSourceFactory) { + this.userAgent = userAgent; this.handler = handler; this.dataSourceFactory = dataSourceFactory; } - ExoPlayerTwoImpl create(Context context, DrmSessionCreator drmSessionCreator, boolean downgradeSecureDecoder) { - MediaSourceFactory mediaSourceFactory = new MediaSourceFactory(context, handler, dataSourceFactory); + ExoPlayerTwoImpl create(Context context, + DrmSessionCreator drmSessionCreator, + boolean downgradeSecureDecoder, + boolean allowCrossProtocolRedirects) { + MediaSourceFactory mediaSourceFactory = new MediaSourceFactory( + context, + userAgent, + handler, + dataSourceFactory, + allowCrossProtocolRedirects + ); MediaCodecSelector mediaCodecSelector = downgradeSecureDecoder ? SecurityDowngradingCodecSelector.newInstance() diff --git a/core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/MediaSourceFactory.java b/core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/MediaSourceFactory.java index 04882c7c..5f3941c2 100644 --- a/core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/MediaSourceFactory.java +++ b/core/src/main/java/com/novoda/noplayer/internal/exoplayer/mediasource/MediaSourceFactory.java @@ -14,6 +14,8 @@ import com.google.android.exoplayer2.upstream.DataSource; import com.google.android.exoplayer2.upstream.DefaultBandwidthMeter; import com.google.android.exoplayer2.upstream.DefaultDataSourceFactory; +import com.google.android.exoplayer2.upstream.DefaultHttpDataSource; +import com.google.android.exoplayer2.upstream.DefaultHttpDataSourceFactory; import com.novoda.noplayer.Options; import com.novoda.noplayer.internal.utils.Optional; @@ -22,11 +24,19 @@ public class MediaSourceFactory { private final Context context; private final Handler handler; private final Optional dataSourceFactory; + private final String userAgent; + private final boolean allowCrossProtocolRedirects; - public MediaSourceFactory(Context context, Handler handler, Optional dataSourceFactory) { + public MediaSourceFactory(Context context, + String userAgent, + Handler handler, + Optional dataSourceFactory, + boolean allowCrossProtocolRedirects) { this.context = context; this.handler = handler; this.dataSourceFactory = dataSourceFactory; + this.userAgent = userAgent; + this.allowCrossProtocolRedirects = allowCrossProtocolRedirects; } public MediaSource create(Options options, @@ -50,7 +60,15 @@ private DefaultDataSourceFactory createDataSourceFactory(DefaultBandwidthMeter b if (dataSourceFactory.isPresent()) { return new DefaultDataSourceFactory(context, bandwidthMeter, dataSourceFactory.get()); } else { - return new DefaultDataSourceFactory(context, "user-agent", bandwidthMeter); + DefaultHttpDataSourceFactory httpDataSourceFactory = new DefaultHttpDataSourceFactory( + userAgent, + bandwidthMeter, + DefaultHttpDataSource.DEFAULT_CONNECT_TIMEOUT_MILLIS, + DefaultHttpDataSource.DEFAULT_READ_TIMEOUT_MILLIS, + allowCrossProtocolRedirects + ); + + return new DefaultDataSourceFactory(context, bandwidthMeter, httpDataSourceFactory); } } diff --git a/core/src/test/java/com/novoda/noplayer/NoPlayerCreatorTest.java b/core/src/test/java/com/novoda/noplayer/NoPlayerCreatorTest.java index c8299486..4fca7ad0 100644 --- a/core/src/test/java/com/novoda/noplayer/NoPlayerCreatorTest.java +++ b/core/src/test/java/com/novoda/noplayer/NoPlayerCreatorTest.java @@ -12,9 +12,6 @@ import com.novoda.noplayer.internal.exoplayer.drm.DrmSessionCreatorFactory; import com.novoda.noplayer.internal.mediaplayer.NoPlayerMediaPlayerCreator; -import java.util.Arrays; -import java.util.List; - import org.junit.Before; import org.junit.Rule; import org.junit.Test; @@ -24,6 +21,9 @@ import org.mockito.junit.MockitoJUnit; import org.mockito.junit.MockitoRule; +import java.util.Arrays; +import java.util.List; + import static org.fest.assertions.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.any; import static org.mockito.BDDMockito.given; @@ -35,6 +35,7 @@ public class NoPlayerCreatorTest { public abstract static class Base { static final boolean USE_SECURE_CODEC = false; + static final boolean ALLOW_CROSS_PROTOCOL_REDIRECTS = false; static final StreamingModularDrm STREAMING_MODULAR_DRM = mock(StreamingModularDrm.class); static final DownloadedModularDrm DOWNLOADED_MODULAR_DRM = mock(DownloadedModularDrm.class); static final NoPlayer EXO_PLAYER = mock(NoPlayer.class); @@ -60,7 +61,7 @@ public abstract static class Base { @Before public void setUp() throws DrmSessionCreatorException { given(drmSessionCreatorFactory.createFor(any(DrmType.class), any(DrmHandler.class))).willReturn(drmSessionCreator); - given(noPlayerExoPlayerCreator.createExoPlayer(context, drmSessionCreator, USE_SECURE_CODEC)).willReturn(EXO_PLAYER); + given(noPlayerExoPlayerCreator.createExoPlayer(context, drmSessionCreator, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS)).willReturn(EXO_PLAYER); given(noPlayerMediaPlayerCreator.createMediaPlayer(context)).willReturn(MEDIA_PLAYER); noPlayerCreator = new NoPlayerCreator(context, prioritizedPlayerTypes(), noPlayerExoPlayerCreator, noPlayerMediaPlayerCreator, drmSessionCreatorFactory); } @@ -77,28 +78,28 @@ List prioritizedPlayerTypes() { @Test public void whenCreatingPlayerWithDrmTypeNone_thenReturnsMediaPlayer() { - NoPlayer player = noPlayerCreator.create(DrmType.NONE, DrmHandler.NO_DRM, USE_SECURE_CODEC); + NoPlayer player = noPlayerCreator.create(DrmType.NONE, DrmHandler.NO_DRM, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); assertThat(player).isEqualTo(MEDIA_PLAYER); } @Test public void whenCreatingPlayerWithDrmTypeWidevineClassic_thenReturnsMediaPlayer() { - NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_CLASSIC, DrmHandler.NO_DRM, USE_SECURE_CODEC); + NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_CLASSIC, DrmHandler.NO_DRM, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); assertThat(player).isEqualTo(MEDIA_PLAYER); } @Test public void whenCreatingPlayerWithDrmTypeWidevineModularStream_thenReturnsExoPlayer() { - NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_MODULAR_STREAM, STREAMING_MODULAR_DRM, USE_SECURE_CODEC); + NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_MODULAR_STREAM, STREAMING_MODULAR_DRM, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); assertThat(player).isEqualTo(EXO_PLAYER); } @Test public void whenCreatingPlayerWithDrmTypeWidevineModularDownload_thenReturnsExoPlayer() { - NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_MODULAR_DOWNLOAD, DOWNLOADED_MODULAR_DRM, USE_SECURE_CODEC); + NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_MODULAR_DOWNLOAD, DOWNLOADED_MODULAR_DRM, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); assertThat(player).isEqualTo(EXO_PLAYER); } @@ -113,28 +114,28 @@ List prioritizedPlayerTypes() { @Test public void whenCreatingPlayerWithDrmTypeNone_thenReturnsExoPlayer() { - NoPlayer player = noPlayerCreator.create(DrmType.NONE, DrmHandler.NO_DRM, USE_SECURE_CODEC); + NoPlayer player = noPlayerCreator.create(DrmType.NONE, DrmHandler.NO_DRM, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); assertThat(player).isEqualTo(EXO_PLAYER); } @Test public void whenCreatingPlayerWithDrmTypeWidevineClassic_thenReturnsMediaPlayer() { - NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_CLASSIC, DrmHandler.NO_DRM, USE_SECURE_CODEC); + NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_CLASSIC, DrmHandler.NO_DRM, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); assertThat(player).isEqualTo(MEDIA_PLAYER); } @Test public void whenCreatingPlayerWithDrmTypeWidevineModularStream_thenReturnsExoPlayer() { - NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_MODULAR_STREAM, STREAMING_MODULAR_DRM, USE_SECURE_CODEC); + NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_MODULAR_STREAM, STREAMING_MODULAR_DRM, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); assertThat(player).isEqualTo(EXO_PLAYER); } @Test public void whenCreatingPlayerWithDrmTypeWidevineModularDownload_thenReturnsExoPlayer() { - NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_MODULAR_DOWNLOAD, DOWNLOADED_MODULAR_DRM, USE_SECURE_CODEC); + NoPlayer player = noPlayerCreator.create(DrmType.WIDEVINE_MODULAR_DOWNLOAD, DOWNLOADED_MODULAR_DRM, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); assertThat(player).isEqualTo(EXO_PLAYER); } diff --git a/core/src/test/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreatorTest.java b/core/src/test/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreatorTest.java index 3ee2cda9..1b09bbaa 100644 --- a/core/src/test/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreatorTest.java +++ b/core/src/test/java/com/novoda/noplayer/internal/exoplayer/NoPlayerExoPlayerCreatorTest.java @@ -17,6 +17,7 @@ public class NoPlayerExoPlayerCreatorTest { private static final boolean USE_SECURE_CODEC = true; + private static final boolean ALLOW_CROSS_PROTOCOL_REDIRECTS = true; @Rule public MockitoRule mockitoRule = MockitoJUnit.rule(); @@ -34,13 +35,13 @@ public class NoPlayerExoPlayerCreatorTest { @Before public void setUp() { - given(internalCreator.create(context, drmSessionCreator, USE_SECURE_CODEC)).willReturn(player); + given(internalCreator.create(context, drmSessionCreator, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS)).willReturn(player); creator = new NoPlayerExoPlayerCreator(internalCreator); } @Test public void whenCreatingExoPlayerTwo_thenInitialisesPlayer() { - creator.createExoPlayer(context, drmSessionCreator, USE_SECURE_CODEC); + creator.createExoPlayer(context, drmSessionCreator, USE_SECURE_CODEC, ALLOW_CROSS_PROTOCOL_REDIRECTS); verify(player).initialise(); } diff --git a/demo/src/main/java/com/novoda/demo/MainActivity.java b/demo/src/main/java/com/novoda/demo/MainActivity.java index 2862d7de..8bb4f5a9 100644 --- a/demo/src/main/java/com/novoda/demo/MainActivity.java +++ b/demo/src/main/java/com/novoda/demo/MainActivity.java @@ -46,6 +46,8 @@ protected void onCreate(Bundle savedInstanceState) { player = new PlayerBuilder() .withWidevineModularStreamingDrm(drmHandler) .withDowngradedSecureDecoder() + .withUserAgent("Android/Linux") + .allowCrossProtocolRedirects() .build(this); demoPresenter = new DemoPresenter(controllerView, player, player.getListeners(), playerView);