Skip to content

Commit c5681bb

Browse files
authored
Fix Android build failure for non-IMA users #203
Fix Android build failure for non-IMA users (#201)
2 parents 908a37f + 44fb235 commit c5681bb

File tree

5 files changed

+343
-136
lines changed

5 files changed

+343
-136
lines changed

android/build.gradle

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,19 @@ android {
6565
lintOptions {
6666
warning 'InvalidPackage'
6767
}
68+
69+
sourceSets {
70+
main {
71+
java {
72+
srcDirs = ['src/main/java']
73+
if (useIMA) {
74+
srcDirs += ['src/ima/java']
75+
} else {
76+
srcDirs += ['src/noima/java']
77+
}
78+
}
79+
}
80+
}
6881
}
6982

7083
allprojects {
@@ -90,14 +103,14 @@ dependencies {
90103
implementation 'com.facebook.react:react-native:+'
91104
implementation 'com.google.code.gson:gson:2.9.0'
92105

93-
// JWPlayer SDK
106+
// JWPlayer SDK Core (always required)
94107
implementation "com.jwplayer:jwplayer-core:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
95108
implementation "com.jwplayer:jwplayer-common:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
96-
implementation "com.jwplayer:jwplayer-ima:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
97109
implementation "com.jwplayer:jwplayer-chromecast:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
98-
99-
// Ad dependencies
110+
111+
// Optional: IMA Ads support
100112
if (useIMA) {
113+
implementation "com.jwplayer:jwplayer-ima:${safeExtGet('jwPlayerVersion', jwPlayerVersion)}"
101114
implementation 'com.google.ads.interactivemedia.v3:interactivemedia:3.36.0'
102115
implementation 'com.google.android.gms:play-services-ads-identifier:18.0.1'
103116
// Core library desugaring (required for IMA SDK 3.37.0+)
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.jwplayer.rnjwplayer;
2+
3+
import com.facebook.react.bridge.ReadableMap;
4+
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
5+
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
6+
import com.jwplayer.pub.api.configuration.ads.AdvertisingConfig;
7+
import com.jwplayer.pub.api.configuration.ads.dai.ImaDaiAdvertisingConfig;
8+
import com.jwplayer.pub.api.configuration.ads.ima.ImaAdvertisingConfig;
9+
import com.jwplayer.pub.api.media.ads.AdBreak;
10+
import com.jwplayer.pub.api.media.ads.dai.ImaDaiSettings;
11+
12+
import java.util.List;
13+
import java.util.Map;
14+
import java.util.HashMap;
15+
import java.util.Objects;
16+
17+
/**
18+
* IMA-specific advertising configuration helper.
19+
* This class is only compiled when RNJWPlayerUseGoogleIMA = true.
20+
*/
21+
public class ImaHelper {
22+
23+
public static AdvertisingConfig configureImaOrDai(ReadableMap ads, List<AdBreak> adSchedule) {
24+
cd /Users/jmilham/source/sdk/react/jwplayer-react-native/Example && yarn remove @jwplayer/jwplayer-react-native && yarn add file:../ 2>&1 | tail -20 // Check both "client" (JWPlayer JSON format) and "adClient" (RN wrapper format)
25+
String adClientType = null;
26+
if (ads.hasKey("adClient")) {
27+
adClientType = ads.getString("adClient");
28+
} else if (ads.hasKey("client")) {
29+
adClientType = ads.getString("client");
30+
}
31+
32+
if (adClientType != null) {
33+
adClientType = adClientType.toLowerCase();
34+
}
35+
36+
if ("ima".equals(adClientType) || "googima".equals(adClientType)) {
37+
return configureImaAdvertising(ads, adSchedule);
38+
} else if ("ima_dai".equals(adClientType)) {
39+
return configureImaDaiAdvertising(ads);
40+
}
41+
42+
// Should never reach here due to validation in RNJWPlayerAds
43+
throw new RuntimeException("Unknown IMA ad client type: " + adClientType);
44+
}
45+
46+
private static ImaAdvertisingConfig configureImaAdvertising(ReadableMap ads, List<AdBreak> adSchedule) {
47+
ImaAdvertisingConfig.Builder builder = new ImaAdvertisingConfig.Builder();
48+
builder.schedule(adSchedule);
49+
50+
if (ads.hasKey("imaSettings")) {
51+
builder.imaSdkSettings(getImaSettings(Objects.requireNonNull(ads.getMap("imaSettings"))));
52+
}
53+
54+
return builder.build();
55+
}
56+
57+
private static ImaDaiAdvertisingConfig configureImaDaiAdvertising(ReadableMap ads) {
58+
ImaDaiAdvertisingConfig.Builder builder = new ImaDaiAdvertisingConfig.Builder();
59+
60+
if (ads.hasKey("imaSettings")) {
61+
builder.imaSdkSettings(getImaSettings(Objects.requireNonNull(ads.getMap("imaSettings"))));
62+
}
63+
64+
if (ads.hasKey("imaDaiSettings")) {
65+
builder.imaDaiSettings(getImaDaiSettings(Objects.requireNonNull(ads.getMap("imaDaiSettings"))));
66+
}
67+
68+
return builder.build();
69+
}
70+
71+
private static ImaDaiSettings getImaDaiSettings(ReadableMap imaDaiSettingsMap) {
72+
String videoId = imaDaiSettingsMap.hasKey("videoId") ? imaDaiSettingsMap.getString("videoId") : null;
73+
String cmsId = imaDaiSettingsMap.hasKey("cmsId") ? imaDaiSettingsMap.getString("cmsId") : null;
74+
String assetKey = imaDaiSettingsMap.hasKey("assetKey") ? imaDaiSettingsMap.getString("assetKey") : null;
75+
String apiKey = imaDaiSettingsMap.hasKey("apiKey") ? imaDaiSettingsMap.getString("apiKey") : null;
76+
77+
// Validate we have either assetKey OR (videoId + cmsId)
78+
if (assetKey == null && (videoId == null || cmsId == null)) {
79+
throw new IllegalArgumentException(
80+
"ImaDaiSettings requires either 'assetKey' OR both 'videoId' and 'cmsId'. " +
81+
"Provided: assetKey=" + assetKey + ", videoId=" + videoId + ", cmsId=" + cmsId
82+
);
83+
}
84+
85+
Map<String, String> adTagParameters = null;
86+
if (imaDaiSettingsMap.hasKey("adTagParameters") && imaDaiSettingsMap.getMap("adTagParameters") != null) {
87+
adTagParameters = new HashMap<>();
88+
ReadableMap adTagParamsMap = imaDaiSettingsMap.getMap("adTagParameters");
89+
for (Map.Entry<String, Object> entry : adTagParamsMap.toHashMap().entrySet()) {
90+
if (entry.getValue() instanceof String) {
91+
adTagParameters.put(entry.getKey(), (String) entry.getValue());
92+
}
93+
}
94+
}
95+
96+
ImaDaiSettings.StreamType streamType = ImaDaiSettings.StreamType.HLS;
97+
if (imaDaiSettingsMap.hasKey("streamType")) {
98+
String streamTypeStr = imaDaiSettingsMap.getString("streamType");
99+
if ("DASH".equalsIgnoreCase(streamTypeStr)) {
100+
streamType = ImaDaiSettings.StreamType.DASH;
101+
}
102+
}
103+
104+
ImaDaiSettings imaDaiSettings = (assetKey != null) ?
105+
new ImaDaiSettings(assetKey, streamType, apiKey) :
106+
new ImaDaiSettings(videoId, cmsId, streamType, apiKey);
107+
108+
if (adTagParameters != null) {
109+
imaDaiSettings.setAdTagParameters(adTagParameters);
110+
}
111+
112+
return imaDaiSettings;
113+
}
114+
115+
public static ImaSdkSettings getImaSettings(ReadableMap imaSettingsMap) {
116+
ImaSdkSettings settings = ImaSdkFactory.getInstance().createImaSdkSettings();
117+
118+
if (imaSettingsMap.hasKey("maxRedirects")) {
119+
settings.setMaxRedirects(imaSettingsMap.getInt("maxRedirects"));
120+
}
121+
if (imaSettingsMap.hasKey("language")) {
122+
settings.setLanguage(imaSettingsMap.getString("language"));
123+
}
124+
if (imaSettingsMap.hasKey("ppid")) {
125+
settings.setPpid(imaSettingsMap.getString("ppid"));
126+
}
127+
if (imaSettingsMap.hasKey("playerType")) {
128+
settings.setPlayerType(imaSettingsMap.getString("playerType"));
129+
}
130+
if (imaSettingsMap.hasKey("playerVersion")) {
131+
settings.setPlayerVersion(imaSettingsMap.getString("playerVersion"));
132+
}
133+
if (imaSettingsMap.hasKey("sessionId")) {
134+
settings.setSessionId(imaSettingsMap.getString("sessionId"));
135+
}
136+
if (imaSettingsMap.hasKey("debugMode")) {
137+
settings.setDebugMode(imaSettingsMap.getBoolean("debugMode"));
138+
}
139+
140+
return settings;
141+
}
142+
}
143+

android/src/main/java/com/jwplayer/rnjwplayer/RNJWPlayerAds.java

Lines changed: 41 additions & 129 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,13 @@
22

33
import com.facebook.react.bridge.ReadableArray;
44
import com.facebook.react.bridge.ReadableMap;
5-
import com.google.ads.interactivemedia.v3.api.FriendlyObstruction;
6-
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
7-
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
85
import com.jwplayer.pub.api.configuration.ads.AdRules;
96
import com.jwplayer.pub.api.configuration.ads.AdvertisingConfig;
107
import com.jwplayer.pub.api.configuration.ads.VastAdvertisingConfig;
11-
import com.jwplayer.pub.api.configuration.ads.dai.ImaDaiAdvertisingConfig;
12-
import com.jwplayer.pub.api.configuration.ads.ima.ImaAdvertisingConfig;
138
import com.jwplayer.pub.api.media.ads.AdBreak;
14-
import com.jwplayer.pub.api.media.ads.dai.ImaDaiSettings;
159

1610
import java.util.ArrayList;
17-
import java.util.HashMap;
1811
import java.util.List;
19-
import java.util.Map;
2012
import java.util.Objects;
2113

2214
public class RNJWPlayerAds {
@@ -27,103 +19,43 @@ public static AdvertisingConfig getAdvertisingConfig(ReadableMap ads) {
2719
return null;
2820
}
2921

30-
String adClientType = ads.getString("adClient");
22+
// Check both "client" (JWPlayer JSON format) and "adClient" (RN wrapper format)
23+
String adClientType = null;
24+
if (ads.hasKey("adClient")) {
25+
adClientType = ads.getString("adClient");
26+
} else if (ads.hasKey("client")) {
27+
adClientType = ads.getString("client");
28+
}
29+
30+
// Validate client type exists and is not null
31+
if (adClientType == null) {
32+
throw new IllegalArgumentException("Missing required 'adClient' or 'client' field in advertising config");
33+
}
34+
35+
// Normalize to lowercase for case-insensitive matching
36+
adClientType = adClientType.toLowerCase();
37+
3138
switch (adClientType) {
3239
case "ima":
33-
try {
34-
return configureImaAdvertising(ads);
35-
} catch (Exception e) {
36-
throw new RuntimeException(e);
37-
}
40+
case "googima": // Support legacy "googima" format
3841
case "ima_dai":
42+
// Delegate to ImaHelper (implementation selected by Gradle)
43+
// Note: Only parse adSchedule for regular IMA, not for DAI (ads are embedded in stream)
44+
List<AdBreak> adSchedule = ("ima".equals(adClientType) || "googima".equals(adClientType))
45+
? getAdSchedule(ads)
46+
: new ArrayList<>();
3947
try {
40-
return configureImaDaiAdvertising(ads);
41-
} catch (Exception e) {
42-
throw new RuntimeException(e);
48+
return ImaHelper.configureImaOrDai(ads, adSchedule);
49+
} catch (RuntimeException e) {
50+
// IMA not enabled - log error and return null (graceful degradation)
51+
android.util.Log.e("RNJWPlayerAds", "Failed to configure IMA ads: " + e.getMessage());
52+
return null;
4353
}
4454
default: // Defaulting to VAST
4555
return configureVastAdvertising(ads);
4656
}
4757
}
4858

49-
// Configure IMA Advertising
50-
private static ImaAdvertisingConfig configureImaAdvertising(ReadableMap ads) throws Exception {
51-
if (!BuildConfig.USE_IMA) {
52-
throw new Exception("Error: Google ads services is not installed. Add RNJWPlayerUseGoogleIMA = true to your app/build.gradle ext {}");
53-
}
54-
55-
ImaAdvertisingConfig.Builder builder = new ImaAdvertisingConfig.Builder();
56-
57-
List<AdBreak> adScheduleList = getAdSchedule(ads);
58-
builder.schedule(adScheduleList);
59-
60-
if (ads.hasKey("imaSettings")) {
61-
builder.imaSdkSettings(getImaSettings(Objects.requireNonNull(ads.getMap("imaSettings"))));
62-
}
63-
64-
// companionSlots
65-
66-
return builder.build();
67-
}
68-
69-
// Configure IMA DAI Advertising
70-
private static ImaDaiAdvertisingConfig configureImaDaiAdvertising(ReadableMap ads) throws Exception {
71-
if (!BuildConfig.USE_IMA) {
72-
throw new Exception("Error: Google ads services is not installed. Add RNJWPlayerUseGoogleIMA = true to your app/build.gradle ext {}");
73-
}
74-
75-
ImaDaiAdvertisingConfig.Builder builder = new ImaDaiAdvertisingConfig.Builder();
76-
77-
if (ads.hasKey("imaSettings")) {
78-
builder.imaSdkSettings(getImaSettings(Objects.requireNonNull(ads.getMap("imaSettings"))));
79-
}
80-
81-
if (ads.hasKey("imaDaiSettings")) {
82-
builder.imaDaiSettings(getImaDaiSettings(Objects.requireNonNull(ads.getMap("imaDaiSettings"))));
83-
}
84-
85-
return builder.build();
86-
}
87-
88-
// You'll need to implement this method based on how you pass ImaDaiSettings from React Native
89-
private static ImaDaiSettings getImaDaiSettings(ReadableMap imaDaiSettingsMap) {
90-
String videoId = imaDaiSettingsMap.hasKey("videoId") ? imaDaiSettingsMap.getString("videoId") : null;
91-
String cmsId = imaDaiSettingsMap.hasKey("cmsId") ? imaDaiSettingsMap.getString("cmsId") : null;
92-
String assetKey = imaDaiSettingsMap.hasKey("assetKey") ? imaDaiSettingsMap.getString("assetKey") : null;
93-
String apiKey = imaDaiSettingsMap.hasKey("apiKey") ? imaDaiSettingsMap.getString("apiKey") : null;
94-
95-
// Extracting adTagParameters from imaDaiSettingsMap if present
96-
Map<String, String> adTagParameters = null;
97-
if (imaDaiSettingsMap.hasKey("adTagParameters") && imaDaiSettingsMap.getMap("adTagParameters") != null) {
98-
adTagParameters = new HashMap<>();
99-
ReadableMap adTagParamsMap = imaDaiSettingsMap.getMap("adTagParameters");
100-
for (Map.Entry<String, Object> entry : adTagParamsMap.toHashMap().entrySet()) {
101-
if (entry.getValue() instanceof String) {
102-
adTagParameters.put(entry.getKey(), (String) entry.getValue());
103-
}
104-
}
105-
}
106-
107-
// Handling streamType
108-
ImaDaiSettings.StreamType streamType = ImaDaiSettings.StreamType.HLS; // Default to HLS
109-
if (imaDaiSettingsMap.hasKey("streamType")) {
110-
String streamTypeStr = imaDaiSettingsMap.getString("streamType");
111-
if ("DASH".equalsIgnoreCase(streamTypeStr)) {
112-
streamType = ImaDaiSettings.StreamType.DASH;
113-
}
114-
}
115-
// Create ImaDaiSettings based on the provided values
116-
ImaDaiSettings imaDaiSettings = (assetKey != null) ?
117-
new ImaDaiSettings(assetKey, streamType, apiKey) :
118-
new ImaDaiSettings(videoId, cmsId, streamType, apiKey);
119-
120-
if (adTagParameters != null) {
121-
imaDaiSettings.setAdTagParameters(adTagParameters);
122-
}
123-
124-
return imaDaiSettings;
125-
}
126-
12759
// Configure VAST Advertising
12860
private static VastAdvertisingConfig configureVastAdvertising(ReadableMap ads) {
12961
VastAdvertisingConfig.Builder builder = new VastAdvertisingConfig.Builder();
@@ -154,9 +86,23 @@ private static VastAdvertisingConfig configureVastAdvertising(ReadableMap ads) {
15486

15587
private static List<AdBreak> getAdSchedule(ReadableMap ads) {
15688
List<AdBreak> adScheduleList = new ArrayList<>();
89+
90+
// Check if adSchedule exists
91+
if (!ads.hasKey("adSchedule")) {
92+
return adScheduleList; // Return empty list
93+
}
94+
15795
ReadableArray adSchedule = ads.getArray("adSchedule");
96+
if (adSchedule == null) {
97+
return adScheduleList; // Return empty list if null
98+
}
99+
158100
for (int i = 0; i < adSchedule.size(); i++) {
159101
ReadableMap adBreakProp = adSchedule.getMap(i);
102+
// Skip null entries in the adSchedule array
103+
if (adBreakProp == null) {
104+
continue;
105+
}
160106
String offset = adBreakProp.hasKey("offset") ? adBreakProp.getString("offset") : "pre";
161107
if (adBreakProp.hasKey("tag")) {
162108
AdBreak adBreak = new AdBreak.Builder()
@@ -202,38 +148,4 @@ private static String mapStartOnSeek(String startOnSeek) {
202148
return AdRules.RULES_START_ON_SEEK_NONE;
203149
}
204150

205-
// public static List<FriendlyObstruction> getFriendlyObstructions(ReadableArray obstructionsArray) {
206-
// List<FriendlyObstruction> obstructions = new ArrayList<>();
207-
// // Example: Parse and create FriendlyObstruction objects from obstructionsArray
208-
// return obstructions;
209-
// }
210-
211-
public static ImaSdkSettings getImaSettings(ReadableMap imaSettingsMap) {
212-
ImaSdkSettings settings = ImaSdkFactory.getInstance().createImaSdkSettings();
213-
214-
if (imaSettingsMap.hasKey("maxRedirects")) {
215-
settings.setMaxRedirects(imaSettingsMap.getInt("maxRedirects"));
216-
}
217-
if (imaSettingsMap.hasKey("language")) {
218-
settings.setLanguage(imaSettingsMap.getString("language"));
219-
}
220-
if (imaSettingsMap.hasKey("ppid")) {
221-
settings.setPpid(imaSettingsMap.getString("ppid"));
222-
}
223-
if (imaSettingsMap.hasKey("playerType")) {
224-
settings.setPlayerType(imaSettingsMap.getString("playerType"));
225-
}
226-
if (imaSettingsMap.hasKey("playerVersion")) {
227-
settings.setPlayerVersion(imaSettingsMap.getString("playerVersion"));
228-
}
229-
if (imaSettingsMap.hasKey("sessionId")) {
230-
settings.setSessionId(imaSettingsMap.getString("sessionId"));
231-
}
232-
if (imaSettingsMap.hasKey("debugMode")) {
233-
settings.setDebugMode(imaSettingsMap.getBoolean("debugMode"));
234-
}
235-
// Add other settings as needed
236-
237-
return settings;
238-
}
239151
}

0 commit comments

Comments
 (0)