Skip to content

[BUG] Make Chromecast Support Optional #202

@Jmilham21

Description

@Jmilham21

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

  • Create android/src/cast/java/com/jwplayer/rnjwplayer/CastHelper.java (full implementation)
  • Create android/src/nocast/java/com/jwplayer/rnjwplayer/CastHelper.java (stub implementation)
  • Move CastOptionsProvider.java to android/src/cast/java/ (or create stub version)
  • Refactor RNJWPlayerView.java to use CastHelper instead of direct Cast imports
  • Remove CastingEvents implementation from RNJWPlayerView.java
  • Update build.gradle with Cast source sets configuration
  • Make jwplayer-chromecast dependency conditional
  • Add BuildConfig.USE_CAST field (similar to USE_IMA)
  • Test: Build with RNJWPlayerUseGoogleCast = false → Should succeed
  • Test: Build with RNJWPlayerUseGoogleCast = true → Should succeed with full Cast functionality
  • Test: Runtime with Cast disabled + attempt to use Cast → Clear error message
  • Test: Runtime with Cast enabled → Full Chromecast functionality works
  • Update documentation with Cast setup instructions
  • Measure APK size reduction when Cast is disabled

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:

  1. Build tests: All 4 combinations above should compile successfully
  2. Runtime tests: Attempting to use disabled features should show clear errors
  3. APK size: Measure size difference with Cast disabled
  4. 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

  1. Always include Cast (current state) - Simple but wasteful
  2. Reflection-based approach - More complex, runtime overhead, error-prone
  3. Separate module - Too much refactoring, breaks existing integrations

Notes

References

Metadata

Metadata

Assignees

Labels

GroomedJWP team has reviewed the ticket and deemed it necessarybugSomething isn't workingin-progressActively being worked on by the asignee

Type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions