adwhale-sdk-react-native 2.7.204 → 2.7.400

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (109) hide show
  1. package/AdwhaleSdkReactNative.podspec +22 -0
  2. package/README.md +95 -91
  3. package/android/build.gradle +35 -1
  4. package/android/proguard-rules.pro +58 -0
  5. package/android/src/main/java/com/adwhalesdkreactnative/AdWhaleCustomNativeBinderFactory.java +18 -0
  6. package/android/src/main/java/com/adwhalesdkreactnative/AdwhaleSdkReactNativePackage.java +51 -6
  7. package/android/src/main/java/com/adwhalesdkreactnative/BinderFactory.java +9 -0
  8. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAdSettingModule.java +148 -49
  9. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAdView.java +46 -27
  10. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAppOpenAd.java +148 -0
  11. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationCustomNativeAdView.java +37 -46
  12. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationExitPopupAd.java +240 -0
  13. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationInterstitialAd.java +205 -0
  14. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationLoggerModule.java +2 -1
  15. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationNativeAdViewListenerBridge.java +83 -0
  16. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationRewardAd.java +341 -6
  17. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationTemplateNativeAdView.java +191 -70
  18. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationTransitionPopupAd.java +218 -0
  19. package/android/src/main/java/com/adwhalesdkreactnative/RNWrapperView.java +137 -0
  20. package/android/src/main/java/com/adwhalesdkreactnative/SimpleBinderFactory.java +1 -1
  21. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodArgConst.java +112 -0
  22. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodCallConst.java +113 -0
  23. package/android/src/main/java/com/adwhalesdkreactnative/common/RNMethodResultConst.java +28 -0
  24. package/build/generated/ios/ReactAppDependencyProvider.podspec +34 -0
  25. package/ios/AdWhaleNativeCustomViewFactoryRegistry.swift +56 -0
  26. package/ios/AdwhaleSdkReactNative.mm +96 -5
  27. package/ios/AdwhaleSdkReactNativeModule.swift +148 -0
  28. package/ios/RNAdWhaleMediationAdViewManager.m +38 -0
  29. package/ios/RNAdWhaleMediationAdViewManager.swift +14 -0
  30. package/ios/RNAdWhaleMediationAppOpenAd.m +18 -0
  31. package/ios/RNAdWhaleMediationAppOpenAd.swift +175 -0
  32. package/ios/RNAdWhaleMediationCustomNativeAdViewManager.m +22 -0
  33. package/ios/RNAdWhaleMediationCustomNativeAdViewManager.swift +239 -0
  34. package/ios/RNAdWhaleMediationInterstitialAd.m +18 -0
  35. package/ios/RNAdWhaleMediationInterstitialAd.swift +161 -0
  36. package/ios/RNAdWhaleMediationRewardAd.m +22 -0
  37. package/ios/RNAdWhaleMediationRewardAd.swift +206 -0
  38. package/ios/WhaleMediationBannerContainer.swift +182 -0
  39. package/lib/module/AdWhaleAdView.js +58 -3
  40. package/lib/module/AdWhaleAdView.js.map +1 -1
  41. package/lib/module/AdWhaleAppOpenAd.js +223 -25
  42. package/lib/module/AdWhaleAppOpenAd.js.map +1 -1
  43. package/lib/module/AdWhaleExitPopupAd.js +93 -0
  44. package/lib/module/AdWhaleExitPopupAd.js.map +1 -0
  45. package/lib/module/AdWhaleInterstitialAd.js +146 -22
  46. package/lib/module/AdWhaleInterstitialAd.js.map +1 -1
  47. package/lib/module/AdWhaleMediationAds.js +75 -4
  48. package/lib/module/AdWhaleMediationAds.js.map +1 -1
  49. package/lib/module/AdWhaleNativeCustomView.js +59 -8
  50. package/lib/module/AdWhaleNativeCustomView.js.map +1 -1
  51. package/lib/module/AdWhaleNativeTemplateView.js +37 -19
  52. package/lib/module/AdWhaleNativeTemplateView.js.map +1 -1
  53. package/lib/module/AdWhaleRewardAd.js +221 -24
  54. package/lib/module/AdWhaleRewardAd.js.map +1 -1
  55. package/lib/module/AdWhaleTransitionPopupAd.js +87 -0
  56. package/lib/module/AdWhaleTransitionPopupAd.js.map +1 -0
  57. package/lib/module/NativeAdwhaleSdkReactNative.js +16 -1
  58. package/lib/module/NativeAdwhaleSdkReactNative.js.map +1 -1
  59. package/lib/module/constants/AdWhaleMethodArgConst.js +53 -0
  60. package/lib/module/constants/AdWhaleMethodArgConst.js.map +1 -0
  61. package/lib/module/constants/AdWhaleMethodCallConst.js +56 -0
  62. package/lib/module/constants/AdWhaleMethodCallConst.js.map +1 -0
  63. package/lib/module/constants/AdWhaleMethodResultConst.js +16 -0
  64. package/lib/module/constants/AdWhaleMethodResultConst.js.map +1 -0
  65. package/lib/module/index.js +10 -1
  66. package/lib/module/index.js.map +1 -1
  67. package/lib/typescript/src/AdWhaleAdView.d.ts +20 -2
  68. package/lib/typescript/src/AdWhaleAdView.d.ts.map +1 -1
  69. package/lib/typescript/src/AdWhaleAppOpenAd.d.ts +10 -0
  70. package/lib/typescript/src/AdWhaleAppOpenAd.d.ts.map +1 -1
  71. package/lib/typescript/src/AdWhaleExitPopupAd.d.ts +36 -0
  72. package/lib/typescript/src/AdWhaleExitPopupAd.d.ts.map +1 -0
  73. package/lib/typescript/src/AdWhaleInterstitialAd.d.ts +16 -0
  74. package/lib/typescript/src/AdWhaleInterstitialAd.d.ts.map +1 -1
  75. package/lib/typescript/src/AdWhaleMediationAds.d.ts +13 -5
  76. package/lib/typescript/src/AdWhaleMediationAds.d.ts.map +1 -1
  77. package/lib/typescript/src/AdWhaleNativeCustomView.d.ts +7 -4
  78. package/lib/typescript/src/AdWhaleNativeCustomView.d.ts.map +1 -1
  79. package/lib/typescript/src/AdWhaleNativeTemplateView.d.ts +20 -7
  80. package/lib/typescript/src/AdWhaleNativeTemplateView.d.ts.map +1 -1
  81. package/lib/typescript/src/AdWhaleRewardAd.d.ts +63 -6
  82. package/lib/typescript/src/AdWhaleRewardAd.d.ts.map +1 -1
  83. package/lib/typescript/src/AdWhaleTransitionPopupAd.d.ts +33 -0
  84. package/lib/typescript/src/AdWhaleTransitionPopupAd.d.ts.map +1 -0
  85. package/lib/typescript/src/NativeAdwhaleSdkReactNative.d.ts +28 -6
  86. package/lib/typescript/src/NativeAdwhaleSdkReactNative.d.ts.map +1 -1
  87. package/lib/typescript/src/constants/AdWhaleMethodArgConst.d.ts +51 -0
  88. package/lib/typescript/src/constants/AdWhaleMethodArgConst.d.ts.map +1 -0
  89. package/lib/typescript/src/constants/AdWhaleMethodCallConst.d.ts +53 -0
  90. package/lib/typescript/src/constants/AdWhaleMethodCallConst.d.ts.map +1 -0
  91. package/lib/typescript/src/constants/AdWhaleMethodResultConst.d.ts +14 -0
  92. package/lib/typescript/src/constants/AdWhaleMethodResultConst.d.ts.map +1 -0
  93. package/lib/typescript/src/index.d.ts +10 -2
  94. package/lib/typescript/src/index.d.ts.map +1 -1
  95. package/package.json +1 -1
  96. package/src/AdWhaleAdView.tsx +92 -4
  97. package/src/AdWhaleAppOpenAd.ts +293 -38
  98. package/src/AdWhaleExitPopupAd.ts +183 -0
  99. package/src/AdWhaleInterstitialAd.ts +206 -36
  100. package/src/AdWhaleMediationAds.ts +108 -4
  101. package/src/AdWhaleNativeCustomView.tsx +85 -23
  102. package/src/AdWhaleNativeTemplateView.tsx +79 -42
  103. package/src/AdWhaleRewardAd.ts +317 -51
  104. package/src/AdWhaleTransitionPopupAd.ts +171 -0
  105. package/src/NativeAdwhaleSdkReactNative.ts +30 -6
  106. package/src/constants/AdWhaleMethodArgConst.ts +50 -0
  107. package/src/constants/AdWhaleMethodCallConst.ts +60 -0
  108. package/src/constants/AdWhaleMethodResultConst.ts +13 -0
  109. package/src/index.ts +35 -0
@@ -1,14 +1,15 @@
1
1
  package com.adwhalesdkreactnative;
2
2
 
3
+ import android.content.Context;
3
4
  import android.os.Handler;
4
5
  import android.os.Looper;
5
6
  import android.util.Log;
6
7
  import android.view.View;
8
+ import android.widget.FrameLayout;
7
9
 
8
10
  import androidx.annotation.NonNull;
9
11
  import androidx.annotation.Nullable;
10
12
 
11
- import com.facebook.react.bridge.Arguments;
12
13
  import com.facebook.react.bridge.WritableMap;
13
14
  import com.facebook.react.common.MapBuilder;
14
15
  import com.facebook.react.bridge.ReadableMap;
@@ -17,8 +18,10 @@ import com.facebook.react.uimanager.ThemedReactContext;
17
18
  import com.facebook.react.uimanager.annotations.ReactProp;
18
19
  import com.facebook.react.uimanager.events.RCTEventEmitter;
19
20
 
21
+ import com.adwhalesdkreactnative.common.RNMethodArgConst;
22
+ import com.adwhalesdkreactnative.common.RNMethodCallConst;
23
+
20
24
  import net.adwhale.sdk.mediation.ads.AdWhaleMediationNativeAdView;
21
- import net.adwhale.sdk.mediation.ads.AdWhaleMediationNativeAdViewListener;
22
25
  import net.adwhale.sdk.mediation.ads.ADWHALE_NATIVE_TEMPLATE;
23
26
  import net.adwhale.sdk.mediation.ads.AdWhaleMediationNativeTemplateStyle;
24
27
  import android.graphics.Color;
@@ -37,6 +40,11 @@ public class RNAdWhaleMediationTemplateNativeAdView extends SimpleViewManager<RN
37
40
  private ADWHALE_NATIVE_TEMPLATE template = ADWHALE_NATIVE_TEMPLATE.SMALL;
38
41
  private AdWhaleMediationNativeTemplateStyle templateStyle = null;
39
42
  private String placementName;
43
+ /** {@link #resetNativeAdChild} 후에도 region/gcoder를 다시 적용하기 위해 보관 */
44
+ @Nullable private String cachedRegion;
45
+ private boolean hasCachedGcoder;
46
+ private double cachedGcoderLt;
47
+ private double cachedGcoderLng;
40
48
 
41
49
  @NonNull
42
50
  @Override
@@ -58,42 +66,13 @@ public class RNAdWhaleMediationTemplateNativeAdView extends SimpleViewManager<RN
58
66
  AdWhaleMediationNativeAdView adWhaleMediationNativeAdView = getAdWhaleMediationNativeAdView(view);
59
67
  if (adWhaleMediationNativeAdView == null) return;
60
68
 
61
- adWhaleMediationNativeAdView.setAdWhaleMediationNativeAdViewListener(new AdWhaleMediationNativeAdViewListener() {
62
- @Override
63
- public void onNativeAdLoaded() {
64
- Log.d(REACT_CLASS, "onNativeAdLoaded");
65
- dispatchEvent(reactContext, view, "onAdLoaded", null);
66
- view.requestLayout();
67
- }
68
-
69
- @Override
70
- public void onNativeAdFailedToLoad(int code, String message) {
71
- Log.d(REACT_CLASS, "onNativeAdFailedToLoad: " + code + ", " + message);
72
- WritableMap event = Arguments.createMap();
73
- event.putInt("errorCode", code);
74
- event.putString("errorMessage", message);
75
- dispatchEvent(reactContext, view, "onAdFailedToLoad", event);
76
- }
77
-
78
- @Override
79
- public void onNativeAdShowFailed(int code, String message) {
80
- Log.d(REACT_CLASS, "onNativeAdShowFailed: " + code + ", " + message);
81
- WritableMap event = Arguments.createMap();
82
- event.putInt("errorCode", code);
83
- event.putString("errorMessage", message);
84
- dispatchEvent(reactContext, view, "onAdShowFailed", event);
85
- }
86
-
87
- @Override
88
- public void onNativeAdClicked() {
89
- // Not used in RN
90
- }
91
-
92
- @Override
93
- public void onNativeAdClosed() {
94
- // Not used in RN
95
- }
96
- });
69
+ adWhaleMediationNativeAdView.setAdWhaleMediationNativeAdViewListener(
70
+ new RNAdWhaleMediationNativeAdViewListenerBridge(REACT_CLASS, (eventName, payload) -> {
71
+ dispatchEvent(reactContext, view, eventName, payload);
72
+ if (RNMethodCallConst.ON_NATIVE_AD_LOADED.equals(eventName)) {
73
+ view.requestLayout();
74
+ }
75
+ }));
97
76
  }
98
77
 
99
78
  private void dispatchEvent(ThemedReactContext context, View view, String eventName, @Nullable WritableMap eventData) {
@@ -108,9 +87,11 @@ public class RNAdWhaleMediationTemplateNativeAdView extends SimpleViewManager<RN
108
87
  @Override
109
88
  public Map<String, Object> getExportedCustomDirectEventTypeConstants() {
110
89
  MapBuilder.Builder<String, Object> builder = MapBuilder.builder();
111
- builder.put("onAdLoaded", MapBuilder.of("registrationName", "onAdLoaded"));
112
- builder.put("onAdFailedToLoad", MapBuilder.of("registrationName", "onAdFailedToLoad"));
113
- builder.put("onAdShowFailed", MapBuilder.of("registrationName", "onAdShowFailed"));
90
+ builder.put(RNMethodCallConst.ON_NATIVE_AD_LOADED, MapBuilder.of(RNMethodArgConst.SEND_REGISTRATION_NAME, RNMethodCallConst.ON_NATIVE_AD_LOADED));
91
+ builder.put(RNMethodCallConst.ON_NATIVE_AD_FAILED_TO_LOAD, MapBuilder.of(RNMethodArgConst.SEND_REGISTRATION_NAME, RNMethodCallConst.ON_NATIVE_AD_FAILED_TO_LOAD));
92
+ builder.put(RNMethodCallConst.ON_NATIVE_AD_SHOW_FAILED, MapBuilder.of(RNMethodArgConst.SEND_REGISTRATION_NAME, RNMethodCallConst.ON_NATIVE_AD_SHOW_FAILED));
93
+ builder.put(RNMethodCallConst.ON_NATIVE_AD_CLICKED, MapBuilder.of(RNMethodArgConst.SEND_REGISTRATION_NAME, RNMethodCallConst.ON_NATIVE_AD_CLICKED));
94
+ builder.put(RNMethodCallConst.ON_NATIVE_AD_CLOSED, MapBuilder.of(RNMethodArgConst.SEND_REGISTRATION_NAME, RNMethodCallConst.ON_NATIVE_AD_CLOSED));
114
95
  return builder.build();
115
96
  }
116
97
 
@@ -123,6 +104,7 @@ public class RNAdWhaleMediationTemplateNativeAdView extends SimpleViewManager<RN
123
104
  @ReactProp(name = "region")
124
105
  public void setRegion(RNWrapperView view, String region) {
125
106
  Log.e(REACT_CLASS, "region: " + region);
107
+ this.cachedRegion = region;
126
108
  AdWhaleMediationNativeAdView adView = getAdWhaleMediationNativeAdView(view);
127
109
  if (adView != null) {
128
110
  adView.setRegion(region);
@@ -134,11 +116,17 @@ public class RNAdWhaleMediationTemplateNativeAdView extends SimpleViewManager<RN
134
116
  Log.e(REACT_CLASS, "gcoderMap: " + gcoderMap);
135
117
  AdWhaleMediationNativeAdView adView = getAdWhaleMediationNativeAdView(view);
136
118
 
137
- if (adView != null && gcoderMap != null) {
119
+ if (gcoderMap != null) {
138
120
  double lt = gcoderMap.hasKey("lt") ? gcoderMap.getDouble("lt") : 0.0;
139
121
  double lng = gcoderMap.hasKey("lng") ? gcoderMap.getDouble("lng") : 0.0;
140
-
141
- adView.setGcoder(lt, lng);
122
+ this.hasCachedGcoder = true;
123
+ this.cachedGcoderLt = lt;
124
+ this.cachedGcoderLng = lng;
125
+ if (adView != null) {
126
+ adView.setGcoder(lt, lng);
127
+ }
128
+ } else {
129
+ this.hasCachedGcoder = false;
142
130
  }
143
131
  }
144
132
 
@@ -278,11 +266,82 @@ public class RNAdWhaleMediationTemplateNativeAdView extends SimpleViewManager<RN
278
266
 
279
267
  @ReactProp(name = "placementUid")
280
268
  public void setPlacementUid(RNWrapperView view, String placementUid) {
281
- this.placementUid = placementUid;
269
+ String u = placementUid != null ? placementUid : "";
270
+ this.placementUid = u;
271
+ view.setStoredPlacementUid(u);
272
+ }
273
+
274
+ /**
275
+ * Fabric(New Arch)에서는 {@code findNodeHandle + dispatchViewManagerCommand}가
276
+ * shadow node 미준비 시 조용히 무시될 수 있어, prop 기반 트리거를 함께 둡니다.
277
+ * JS는 값이 바뀔 때마다 1씩 올립니다(0은 무시).
278
+ */
279
+ @ReactProp(name = "viewSyncKey", defaultInt = -1)
280
+ public void setViewSyncKey(RNWrapperView view, int viewSyncKey) {
281
+ int prev = view.getViewSyncKey();
282
+ if (viewSyncKey != -1 && prev != -1 && prev != viewSyncKey) {
283
+ view.resetRequestDedupeState();
284
+ view.setViewSyncKey(viewSyncKey);
285
+ view.setLastConsumedLoadRequestId(0);
286
+ view.setLastConsumedShowRequestId(0);
287
+ Log.d(REACT_CLASS, "viewSyncKey " + prev + " -> " + viewSyncKey + " (reset load/show dedupe)");
288
+ return;
289
+ }
290
+ if (viewSyncKey != -1) {
291
+ view.setViewSyncKey(viewSyncKey);
292
+ }
293
+ }
294
+
295
+ @ReactProp(name = "loadRequestId")
296
+ public void setLoadRequestId(RNWrapperView view, int loadRequestId) {
297
+ Log.d(REACT_CLASS, "setLoadRequestId(view=" + view.getId() + ", sync=" + view.getViewSyncKey() + ", id=" + loadRequestId + ")");
298
+ if (loadRequestId <= 0) {
299
+ // JS가 시그널을 0으로 리셋할 때마다 소비 카운터를 비움. Fabric이 네이티브 래퍼를 재사용하면
300
+ // 이전 reactTag에서 lastConsumed=1이 남아 동일 id=1 로드가 영구 스킵되는 문제를 막음.
301
+ view.setLastConsumedLoadRequestId(0);
302
+ view.clearLoadRequestDedupePair();
303
+ return;
304
+ }
305
+ // 동일 (viewSyncKey, loadRequestId) 조합만 스킵 — 템플릿 전환 후 다시 id=1이 와도 syncKey가 바뀌면 처리됨
306
+ if (view.shouldSkipLoadRequestDedupe(loadRequestId)) {
307
+ Log.d(REACT_CLASS, "setLoadRequestId skip duplicate (sync=" + view.getViewSyncKey() + ", id=" + loadRequestId + ")");
308
+ return;
309
+ }
310
+ view.markLoadRequestDeduped(loadRequestId);
311
+ view.setLastConsumedLoadRequestId(loadRequestId);
312
+ Log.d(REACT_CLASS, "loadRequestId prop -> schedule loadAd (id=" + loadRequestId + ")");
313
+ // 동일 레이아웃 패스에서 다른 @ReactProp(placementUid, template 등)이 아직 안 올 수 있음
314
+ view.post(() -> loadAd(view));
315
+ }
316
+
317
+ @ReactProp(name = "showRequestId")
318
+ public void setShowRequestId(RNWrapperView view, int showRequestId) {
319
+ Log.d(REACT_CLASS, "setShowRequestId(view=" + view.getId() + ", sync=" + view.getViewSyncKey() + ", id=" + showRequestId + ")");
320
+ if (showRequestId <= 0) {
321
+ view.setLastConsumedShowRequestId(0);
322
+ view.clearShowRequestDedupePair();
323
+ return;
324
+ }
325
+ if (view.shouldSkipShowRequestDedupe(showRequestId)) {
326
+ Log.d(REACT_CLASS, "setShowRequestId skip duplicate (sync=" + view.getViewSyncKey() + ", id=" + showRequestId + ")");
327
+ return;
328
+ }
329
+ view.markShowRequestDeduped(showRequestId);
330
+ view.setLastConsumedShowRequestId(showRequestId);
331
+ Log.d(REACT_CLASS, "showRequestId prop -> schedule showAd (id=" + showRequestId + ")");
332
+ view.post(() -> showAd(view));
282
333
  }
283
334
 
284
335
  @ReactProp(name = "template")
285
336
  public void setTemplate(RNWrapperView view, String templateName) {
337
+ if (templateName != null) {
338
+ String prev = view.getLastReactTemplateKey();
339
+ if (prev != null && !prev.equalsIgnoreCase(templateName)) {
340
+ view.setRecreateNativeAdChildBeforeNextLoad(true);
341
+ }
342
+ view.setLastReactTemplateKey(templateName);
343
+ view.setStoredTemplateKey(templateName);
344
+ }
286
345
  if ("SMALL".equalsIgnoreCase(templateName)) {
287
346
  this.template = ADWHALE_NATIVE_TEMPLATE.SMALL;
288
347
  } else if ("MEDIUM".equalsIgnoreCase(templateName)) {
@@ -292,45 +351,107 @@ public class RNAdWhaleMediationTemplateNativeAdView extends SimpleViewManager<RN
292
351
  }
293
352
  }
294
353
 
354
+ private static ADWHALE_NATIVE_TEMPLATE parseTemplateKey(@Nullable String templateName) {
355
+ if ("MEDIUM".equalsIgnoreCase(templateName)) {
356
+ return ADWHALE_NATIVE_TEMPLATE.MEDIUM;
357
+ }
358
+ if ("FULLSCREEN".equalsIgnoreCase(templateName)) {
359
+ return ADWHALE_NATIVE_TEMPLATE.FULLSCREEN;
360
+ }
361
+ return ADWHALE_NATIVE_TEMPLATE.SMALL;
362
+ }
363
+
364
+ /**
365
+ * 템플릿 전환·재로드·show() 이후 동일 뷰에서 다시 로드할 때 SDK가 콜백을 주지 않는 경우가 있어
366
+ * {@link AdWhaleMediationNativeAdView}를 새로 붙이고 리스너를 다시 연결합니다.
367
+ */
368
+ private void resetNativeAdChild(@NonNull RNWrapperView wrapper) {
369
+ Context ctx = wrapper.getContext();
370
+ if (!(ctx instanceof ThemedReactContext)) {
371
+ Log.e(REACT_CLASS, "resetNativeAdChild: context is not ThemedReactContext");
372
+ return;
373
+ }
374
+ ThemedReactContext themedContext = (ThemedReactContext) ctx;
375
+ AdWhaleMediationNativeAdView old = getAdWhaleMediationNativeAdView(wrapper);
376
+ if (old != null) {
377
+ try {
378
+ old.destroy();
379
+ } catch (Throwable t) {
380
+ Log.w(REACT_CLASS, "destroy before reload failed: " + t.getMessage());
381
+ }
382
+ wrapper.removeView(old);
383
+ }
384
+ AdWhaleMediationNativeAdView fresh = new AdWhaleMediationNativeAdView(themedContext);
385
+ FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
386
+ FrameLayout.LayoutParams.MATCH_PARENT,
387
+ FrameLayout.LayoutParams.WRAP_CONTENT);
388
+ wrapper.addView(fresh, 0, lp);
389
+ setAdListener(themedContext, wrapper);
390
+ }
391
+
392
+ private void applyCachedOptions(@NonNull AdWhaleMediationNativeAdView adView) {
393
+ if (cachedRegion != null) {
394
+ adView.setRegion(cachedRegion);
395
+ }
396
+ if (hasCachedGcoder) {
397
+ adView.setGcoder(cachedGcoderLt, cachedGcoderLng);
398
+ }
399
+ }
400
+
295
401
  private void loadAd(RNWrapperView view) {
296
402
  new Handler(Looper.getMainLooper()).post(() -> {
403
+ final String uid = view.getStoredPlacementUid();
404
+ final ADWHALE_NATIVE_TEMPLATE tmpl = parseTemplateKey(view.getStoredTemplateKey());
405
+ if (uid.isEmpty()) {
406
+ Log.e(REACT_CLASS, "Placement UID is empty on view. Cannot load ad.");
407
+ return;
408
+ }
409
+ // 매 로드마다 자식을 갈아끼우면, remount 직후 첫 로드에서 방금 만든 뷰가 깨지거나 SDK가 응답하지 않는 경우가 있음.
410
+ // show() 이후 재로드, 또는 동일 래퍼에서 template prop만 바뀐 경우에만 재생성.
411
+ if (view.isRecreateNativeAdChildBeforeNextLoad()) {
412
+ Log.d(REACT_CLASS, "Recreating native ad child before load (template change or post-show).");
413
+ resetNativeAdChild(view);
414
+ view.setRecreateNativeAdChildBeforeNextLoad(false);
415
+ }
297
416
  AdWhaleMediationNativeAdView adWhaleMediationNativeAdView = getAdWhaleMediationNativeAdView(view);
298
- if (adWhaleMediationNativeAdView != null) {
299
- if (placementUid == null || placementUid.isEmpty()) {
300
- Log.e(REACT_CLASS, "Placement UID is null or empty. Cannot load ad.");
301
- return;
302
- }
303
- if (template == null) {
304
- Log.e(REACT_CLASS, "Template is null. Cannot load ad.");
305
- return;
306
- }
307
- Log.d(REACT_CLASS, "Placement UID: " + placementUid);
308
- Log.d(REACT_CLASS, "Template: " + template.name());
309
- adWhaleMediationNativeAdView.setPlacementUid(placementUid);
310
- if (this.templateStyle != null) {
311
- adWhaleMediationNativeAdView.setTemplateStyle(this.templateStyle);
312
- }
313
- adWhaleMediationNativeAdView.loadAdWithTemplate(template);
314
- Log.d(REACT_CLASS, "Calling loadAdWithTemplate...");
315
- } else {
417
+ if (adWhaleMediationNativeAdView == null) {
316
418
  Log.e(REACT_CLASS, "adWhaleMediationNativeAdView is null. Cannot load ad.");
419
+ return;
317
420
  }
421
+ Log.d(REACT_CLASS, "Placement UID: " + uid + " (view template key=" + view.getStoredTemplateKey() + ")");
422
+ Log.d(REACT_CLASS, "Template: " + tmpl.name());
423
+ applyCachedOptions(adWhaleMediationNativeAdView);
424
+ adWhaleMediationNativeAdView.setPlacementUid(uid);
425
+ if (this.templateStyle != null) {
426
+ adWhaleMediationNativeAdView.setTemplateStyle(this.templateStyle);
427
+ }
428
+ adWhaleMediationNativeAdView.loadAdWithTemplate(tmpl);
429
+ Log.d(REACT_CLASS, "Calling loadAdWithTemplate...");
430
+ view.requestLayout();
318
431
  });
319
432
  }
320
433
 
321
434
  private void showAd(RNWrapperView view) {
322
- AdWhaleMediationNativeAdView adWhaleMediationNativeAdView = getAdWhaleMediationNativeAdView(view);
323
- if (adWhaleMediationNativeAdView != null) {
324
- adWhaleMediationNativeAdView.show();
325
- } else {
326
- Log.e(REACT_CLASS, "adWhaleMediationNativeAdView is null. Cannot show ad.");
327
- }
435
+ new Handler(Looper.getMainLooper()).post(() -> {
436
+ AdWhaleMediationNativeAdView adWhaleMediationNativeAdView = getAdWhaleMediationNativeAdView(view);
437
+ if (adWhaleMediationNativeAdView != null) {
438
+ adWhaleMediationNativeAdView.show();
439
+ view.setRecreateNativeAdChildBeforeNextLoad(true);
440
+ } else {
441
+ Log.e(REACT_CLASS, "adWhaleMediationNativeAdView is null. Cannot show ad.");
442
+ }
443
+ });
328
444
  }
329
445
 
330
446
  @Override
331
447
  public void onDropViewInstance(@NonNull RNWrapperView view) {
332
448
  super.onDropViewInstance(view);
333
- Log.d(REACT_CLASS, "onDropViewInstance called");
449
+ Log.d(REACT_CLASS, "onDropViewInstance reactTag=" + view.getId());
450
+ view.setLastConsumedLoadRequestId(0);
451
+ view.setLastConsumedShowRequestId(0);
452
+ view.resetRequestDedupeState();
453
+ view.setRecreateNativeAdChildBeforeNextLoad(false);
454
+ view.setLastReactTemplateKey(null);
334
455
  AdWhaleMediationNativeAdView adWhaleMediationNativeAdView = getAdWhaleMediationNativeAdView(view);
335
456
  if (adWhaleMediationNativeAdView != null) {
336
457
  adWhaleMediationNativeAdView.destroy();
@@ -0,0 +1,218 @@
1
+ package com.adwhalesdkreactnative;
2
+
3
+ import android.app.Activity;
4
+ import android.util.Log;
5
+
6
+ import androidx.annotation.NonNull;
7
+ import androidx.annotation.Nullable;
8
+ import androidx.fragment.app.FragmentActivity;
9
+ import androidx.fragment.app.FragmentManager;
10
+
11
+ import com.facebook.react.bridge.Arguments;
12
+ import com.facebook.react.bridge.ReactApplicationContext;
13
+ import com.facebook.react.bridge.ReactContextBaseJavaModule;
14
+ import com.facebook.react.bridge.ReactMethod;
15
+ import com.facebook.react.bridge.ReadableMap;
16
+ import com.facebook.react.bridge.UiThreadUtil;
17
+ import com.facebook.react.bridge.WritableMap;
18
+ import com.facebook.react.modules.core.DeviceEventManagerModule;
19
+
20
+ import net.adwhale.sdk.mediation.ads.ADWHALE_POPUP_AD_CLOSE_REASON;
21
+ import net.adwhale.sdk.mediation.ads.AdWhaleMediationTransitionPopupAd;
22
+ import net.adwhale.sdk.mediation.ads.AdWhaleMediationTransitionPopupAdListener;
23
+ import net.adwhale.sdk.utils.AdWhaleLog;
24
+
25
+ import java.util.HashMap;
26
+ import java.util.Map;
27
+
28
+ public class RNAdWhaleMediationTransitionPopupAd extends ReactContextBaseJavaModule implements AdWhaleMediationTransitionPopupAdListener {
29
+
30
+ public static final String REACT_CLASS_NAME = RNAdWhaleMediationTransitionPopupAd.class.getSimpleName();
31
+ private AdWhaleMediationTransitionPopupAd adWhaleMediationTransitionPopupAd;
32
+ private ReactApplicationContext reactContext;
33
+ private String placementUid;
34
+ private String placementName;
35
+ private String region;
36
+ private double lt;
37
+ private double lng;
38
+
39
+ public RNAdWhaleMediationTransitionPopupAd(ReactApplicationContext context) {
40
+ super(context);
41
+ reactContext = context;
42
+ }
43
+ @NonNull
44
+ @Override
45
+ public String getName() {
46
+ return REACT_CLASS_NAME;
47
+ }
48
+
49
+ @ReactMethod
50
+ public void setPlacementName(String placementName) {
51
+ Log.d(REACT_CLASS_NAME, "placementName: " + placementName);
52
+ this.placementName = placementName;
53
+ }
54
+
55
+ @ReactMethod
56
+ public void setRegion(String region) {
57
+ Log.d(REACT_CLASS_NAME, "region: " + region);
58
+ this.region = region;
59
+ }
60
+
61
+ @ReactMethod
62
+ public void setGcoder(ReadableMap gcoderMap) {
63
+ Log.d(REACT_CLASS_NAME, "gcoderMap: " + gcoderMap);
64
+
65
+ if (gcoderMap != null) {
66
+ double lt = gcoderMap.hasKey("lt") ? gcoderMap.getDouble("lt") : 0.0;
67
+ double lng = gcoderMap.hasKey("lng") ? gcoderMap.getDouble("lng") : 0.0;
68
+
69
+ this.lt = lt;
70
+ this.lng = lng;
71
+ }
72
+ }
73
+
74
+ @ReactMethod
75
+ public void loadAd(String placementUid) {
76
+ Log.e(REACT_CLASS_NAME, ".loadAd()");
77
+ this.placementUid = placementUid;
78
+ adWhaleMediationTransitionPopupAd = new AdWhaleMediationTransitionPopupAd(placementUid);
79
+ adWhaleMediationTransitionPopupAd.setAdWhaleMediationTransitionPopupAdListener(this);
80
+ adWhaleMediationTransitionPopupAd.setPlacementName(placementName);
81
+ adWhaleMediationTransitionPopupAd.setRegion(region);
82
+ adWhaleMediationTransitionPopupAd.setGcoder(lt, lng);
83
+
84
+ UiThreadUtil.runOnUiThread(() -> {
85
+ adWhaleMediationTransitionPopupAd.loadAd();
86
+ });
87
+ }
88
+
89
+ @ReactMethod
90
+ public void showAd() {
91
+ if (adWhaleMediationTransitionPopupAd == null) {
92
+ AdWhaleLog.d(REACT_CLASS_NAME +" Error showing transition popup - the transition popup ad wasn't loaded yet.");
93
+ return;
94
+ }
95
+
96
+ UiThreadUtil.runOnUiThread(() -> {
97
+ Activity activity = reactContext.getCurrentActivity();
98
+ if (activity == null) {
99
+ AdWhaleLog.d(REACT_CLASS_NAME + " Tried to show transition popup before activity was bound to the plugin.");
100
+ return;
101
+ }
102
+
103
+ try {
104
+ if (!(activity instanceof FragmentActivity)) {
105
+ AdWhaleLog.d(REACT_CLASS_NAME + " Activity must be FragmentActivity to show TransitionPopupAd.");
106
+ return;
107
+ }
108
+
109
+ FragmentManager fragmentManager =
110
+ ((FragmentActivity) activity).getSupportFragmentManager();
111
+ adWhaleMediationTransitionPopupAd.showAd(activity, fragmentManager);
112
+ } catch (Throwable t) {
113
+ AdWhaleLog.d(REACT_CLASS_NAME +".showAd() failed. " + t.getMessage());
114
+ }
115
+ });
116
+ }
117
+
118
+ @ReactMethod
119
+ public void destroy() {
120
+ try {
121
+ if (adWhaleMediationTransitionPopupAd != null) {
122
+ adWhaleMediationTransitionPopupAd.destroy();
123
+ }
124
+ } catch (Throwable t) {
125
+ AdWhaleLog.d(REACT_CLASS_NAME +".destroy() failed. " + t.getMessage());
126
+ } finally {
127
+ adWhaleMediationTransitionPopupAd = null;
128
+ }
129
+ }
130
+
131
+ @ReactMethod
132
+ public void resume() {
133
+ Activity activity = reactContext.getCurrentActivity();
134
+ if (activity == null) {
135
+ AdWhaleLog.d(REACT_CLASS_NAME + " Tried to resume transition popup before activity was bound to the plugin.");
136
+ return;
137
+ }
138
+
139
+ try {
140
+ if (adWhaleMediationTransitionPopupAd != null) {
141
+ adWhaleMediationTransitionPopupAd.resume(activity);
142
+ }
143
+ } catch (Throwable t) {
144
+ AdWhaleLog.d(REACT_CLASS_NAME +".resume() failed. " + t.getMessage());
145
+ } finally {
146
+ adWhaleMediationTransitionPopupAd = null;
147
+ }
148
+ }
149
+
150
+ @Override
151
+ public void onAdLoaded() {
152
+ Log.i(REACT_CLASS_NAME, "onAdLoaded()");
153
+ sendEvent("onTransitionPopupAdLoaded", null);
154
+ }
155
+
156
+ @Override
157
+ public void onAdLoadFailed(int statusCode, String message) {
158
+ Log.i(REACT_CLASS_NAME, "onAdLoadFailed(" + statusCode + ", " + message + ")");
159
+ WritableMap params = Arguments.createMap();
160
+ params.putInt("statusCode", statusCode);
161
+ params.putString("message", message);
162
+ sendEvent("onTransitionPopupAdLoadFailed", params);
163
+ }
164
+
165
+ @Override
166
+ public void onAdShowed() {
167
+ Log.i(REACT_CLASS_NAME, "onAdShowed()");
168
+ sendEvent("onTransitionPopupAdShowed", null);
169
+ }
170
+
171
+ @Override
172
+ public void onAdClicked() {
173
+ Log.i(REACT_CLASS_NAME, "onAdClicked()");
174
+ sendEvent("onTransitionPopupAdClicked", null);
175
+ }
176
+
177
+ @Override
178
+ public void onAdShowFailed(int statusCode, String message) {
179
+ Log.i(REACT_CLASS_NAME, "onAdShowFailed(" + statusCode + ", " + message + ")");
180
+ WritableMap params = Arguments.createMap();
181
+ params.putInt("statusCode", statusCode);
182
+ params.putString("message", message);
183
+ sendEvent("onTransitionPopupAdShowFailed", params);
184
+ }
185
+
186
+ @Override
187
+ public void onAdClosed(ADWHALE_POPUP_AD_CLOSE_REASON adwhalePopupAdCloseReason) {
188
+ Log.i(REACT_CLASS_NAME, "onAdClosed()");
189
+ WritableMap params = Arguments.createMap();
190
+ params.putInt("reasonId", adwhalePopupAdCloseReason.getIdx());
191
+ params.putString("reasonMessage", adwhalePopupAdCloseReason.getCloseReasonTypeString());
192
+ sendEvent("onTransitionPopupAdClosed", params);
193
+ }
194
+
195
+ @Nullable
196
+ @Override
197
+ public Map<String, Object> getConstants() {
198
+ final Map<String, Object> constants = new HashMap<>();
199
+ return constants;
200
+ }
201
+
202
+ private void sendEvent(String eventName, @Nullable WritableMap params) {
203
+ getReactApplicationContext()
204
+ .getJSModule(DeviceEventManagerModule.RCTDeviceEventEmitter.class)
205
+ .emit(eventName, params);
206
+ }
207
+
208
+ @ReactMethod
209
+ public void addListener(String eventName) {
210
+ // NativeEventEmitter 요구사항 (이벤트는 RCTDeviceEventEmitter로 전달)
211
+ }
212
+
213
+ @ReactMethod
214
+ public void removeListeners(Integer count) {
215
+ // NativeEventEmitter 요구사항
216
+ }
217
+
218
+ }