Make Chromecast Support Optional (Apply Source Sets Pattern)
Summary
Currently, the jwplayer-chromecast dependency is always included in the Android build, even if developers don't use Chromecast functionality. This is similar to issue #201 (IMA ads) which we fixed using Gradle source sets. We should apply the same pattern to make Chromecast truly optional.
Background
Issue #201 demonstrated that always including optional SDKs causes problems:
- Unnecessary dependencies increase app size
- Some dependencies (like IMA) require additional configuration (desugaring)
- Developers who don't use these features shouldn't pay the cost
We successfully fixed this for IMA ads using Gradle source sets. We should apply the same pattern to Chromecast.
Current Behavior
build.gradle:
// Always included (NOT actually optional)
implementation "com.jwplayer:jwplayer-chromecast:${...}"
// Only this part is conditional
if (useCast) {
implementation 'com.google.android.gms:play-services-cast-framework:21.3.0'
}
Code:
CastOptionsProvider.java - Unconditionally imports Google Cast SDK classes
RNJWPlayerView.java - Unconditionally imports and implements CastingEvents
Problem: If we make jwplayer-chromecast conditional (like we tried to do), builds fail when RNJWPlayerUseGoogleCast = false because Cast classes are referenced unconditionally in the code.
Desired Behavior
When RNJWPlayerUseGoogleCast = false (or not set):
- ✅ Build succeeds without any Cast dependencies
- ✅ No
jwplayer-chromecast included
- ✅ No Google Cast SDK included
- ✅ Smaller APK size
- ✅ Clear error message if user tries to use Cast functionality
When RNJWPlayerUseGoogleCast = true:
- ✅ Full Chromecast functionality works
- ✅ All Cast dependencies included
Proposed Solution
Apply the same source sets pattern we used for IMA (see PR #XXX):
1. Create Cast Helper (Full Implementation)
File: android/src/cast/java/com/jwplayer/rnjwplayer/CastHelper.java
package com.jwplayer.rnjwplayer;
import com.google.android.gms.cast.framework.CastContext;
// ... other Cast imports
/**
* Chromecast-specific helper.
* Only compiled when RNJWPlayerUseGoogleCast = true.
*/
public class CastHelper {
public static void initializeCast(Context context) {
// Cast initialization logic
}
public static boolean isCastAvailable() {
return true;
}
// ... other Cast methods
}
2. Create Cast Helper (Stub Implementation)
File: android/src/nocast/java/com/jwplayer/rnjwplayer/CastHelper.java
package com.jwplayer.rnjwplayer;
/**
* Stub implementation when Cast is disabled.
* Provides clear error messages.
*/
public class CastHelper {
public static void initializeCast(Context context) {
// No-op when Cast is disabled
}
public static boolean isCastAvailable() {
return false;
}
}
3. Move CastOptionsProvider to Cast Source Set
- Move
CastOptionsProvider.java to android/src/cast/java/
- Create stub version in
android/src/nocast/java/ if needed by manifest
4. Update RNJWPlayerView.java
Remove unconditional Cast imports and implementations:
// Before: Always implements CastingEvents
public class RNJWPlayerView extends RelativeLayout implements
VideoPlayerEvents.OnFullscreenListener,
CastingEvents.OnCastListener, // ← Remove this
...
// After: Delegate to CastHelper
private void setupCastListeners() {
if (CastHelper.isCastAvailable()) {
// Only set up Cast listeners when available
}
}
5. Update build.gradle
android {
// ... existing config ...
sourceSets {
main {
java {
srcDirs = ['src/main/java']
// IMA source sets (already implemented)
if (useIMA) {
srcDirs += ['src/ima/java']
} else {
srcDirs += ['src/noima/java']
}
// Cast source sets (NEW)
if (useCast) {
srcDirs += ['src/cast/java']
} else {
srcDirs += ['src/nocast/java']
}
}
}
}
}
dependencies {
// ... existing dependencies ...
// Chromecast dependencies (conditional)
if (useCast) {
implementation "com.jwplayer:jwplayer-chromecast:${...}"
implementation 'com.google.android.gms:play-services-cast-framework:21.3.0'
}
}
Implementation Checklist
Testing Strategy
Test Matrix:
| Cast Flag |
IMA Flag |
Expected Result |
| false |
false |
Minimal build, both optional features disabled |
| false |
true |
IMA works, Cast disabled |
| true |
false |
Cast works, IMA disabled |
| true |
true |
Both features work |
Specific Tests:
- Build tests: All 4 combinations above should compile successfully
- Runtime tests: Attempting to use disabled features should show clear errors
- APK size: Measure size difference with Cast disabled
- Integration tests: Verify existing Cast functionality still works when enabled
Related Issues/PRs
Benefits
- Smaller APKs: Developers who don't use Chromecast save ~XX MB
- Faster builds: Fewer dependencies to process
- Flexibility: Developers can enable only the features they need
- Consistency: Same pattern as IMA (easier to maintain)
- Clear errors: If Cast disabled but code tries to use it, clear error message
Alternatives Considered
- Always include Cast (current state) - Simple but wasteful
- Reflection-based approach - More complex, runtime overhead, error-prone
- Separate module - Too much refactoring, breaks existing integrations
Notes
References
Make Chromecast Support Optional (Apply Source Sets Pattern)
Summary
Currently, the
jwplayer-chromecastdependency is always included in the Android build, even if developers don't use Chromecast functionality. This is similar to issue #201 (IMA ads) which we fixed using Gradle source sets. We should apply the same pattern to make Chromecast truly optional.Background
Issue #201 demonstrated that always including optional SDKs causes problems:
We successfully fixed this for IMA ads using Gradle source sets. We should apply the same pattern to Chromecast.
Current Behavior
build.gradle:
Code:
CastOptionsProvider.java- Unconditionally imports Google Cast SDK classesRNJWPlayerView.java- Unconditionally imports and implementsCastingEventsProblem: If we make
jwplayer-chromecastconditional (like we tried to do), builds fail whenRNJWPlayerUseGoogleCast = falsebecause Cast classes are referenced unconditionally in the code.Desired Behavior
When
RNJWPlayerUseGoogleCast = false(or not set):jwplayer-chromecastincludedWhen
RNJWPlayerUseGoogleCast = true:Proposed Solution
Apply the same source sets pattern we used for IMA (see PR #XXX):
1. Create Cast Helper (Full Implementation)
File:
android/src/cast/java/com/jwplayer/rnjwplayer/CastHelper.java2. Create Cast Helper (Stub Implementation)
File:
android/src/nocast/java/com/jwplayer/rnjwplayer/CastHelper.java3. Move CastOptionsProvider to Cast Source Set
CastOptionsProvider.javatoandroid/src/cast/java/android/src/nocast/java/if needed by manifest4. Update RNJWPlayerView.java
Remove unconditional Cast imports and implementations:
5. Update build.gradle
Implementation Checklist
android/src/cast/java/com/jwplayer/rnjwplayer/CastHelper.java(full implementation)android/src/nocast/java/com/jwplayer/rnjwplayer/CastHelper.java(stub implementation)CastOptionsProvider.javatoandroid/src/cast/java/(or create stub version)RNJWPlayerView.javato useCastHelperinstead of direct Cast importsCastingEventsimplementation fromRNJWPlayerView.javabuild.gradlewith Cast source sets configurationjwplayer-chromecastdependency conditionalBuildConfig.USE_CASTfield (similar toUSE_IMA)RNJWPlayerUseGoogleCast = false→ Should succeedRNJWPlayerUseGoogleCast = true→ Should succeed with full Cast functionalityTesting Strategy
Test Matrix:
Specific Tests:
Related Issues/PRs
Benefits
Alternatives Considered
Notes
References
android/src/ima/java/com/jwplayer/rnjwplayer/ImaHelper.java