Skip to content

Commit

Permalink
feat: add android media view
Browse files Browse the repository at this point in the history
  • Loading branch information
wjaykim committed Nov 20, 2024
1 parent 25f32a3 commit 1b39cf8
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 11 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package io.invertase.googlemobileads

/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import android.annotation.SuppressLint
import com.facebook.react.bridge.ReactContext
import com.google.android.gms.ads.nativead.MediaView

@SuppressLint("ViewConstructor")
class ReactNativeGoogleMobileAdsMediaView(
private val context: ReactContext
): MediaView(context) {
fun setResponseId(responseId: String?) {
val nativeModule = context.getNativeModule(ReactNativeGoogleMobileAdsNativeModule.NAME) as ReactNativeGoogleMobileAdsNativeModule?
nativeModule?.getNativeAd(responseId ?: "")?.let {
this.mediaContent = it.mediaContent
requestLayout()
}
}

override fun requestLayout() {
super.requestLayout()
post(measureAndLayout)
}

private val measureAndLayout = Runnable {
measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
)
layout(left, top, right, bottom)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package io.invertase.googlemobileads

/*
* Copyright (c) 2016-present Invertase Limited & Contributors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this library except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/

import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.module.annotations.ReactModule
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.ViewGroupManager
import com.facebook.react.uimanager.ViewManagerDelegate
import com.facebook.react.uimanager.annotations.ReactProp
import com.facebook.react.viewmanagers.RNGoogleMobileAdsMediaViewManagerDelegate
import com.facebook.react.viewmanagers.RNGoogleMobileAdsMediaViewManagerInterface

@ReactModule(name = ReactNativeGoogleMobileAdsMediaViewManager.NAME)
class ReactNativeGoogleMobileAdsMediaViewManager(
reactContext: ReactApplicationContext
) : ViewGroupManager<ReactNativeGoogleMobileAdsMediaView>(reactContext),
RNGoogleMobileAdsMediaViewManagerInterface<ReactNativeGoogleMobileAdsMediaView> {
private val delegate: ViewManagerDelegate<ReactNativeGoogleMobileAdsMediaView> = RNGoogleMobileAdsMediaViewManagerDelegate(this)

override fun getDelegate(): ViewManagerDelegate<ReactNativeGoogleMobileAdsMediaView> = delegate

override fun getName(): String = NAME

override fun createViewInstance(context: ThemedReactContext): ReactNativeGoogleMobileAdsMediaView = ReactNativeGoogleMobileAdsMediaView(context)

@ReactProp(name = "responseId")
override fun setResponseId(mediaView: ReactNativeGoogleMobileAdsMediaView, responseId: String?) {
mediaView.setResponseId(responseId)
}

companion object {
const val NAME = "RNGoogleMobileAdsMediaView"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,10 @@ package io.invertase.googlemobileads
import android.annotation.SuppressLint
import android.widget.FrameLayout
import com.facebook.react.bridge.ReactContext
import com.facebook.react.uimanager.UIManagerHelper
import com.facebook.react.uimanager.common.UIManagerType
import com.facebook.react.views.view.ReactViewGroup
import com.google.android.gms.ads.nativead.MediaView
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdView
import kotlinx.coroutines.CoroutineScope
Expand All @@ -29,6 +32,7 @@ import kotlinx.coroutines.Job
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch


@SuppressLint("ViewConstructor")
class ReactNativeGoogleMobileAdsNativeAdView(
private val context: ReactContext
Expand All @@ -55,7 +59,8 @@ class ReactNativeGoogleMobileAdsNativeAdView(
}

fun registerAsset(assetKey: String, reactTag: Int) {
val assetView = context.fabricUIManager?.resolveView(reactTag) ?: return
val uiManager = UIManagerHelper.getUIManager(context, UIManagerType.FABRIC)
val assetView = uiManager?.resolveView(reactTag) ?: return
when (assetKey) {
"advertiser" -> nativeAdView.advertiserView = assetView
"body" -> nativeAdView.bodyView = assetView
Expand All @@ -66,6 +71,7 @@ class ReactNativeGoogleMobileAdsNativeAdView(
"starRating" -> nativeAdView.starRatingView = assetView
"icon" -> nativeAdView.iconView = assetView
"image" -> nativeAdView.imageView = assetView
"media" -> nativeAdView.mediaView = assetView as MediaView
}
reloadAd()
}
Expand All @@ -74,7 +80,21 @@ class ReactNativeGoogleMobileAdsNativeAdView(
reloadJob?.cancel()
reloadJob = CoroutineScope(Dispatchers.Main).launch {
delay(100)
requestLayout()
nativeAd?.let { nativeAdView.setNativeAd(it) }
}
}

override fun requestLayout() {
super.requestLayout()
post(measureAndLayout)
}

private val measureAndLayout = Runnable {
measure(
MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY)
)
layout(left, top, right, bottom)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReadableMap
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.MediaAspectRatio
import com.google.android.gms.ads.nativead.NativeAd
import com.google.android.gms.ads.nativead.NativeAdOptions

Expand All @@ -33,8 +34,20 @@ class ReactNativeGoogleMobileAdsNativeModule(
override fun getName() = NAME

override fun load(adUnitId: String, requestOptions: ReadableMap, promise: Promise) {
val mediaAspectRatio = if (requestOptions.hasKey("aspectRatio")) {
when (requestOptions.getInt("aspectRatio")) {
1 -> MediaAspectRatio.ANY
2 -> MediaAspectRatio.PORTRAIT
3 -> MediaAspectRatio.LANDSCAPE
4 -> MediaAspectRatio.SQUARE
else -> MediaAspectRatio.UNKNOWN
}
} else {
MediaAspectRatio.ANY
}
val nativeAdOptions = NativeAdOptions.Builder()
.setReturnUrlsForImageAssets(true)
// .setReturnUrlsForImageAssets(true)
.setMediaAspectRatio(mediaAspectRatio)
.build()
val adLoader = AdLoader.Builder(reactApplicationContext, adUnitId)
.withNativeAdOptions(nativeAdOptions)
Expand Down Expand Up @@ -63,6 +76,13 @@ class ReactNativeGoogleMobileAdsNativeModule(
} ?: run {
data.putNull("icon")
}
val mediaContent = Arguments.createMap()
nativeAd.mediaContent?.let {
mediaContent.putDouble("aspectRatio", it.aspectRatio.toDouble())
mediaContent.putBoolean("hasVideoContent", it.hasVideoContent())
mediaContent.putDouble("duration", it.duration.toDouble())
data.putMap("mediaContent", mediaContent)
}

promise.resolve(data)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
): List<ViewManager<*, *>> {
return listOf(
ReactNativeGoogleMobileAdsBannerAdViewManager(),
ReactNativeGoogleMobileAdsNativeAdViewManager(reactContext)
ReactNativeGoogleMobileAdsNativeAdViewManager(reactContext),
ReactNativeGoogleMobileAdsMediaViewManager(reactContext)
)
}

Expand Down Expand Up @@ -59,7 +60,6 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
ReactNativeAppModule.NAME,
false, // canOverrideExistingModule
false, // needsEagerInit
false, // hasConstants
false, // isCxxModule
false, // isTurboModule
)
Expand All @@ -70,7 +70,6 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
false,
false,
false,
false,
isTurboModule,
)
moduleInfos[ReactNativeGoogleMobileAdsConsentModule.NAME] =
Expand All @@ -81,7 +80,6 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
false,
false,
false,
false,
)
moduleInfos[ReactNativeGoogleMobileAdsAppOpenModule.NAME] =
ReactModuleInfo(
Expand All @@ -91,7 +89,6 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
false,
false,
false,
false,
)
moduleInfos[ReactNativeGoogleMobileAdsInterstitialModule.NAME] =
ReactModuleInfo(
Expand All @@ -101,7 +98,6 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
false,
false,
false,
false,
)
moduleInfos[ReactNativeGoogleMobileAdsRewardedModule.NAME] =
ReactModuleInfo(
Expand All @@ -111,7 +107,6 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
false,
false,
false,
false,
)
moduleInfos[ReactNativeGoogleMobileAdsRewardedInterstitialModule.NAME] =
ReactModuleInfo(
Expand All @@ -121,7 +116,6 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
false,
false,
false,
false,
)
moduleInfos[ReactNativeGoogleMobileAdsNativeModule.NAME] =
ReactModuleInfo(
Expand All @@ -130,7 +124,6 @@ class ReactNativeGoogleMobileAdsPackage : TurboReactPackage() {
false,
false,
false,
false,
isTurboModule,
)
moduleInfos
Expand Down
33 changes: 33 additions & 0 deletions ios/RNGoogleMobileAds/RNGoogleMobileAdsNativeModule.mm
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ - (void)load:(NSString *)adUnitId
@"mediaContent" : @{
@"aspectRatio" : @(nativeAd.mediaContent.aspectRatio),
@"hasVideoContent" : @(nativeAd.mediaContent.hasVideoContent),
@"duration" : @(nativeAd.mediaContent.duration)
}
});
}];
Expand All @@ -104,6 +105,8 @@ - (GADNativeAd *)nativeAdForResponseId:(NSString *)responseId {

@end

#pragma mark - RNGMANativeAdHolder

@implementation RNGMANativeAdHolder {
GADAdLoader *_adLoader;
GAMRequest *_adRequest;
Expand Down Expand Up @@ -149,9 +152,12 @@ - (void)loadWithCompletionHandler:(RNGMANativeAdLoadCompletionHandler)completion
[_adLoader loadRequest:_adRequest];
}

#pragma mark - GADNativeAdLoaderDelegate

- (void)adLoader:(nonnull GADAdLoader *)adLoader
didReceiveNativeAd:(nonnull GADNativeAd *)nativeAd {
_nativeAd = nativeAd;
_nativeAd.delegate = self;
_completionHandler(nativeAd, nil);
}

Expand All @@ -160,6 +166,33 @@ - (void)adLoader:(nonnull GADAdLoader *)adLoader
_completionHandler(nil, error);
}

#pragma mark - GADNativeAdDelegate

- (void)nativeAdDidRecordImpression:(GADNativeAd *)nativeAd {
// The native ad was shown.
}

- (void)nativeAdDidRecordClick:(GADNativeAd *)nativeAd {
// The native ad was clicked on.
}

- (void)nativeAdWillPresentScreen:(GADNativeAd *)nativeAd {
// The native ad will present a full screen view.
}

- (void)nativeAdWillDismissScreen:(GADNativeAd *)nativeAd {
// The native ad will dismiss a full screen view.
}

- (void)nativeAdDidDismissScreen:(GADNativeAd *)nativeAd {
// The native ad did dismiss a full screen view.
}

- (void)nativeAdWillLeaveApplication:(GADNativeAd *)nativeAd {
// The native ad will cause the app to become inactive and
// open a new app.
}

@end

#endif

0 comments on commit 1b39cf8

Please sign in to comment.