feat: add ads localize (#4113)

* add prop adLanguage; add docs

* add native code ios&android for adLanguage props

* add missing function to adsLoader and imafactory

* Update docs/pages/component/ads.md

Language correction

Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>

---------

Co-authored-by: Guy <guyha@reshet.tv>
Co-authored-by: Olivier Bouillet <62574056+freeboub@users.noreply.github.com>
This commit is contained in:
Guy Haguy 2024-08-29 13:30:05 +03:00 committed by GitHub
parent 9c38d9f4ef
commit 703ed43996
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 109 additions and 1 deletions

View File

@ -11,9 +11,17 @@ import androidx.media3.exoplayer.ExoPlayer;
import androidx.media3.exoplayer.source.ads.AdsLoader;
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
import java.io.IOException;
public class ImaAdsLoader implements AdsLoader {
private final ImaSdkSettings imaSdkSettings;
public ImaAdsLoader(ImaSdkSettings imaSdkSettings) {
this.imaSdkSettings = imaSdkSettings;
}
public void setPlayer(ExoPlayer ignoredPlayer) {
}
@ -45,6 +53,7 @@ public class ImaAdsLoader implements AdsLoader {
}
public static class Builder {
private ImaSdkSettings imaSdkSettings;
public Builder(Context ignoredThemedReactContext) {
}
@ -56,6 +65,11 @@ public class ImaAdsLoader implements AdsLoader {
return this;
}
public Builder setImaSdkSettings(ImaSdkSettings imaSdkSettings) {
this.imaSdkSettings = imaSdkSettings;
return this;
}
public ImaAdsLoader build() {
return null;
}

View File

@ -133,6 +133,8 @@ import com.facebook.react.uimanager.ThemedReactContext;
import com.google.ads.interactivemedia.v3.api.AdError;
import com.google.ads.interactivemedia.v3.api.AdEvent;
import com.google.ads.interactivemedia.v3.api.AdErrorEvent;
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
import com.google.ads.interactivemedia.v3.api.ImaSdkFactory;
import com.google.common.collect.ImmutableList;
import java.net.CookieHandler;
@ -251,6 +253,7 @@ public class ReactExoplayerView extends FrameLayout implements
private boolean mReportBandwidth = false;
private boolean controls;
private Uri adTagUrl;
private String adLanguage;
private boolean showNotificationControls = false;
// \ End props
@ -754,9 +757,13 @@ public class ReactExoplayerView extends FrameLayout implements
.setEnableDecoderFallback(true)
.forceEnableMediaCodecAsynchronousQueueing();
ImaSdkSettings imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
imaSdkSettings.setLanguage(adLanguage);
// Create an AdsLoader.
adsLoader = new ImaAdsLoader
.Builder(themedReactContext)
.setImaSdkSettings(imaSdkSettings)
.setAdEventListener(this)
.setAdErrorListener(this)
.build();
@ -1851,6 +1858,10 @@ public class ReactExoplayerView extends FrameLayout implements
adTagUrl = uri;
}
public void setAdLanguage(final String language) {
adLanguage = language;
}
public void setTextTracks(SideLoadedTextTrackList textTracks) {
this.textTracks = textTracks;
reloadSource(); // FIXME Shall be moved inside source

View File

@ -29,6 +29,7 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
private const val REACT_CLASS = "RCTVideo"
private const val PROP_SRC = "src"
private const val PROP_AD_TAG_URL = "adTagUrl"
private const val PROP_AD_LANGUAGE = "adLanguage"
private const val PROP_RESIZE_MODE = "resizeMode"
private const val PROP_REPEAT = "repeat"
private const val PROP_SELECTED_AUDIO_TRACK = "selectedAudioTrack"
@ -110,6 +111,16 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
videoView.setAdTagUrl(adTagUrl)
}
@ReactProp(name = PROP_AD_LANGUAGE)
fun setAdLanguage(videoView: ReactExoplayerView, languageString: String?) {
if (TextUtils.isEmpty(languageString)) {
videoView.setAdLanguage(null) // Maybe "en" default?
return
}
videoView.setAdLanguage(languageString)
}
@ReactProp(name = PROP_RESIZE_MODE)
fun setResizeMode(videoView: ReactExoplayerView, resizeMode: String) {
when (resizeMode) {

View File

@ -0,0 +1,22 @@
package com.google.ads.interactivemedia.v3.api;
public abstract class ImaSdkFactory {
private static ImaSdkFactory instance;
public abstract ImaSdkSettings createImaSdkSettings();
public static ImaSdkFactory getInstance() {
if (instance == null) {
instance = new ConcreteImaSdkFactory();
}
return instance;
}
}
class ConcreteImaSdkFactory extends ImaSdkFactory {
@Override
public ImaSdkSettings createImaSdkSettings() {
return new ConcreteImaSdkSettings();
}
}

View File

@ -0,0 +1,24 @@
package com.google.ads.interactivemedia.v3.api;
import androidx.annotation.InspectableProperty;
public abstract class ImaSdkSettings {
public abstract String getLanguage();
public abstract void setLanguage(String language);
}
// Concrete Implementation
class ConcreteImaSdkSettings extends ImaSdkSettings {
private String language;
@Override
public String getLanguage() {
return language;
}
@Override
public void setLanguage(String language) {
this.language = language;
}
}

View File

@ -23,3 +23,16 @@ Example:
onReceiveAdEvent={event => console.log(event)}
...
```
### Localization
To change the language of the IMA SDK, you need to pass `adLanguage` prop to `Video` component. List of supported languages, you can find [here](https://developers.google.com/interactive-media-ads/docs/sdks/android/client-side/localization#locale-codes)
By default, ios will use system language and android will use `en`
Example:
```jsx
...
adLanguage="fr"
...
```

View File

@ -19,7 +19,12 @@
}
func setUpAdsLoader() {
adsLoader = IMAAdsLoader(settings: nil)
guard let _video else { return }
let settings = IMASettings()
if let adLanguage = _video.getAdLanguage() {
settings.language = adLanguage
}
adsLoader = IMAAdsLoader(settings: settings)
adsLoader.delegate = self
}

View File

@ -87,6 +87,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
/* IMA Ads */
private var _adTagUrl: String?
private var _adLanguage: String?
#if USE_GOOGLE_IMA
private var _imaAdsManager: RCTIMAAdsManager!
/* Playhead used by the SDK to track content video progress and insert mid-rolls. */
@ -1222,6 +1223,10 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
// MARK: - RCTIMAAdsManager
func getAdLanguage() -> String? {
return _adLanguage
}
func getAdTagUrl() -> String? {
return _adTagUrl
}

View File

@ -6,6 +6,7 @@
RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary);
RCT_EXPORT_VIEW_PROPERTY(drm, NSDictionary);
RCT_EXPORT_VIEW_PROPERTY(adTagUrl, NSString);
RCT_EXPORT_VIEW_PROPERTY(adLanguage, NSString);
RCT_EXPORT_VIEW_PROPERTY(maxBitRate, float);
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL);

View File

@ -305,6 +305,7 @@ export type OnControlsVisibilityChange = Readonly<{
export interface VideoNativeProps extends ViewProps {
src?: VideoSrc;
adTagUrl?: string;
adLanguage?: string;
allowsExternalPlayback?: boolean; // ios, true
disableFocus?: boolean; // android
maxBitRate?: Float;

View File

@ -255,6 +255,7 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
drm?: Drm;
style?: StyleProp<ViewStyle>;
adTagUrl?: string;
adLanguage?: ISO639_1;
audioOutput?: AudioOutput; // Mobile
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
bufferConfig?: BufferConfig; // Android