From 596c02d2b3b5175e1653844c39a47ecfd5e23163 Mon Sep 17 00:00:00 2001 From: Axel Vencatareddy Date: Sat, 2 Dec 2023 13:52:01 +0100 Subject: [PATCH] feat: add onAdError event listener (#3381) * feat: add onAdError event listener * feat: remove onAdError event listener and use already existing * feat: add ERROR event to docs --- .../media3/exoplayer/ima/ImaAdsLoader.java | 4 ++++ .../common/react/VideoEventEmitter.java | 14 ++++++++++++++ .../brentvatne/exoplayer/ReactExoplayerView.java | 13 +++++++++++-- .../ads/interactivemedia/v3/api/AdError.java | 10 ++++++++++ .../interactivemedia/v3/api/AdErrorEvent.java | 9 +++++++++ docs/pages/component/events.md | 1 + ios/Video/Features/RCTIMAAdsManager.swift | 16 +++++++++++++++- ios/Video/RCTVideo.swift | 6 +++--- src/Video.tsx | 4 ++-- src/types/Ads.ts | 4 ++++ 10 files changed, 73 insertions(+), 8 deletions(-) create mode 100644 android/src/main/java/com/google/ads/interactivemedia/v3/api/AdError.java create mode 100644 android/src/main/java/com/google/ads/interactivemedia/v3/api/AdErrorEvent.java diff --git a/android/src/main/java/androidx/media3/exoplayer/ima/ImaAdsLoader.java b/android/src/main/java/androidx/media3/exoplayer/ima/ImaAdsLoader.java index 148d5803..3119d09a 100644 --- a/android/src/main/java/androidx/media3/exoplayer/ima/ImaAdsLoader.java +++ b/android/src/main/java/androidx/media3/exoplayer/ima/ImaAdsLoader.java @@ -54,6 +54,10 @@ public class ImaAdsLoader implements AdsLoader { return this; } + public Builder setAdErrorListener(Object ignoredReactExoplayerView) { + return this; + } + public ImaAdsLoader build() { return null; } diff --git a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java index 8e818f6a..f119cb2f 100644 --- a/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java +++ b/android/src/main/java/com/brentvatne/common/react/VideoEventEmitter.java @@ -12,6 +12,7 @@ import com.facebook.react.bridge.ReactContext; import com.facebook.react.bridge.WritableArray; import com.facebook.react.bridge.WritableMap; import com.facebook.react.uimanager.events.RCTEventEmitter; +import com.google.ads.interactivemedia.v3.api.AdError; import java.io.PrintWriter; import java.io.StringWriter; @@ -434,6 +435,19 @@ public class VideoEventEmitter { receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map); } + public void receiveAdErrorEvent(AdError error) { + WritableMap map = Arguments.createMap(); + map.putString("event", "ERROR"); + + WritableMap dataMap = Arguments.createMap(); + dataMap.putString("message", error.getMessage()); + dataMap.putString("code", String.valueOf(error.getErrorCode())); + dataMap.putString("type", String.valueOf(error.getErrorType())); + map.putMap("data", dataMap); + + receiveEvent(EVENT_ON_RECEIVE_AD_EVENT, map); + } + private void receiveEvent(@VideoEvents String type, WritableMap event) { eventEmitter.receiveEvent(viewId, type, event); } diff --git a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java index e0b7d452..f8c2cc95 100644 --- a/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java +++ b/android/src/main/java/com/brentvatne/exoplayer/ReactExoplayerView.java @@ -108,6 +108,7 @@ import com.facebook.react.bridge.ReadableMap; import com.facebook.react.bridge.UiThreadUtil; import com.facebook.react.uimanager.ThemedReactContext; import com.google.ads.interactivemedia.v3.api.AdEvent; +import com.google.ads.interactivemedia.v3.api.AdErrorEvent; import com.google.common.collect.ImmutableList; import java.net.CookieHandler; @@ -132,7 +133,8 @@ public class ReactExoplayerView extends FrameLayout implements BandwidthMeter.EventListener, BecomingNoisyListener, DrmSessionEventListener, - AdEvent.AdEventListener { + AdEvent.AdEventListener, + AdErrorEvent.AdErrorListener { public static final double DEFAULT_MAX_HEAP_ALLOCATION_PERCENT = 1; public static final double DEFAULT_MIN_BACK_BUFFER_MEMORY_RESERVE = 0; @@ -621,8 +623,10 @@ public class ReactExoplayerView extends FrameLayout implements .setExtensionRendererMode(DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF); // Create an AdsLoader. - adsLoader = new ImaAdsLoader.Builder(themedReactContext) + adsLoader = new ImaAdsLoader + .Builder(themedReactContext) .setAdEventListener(this) + .setAdErrorListener(this) .build(); DefaultMediaSourceFactory mediaSourceFactory = new DefaultMediaSourceFactory(mediaDataSourceFactory); @@ -2103,4 +2107,9 @@ public class ReactExoplayerView extends FrameLayout implements eventEmitter.receiveAdEvent(adEvent.getType().name()); } } + + @Override + public void onAdError(AdErrorEvent adErrorEvent) { + eventEmitter.receiveAdErrorEvent(adErrorEvent.getError()); + } } diff --git a/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdError.java b/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdError.java new file mode 100644 index 00000000..86c29610 --- /dev/null +++ b/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdError.java @@ -0,0 +1,10 @@ +package com.google.ads.interactivemedia.v3.api; + +import androidx.annotation.InspectableProperty; + +public abstract class AdError { + public abstract InspectableProperty getErrorCode(); + public abstract InspectableProperty getErrorCodeNumber(); + public abstract InspectableProperty getErrorType(); + public abstract String getMessage(); +} diff --git a/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdErrorEvent.java b/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdErrorEvent.java new file mode 100644 index 00000000..f96418ff --- /dev/null +++ b/android/src/main/java/com/google/ads/interactivemedia/v3/api/AdErrorEvent.java @@ -0,0 +1,9 @@ +package com.google.ads.interactivemedia.v3.api; + +public abstract class AdErrorEvent { + public abstract AdError getError(); + + public interface AdErrorListener { + public void onAdError(AdErrorEvent adErrorEvent); + } +} diff --git a/docs/pages/component/events.md b/docs/pages/component/events.md index b093377e..638efcbf 100644 --- a/docs/pages/component/events.md +++ b/docs/pages/component/events.md @@ -370,6 +370,7 @@ Enum `AdEvent` possible values for [Android](https://developers.google.com/inter | `CONTENT_RESUME_REQUESTED` | Android | Fires when content should be resumed. This usually happens when an ad finishes or collapses. | | `CUEPOINTS_CHANGED` | iOS | Cuepoints changed for VOD stream (only used for dynamic ad insertion). | | `DURATION_CHANGE` | Android | Fires when the ad's duration changes. | +| `ERROR` | Android, iOS | Fires when an error occurred while loading the ad and prevent it from playing. | | `FIRST_QUARTILE` | Android, iOS | Fires when the ad playhead crosses first quartile. | | `IMPRESSION` | Android | Fires when the impression URL has been pinged. | | `INTERACTION` | Android | Fires when an ad triggers the interaction callback. Ad interactions contain an interaction ID string in the ad data. | diff --git a/ios/Video/Features/RCTIMAAdsManager.swift b/ios/Video/Features/RCTIMAAdsManager.swift index 4cb55fc4..e8f9b256 100644 --- a/ios/Video/Features/RCTIMAAdsManager.swift +++ b/ios/Video/Features/RCTIMAAdsManager.swift @@ -118,8 +118,22 @@ class RCTIMAAdsManager: NSObject, IMAAdsLoaderDelegate, IMAAdsManagerDelegate, I print("AdsManager error: " + error.message!) } + guard let _video = _video else {return} + + if _video.onReceiveAdEvent != nil { + _video.onReceiveAdEvent?([ + "event": "ERROR", + "data": [ + "message": error.message ?? "", + "code": error.code, + "type": error.type, + ], + "target": _video.reactTag! + ]) + } + // Fall back to playing content - _video?.setPaused(false) + _video.setPaused(false) } func adsManagerDidRequestContentPause(_ adsManager: IMAAdsManager) { diff --git a/ios/Video/RCTVideo.swift b/ios/Video/RCTVideo.swift index dfdde5c5..530fe1fd 100644 --- a/ios/Video/RCTVideo.swift +++ b/ios/Video/RCTVideo.swift @@ -146,7 +146,7 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH name: UIApplication.willResignActiveNotification, object: nil ) - + NotificationCenter.default.addObserver( self, selector: #selector(applicationDidBecomeActive(notification:)), @@ -1346,11 +1346,11 @@ class RCTVideo: UIView, RCTVideoPlayerViewControllerDelegate, RCTPlayerObserverH _playerObserver.removePlayerTimeObserver() } } - + @objc func handleAVPlayerAccess(notification:NSNotification!) { let accessLog:AVPlayerItemAccessLog! = (notification.object as! AVPlayerItem).accessLog() let lastEvent:AVPlayerItemAccessLogEvent! = accessLog.events.last - + onVideoBandwidthUpdate?(["bitrate": lastEvent.observedBitrate, "target": reactTag]) } } diff --git a/src/Video.tsx b/src/Video.tsx index 7f99651e..4a718e87 100644 --- a/src/Video.tsx +++ b/src/Video.tsx @@ -13,7 +13,6 @@ import NativeVideoComponent, { } from './VideoNativeComponent'; import type {StyleProp, ImageStyle, NativeSyntheticEvent} from 'react-native'; -import type {ReactVideoProps} from './types/video'; import {getReactTag, resolveAssetSourceForVideo} from './utils'; import {VideoManager} from './VideoNativeComponent'; import type { @@ -35,7 +34,8 @@ import type { OnVideoAspectRatioData, OnVideoErrorData, OnVideoTracksData, -} from './types/events'; + ReactVideoProps, +} from './types'; export type VideoSaveData = { uri: string; diff --git a/src/types/Ads.ts b/src/types/Ads.ts index 2d100e22..3da25f76 100644 --- a/src/types/Ads.ts +++ b/src/types/Ads.ts @@ -63,6 +63,10 @@ export enum AdEvent { * Android only: Fires when the ad's duration changes. */ DURATION_CHANGE = 'DURATION_CHANGE', + /** + * Fires when an error is encountered and the ad can't be played. + */ + ERROR = 'ERROR', /** * Fires when the ad playhead crosses first quartile. */