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:
parent
9c38d9f4ef
commit
703ed43996
@ -11,9 +11,17 @@ import androidx.media3.exoplayer.ExoPlayer;
|
|||||||
import androidx.media3.exoplayer.source.ads.AdsLoader;
|
import androidx.media3.exoplayer.source.ads.AdsLoader;
|
||||||
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
|
import androidx.media3.exoplayer.source.ads.AdsMediaSource;
|
||||||
|
|
||||||
|
import com.google.ads.interactivemedia.v3.api.ImaSdkSettings;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
|
||||||
public class ImaAdsLoader implements AdsLoader {
|
public class ImaAdsLoader implements AdsLoader {
|
||||||
|
private final ImaSdkSettings imaSdkSettings;
|
||||||
|
|
||||||
|
public ImaAdsLoader(ImaSdkSettings imaSdkSettings) {
|
||||||
|
this.imaSdkSettings = imaSdkSettings;
|
||||||
|
}
|
||||||
|
|
||||||
public void setPlayer(ExoPlayer ignoredPlayer) {
|
public void setPlayer(ExoPlayer ignoredPlayer) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,6 +53,7 @@ public class ImaAdsLoader implements AdsLoader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class Builder {
|
public static class Builder {
|
||||||
|
private ImaSdkSettings imaSdkSettings;
|
||||||
public Builder(Context ignoredThemedReactContext) {
|
public Builder(Context ignoredThemedReactContext) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -56,6 +65,11 @@ public class ImaAdsLoader implements AdsLoader {
|
|||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Builder setImaSdkSettings(ImaSdkSettings imaSdkSettings) {
|
||||||
|
this.imaSdkSettings = imaSdkSettings;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
public ImaAdsLoader build() {
|
public ImaAdsLoader build() {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -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.AdError;
|
||||||
import com.google.ads.interactivemedia.v3.api.AdEvent;
|
import com.google.ads.interactivemedia.v3.api.AdEvent;
|
||||||
import com.google.ads.interactivemedia.v3.api.AdErrorEvent;
|
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 com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import java.net.CookieHandler;
|
import java.net.CookieHandler;
|
||||||
@ -251,6 +253,7 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
private boolean mReportBandwidth = false;
|
private boolean mReportBandwidth = false;
|
||||||
private boolean controls;
|
private boolean controls;
|
||||||
private Uri adTagUrl;
|
private Uri adTagUrl;
|
||||||
|
private String adLanguage;
|
||||||
|
|
||||||
private boolean showNotificationControls = false;
|
private boolean showNotificationControls = false;
|
||||||
// \ End props
|
// \ End props
|
||||||
@ -754,9 +757,13 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
.setEnableDecoderFallback(true)
|
.setEnableDecoderFallback(true)
|
||||||
.forceEnableMediaCodecAsynchronousQueueing();
|
.forceEnableMediaCodecAsynchronousQueueing();
|
||||||
|
|
||||||
|
ImaSdkSettings imaSdkSettings = ImaSdkFactory.getInstance().createImaSdkSettings();
|
||||||
|
imaSdkSettings.setLanguage(adLanguage);
|
||||||
|
|
||||||
// Create an AdsLoader.
|
// Create an AdsLoader.
|
||||||
adsLoader = new ImaAdsLoader
|
adsLoader = new ImaAdsLoader
|
||||||
.Builder(themedReactContext)
|
.Builder(themedReactContext)
|
||||||
|
.setImaSdkSettings(imaSdkSettings)
|
||||||
.setAdEventListener(this)
|
.setAdEventListener(this)
|
||||||
.setAdErrorListener(this)
|
.setAdErrorListener(this)
|
||||||
.build();
|
.build();
|
||||||
@ -1851,6 +1858,10 @@ public class ReactExoplayerView extends FrameLayout implements
|
|||||||
adTagUrl = uri;
|
adTagUrl = uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setAdLanguage(final String language) {
|
||||||
|
adLanguage = language;
|
||||||
|
}
|
||||||
|
|
||||||
public void setTextTracks(SideLoadedTextTrackList textTracks) {
|
public void setTextTracks(SideLoadedTextTrackList textTracks) {
|
||||||
this.textTracks = textTracks;
|
this.textTracks = textTracks;
|
||||||
reloadSource(); // FIXME Shall be moved inside source
|
reloadSource(); // FIXME Shall be moved inside source
|
||||||
|
@ -29,6 +29,7 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
|
|||||||
private const val REACT_CLASS = "RCTVideo"
|
private const val REACT_CLASS = "RCTVideo"
|
||||||
private const val PROP_SRC = "src"
|
private const val PROP_SRC = "src"
|
||||||
private const val PROP_AD_TAG_URL = "adTagUrl"
|
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_RESIZE_MODE = "resizeMode"
|
||||||
private const val PROP_REPEAT = "repeat"
|
private const val PROP_REPEAT = "repeat"
|
||||||
private const val PROP_SELECTED_AUDIO_TRACK = "selectedAudioTrack"
|
private const val PROP_SELECTED_AUDIO_TRACK = "selectedAudioTrack"
|
||||||
@ -110,6 +111,16 @@ class ReactExoplayerViewManager(private val config: ReactExoplayerConfig) : View
|
|||||||
videoView.setAdTagUrl(adTagUrl)
|
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)
|
@ReactProp(name = PROP_RESIZE_MODE)
|
||||||
fun setResizeMode(videoView: ReactExoplayerView, resizeMode: String) {
|
fun setResizeMode(videoView: ReactExoplayerView, resizeMode: String) {
|
||||||
when (resizeMode) {
|
when (resizeMode) {
|
||||||
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
@ -23,3 +23,16 @@ Example:
|
|||||||
onReceiveAdEvent={event => console.log(event)}
|
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"
|
||||||
|
...
|
||||||
|
```
|
||||||
|
@ -19,7 +19,12 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
func setUpAdsLoader() {
|
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
|
adsLoader.delegate = self
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -87,6 +87,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH
|
|||||||
|
|
||||||
/* IMA Ads */
|
/* IMA Ads */
|
||||||
private var _adTagUrl: String?
|
private var _adTagUrl: String?
|
||||||
|
private var _adLanguage: String?
|
||||||
#if USE_GOOGLE_IMA
|
#if USE_GOOGLE_IMA
|
||||||
private var _imaAdsManager: RCTIMAAdsManager!
|
private var _imaAdsManager: RCTIMAAdsManager!
|
||||||
/* Playhead used by the SDK to track content video progress and insert mid-rolls. */
|
/* 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
|
// MARK: - RCTIMAAdsManager
|
||||||
|
|
||||||
|
func getAdLanguage() -> String? {
|
||||||
|
return _adLanguage
|
||||||
|
}
|
||||||
|
|
||||||
func getAdTagUrl() -> String? {
|
func getAdTagUrl() -> String? {
|
||||||
return _adTagUrl
|
return _adTagUrl
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@
|
|||||||
RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary);
|
RCT_EXPORT_VIEW_PROPERTY(src, NSDictionary);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(drm, NSDictionary);
|
RCT_EXPORT_VIEW_PROPERTY(drm, NSDictionary);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(adTagUrl, NSString);
|
RCT_EXPORT_VIEW_PROPERTY(adTagUrl, NSString);
|
||||||
|
RCT_EXPORT_VIEW_PROPERTY(adLanguage, NSString);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(maxBitRate, float);
|
RCT_EXPORT_VIEW_PROPERTY(maxBitRate, float);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
|
RCT_EXPORT_VIEW_PROPERTY(resizeMode, NSString);
|
||||||
RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL);
|
RCT_EXPORT_VIEW_PROPERTY(repeat, BOOL);
|
||||||
|
@ -305,6 +305,7 @@ export type OnControlsVisibilityChange = Readonly<{
|
|||||||
export interface VideoNativeProps extends ViewProps {
|
export interface VideoNativeProps extends ViewProps {
|
||||||
src?: VideoSrc;
|
src?: VideoSrc;
|
||||||
adTagUrl?: string;
|
adTagUrl?: string;
|
||||||
|
adLanguage?: string;
|
||||||
allowsExternalPlayback?: boolean; // ios, true
|
allowsExternalPlayback?: boolean; // ios, true
|
||||||
disableFocus?: boolean; // android
|
disableFocus?: boolean; // android
|
||||||
maxBitRate?: Float;
|
maxBitRate?: Float;
|
||||||
|
@ -255,6 +255,7 @@ export interface ReactVideoProps extends ReactVideoEvents, ViewProps {
|
|||||||
drm?: Drm;
|
drm?: Drm;
|
||||||
style?: StyleProp<ViewStyle>;
|
style?: StyleProp<ViewStyle>;
|
||||||
adTagUrl?: string;
|
adTagUrl?: string;
|
||||||
|
adLanguage?: ISO639_1;
|
||||||
audioOutput?: AudioOutput; // Mobile
|
audioOutput?: AudioOutput; // Mobile
|
||||||
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
|
automaticallyWaitsToMinimizeStalling?: boolean; // iOS
|
||||||
bufferConfig?: BufferConfig; // Android
|
bufferConfig?: BufferConfig; // Android
|
||||||
|
Loading…
Reference in New Issue
Block a user