adwhale-sdk-react-native 2.7.202 → 2.7.204

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 (41) hide show
  1. package/README.md +29 -6
  2. package/android/build.gradle +15 -15
  3. package/android/proguard-rules.pro +0 -111
  4. package/android/src/main/java/com/adwhalesdkreactnative/AdwhaleSdkReactNativePackage.java +37 -0
  5. package/android/src/main/java/com/adwhalesdkreactnative/NativeAdBinderFactory.java +19 -0
  6. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAdSettingModule.java +10 -2
  7. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationAdView.java +15 -4
  8. package/android/src/main/java/com/adwhalesdkreactnative/RNAdWhaleMediationCustomNativeAdView.java +186 -196
  9. package/android/src/main/java/com/adwhalesdkreactnative/SimpleBinderFactory.java +55 -0
  10. package/lib/module/AdWhaleAdView.js +44 -0
  11. package/lib/module/AdWhaleAdView.js.map +1 -0
  12. package/lib/module/{AdWhaleMediationSdk.js → AdWhaleMediationAds.js} +14 -14
  13. package/lib/module/AdWhaleMediationAds.js.map +1 -0
  14. package/lib/module/NativeAdwhaleSdkReactNative.js.map +1 -1
  15. package/lib/module/index.js +9 -9
  16. package/lib/module/index.js.map +1 -1
  17. package/lib/typescript/src/AdWhaleAdView.d.ts +51 -0
  18. package/lib/typescript/src/AdWhaleAdView.d.ts.map +1 -0
  19. package/lib/typescript/src/{AdWhaleMediationSdk.d.ts → AdWhaleMediationAds.d.ts} +4 -3
  20. package/lib/typescript/src/AdWhaleMediationAds.d.ts.map +1 -0
  21. package/lib/typescript/src/AdWhaleNativeCustomView.d.ts +1 -1
  22. package/lib/typescript/src/AdWhaleNativeCustomView.d.ts.map +1 -1
  23. package/lib/typescript/src/NativeAdwhaleSdkReactNative.d.ts +6 -1
  24. package/lib/typescript/src/NativeAdwhaleSdkReactNative.d.ts.map +1 -1
  25. package/lib/typescript/src/index.d.ts +3 -3
  26. package/lib/typescript/src/index.d.ts.map +1 -1
  27. package/package.json +4 -4
  28. package/plugin/index.js +14 -367
  29. package/src/AdWhaleAdView.tsx +90 -0
  30. package/src/{AdWhaleMediationSdk.ts → AdWhaleMediationAds.ts} +15 -15
  31. package/src/AdWhaleNativeCustomView.tsx +1 -1
  32. package/src/NativeAdwhaleSdkReactNative.ts +7 -1
  33. package/src/index.ts +12 -12
  34. package/android/src/main/res/layout/custom_native_ad_layout.xml +0 -58
  35. package/lib/module/AdWhaleBannerView.js +0 -20
  36. package/lib/module/AdWhaleBannerView.js.map +0 -1
  37. package/lib/module/AdWhaleMediationSdk.js.map +0 -1
  38. package/lib/typescript/src/AdWhaleBannerView.d.ts +0 -25
  39. package/lib/typescript/src/AdWhaleBannerView.d.ts.map +0 -1
  40. package/lib/typescript/src/AdWhaleMediationSdk.d.ts.map +0 -1
  41. package/src/AdWhaleBannerView.tsx +0 -61
@@ -0,0 +1,51 @@
1
+ import React from 'react';
2
+ import type { StyleProp, ViewStyle } from 'react-native';
3
+ /**
4
+ * AdWhale 배너 광고 사이즈 enum
5
+ * Android SDK의 ADWHALE_AD_SIZE enum과 매핑됩니다.
6
+ */
7
+ export declare enum AdWhaleAdSize {
8
+ /** 표준 배너 (320x50) */
9
+ BANNER_320x50 = "BANNER320x50",
10
+ /** 큰 배너 (320x100) */
11
+ BANNER_320x100 = "BANNER320x100",
12
+ /** 중간 직사각형 (300x250) */
13
+ BANNER_300x250 = "BANNER300x250",
14
+ /** 정사각형 (250x250) */
15
+ BANNER_250x250 = "BANNER250x250",
16
+ /** 적응형 앵커 배너 - adaptiveAnchorWidth와 함께 사용 */
17
+ ADAPTIVE_ANCHOR = "ADAPTIVE_ANCHOR"
18
+ }
19
+ export interface AdWhaleBannerError {
20
+ statusCode: number;
21
+ message: string;
22
+ }
23
+ export interface AdWhaleBannerGcoder {
24
+ lt: number;
25
+ lng: number;
26
+ }
27
+ export interface AdWhaleBannerViewProps {
28
+ style?: StyleProp<ViewStyle>;
29
+ placementUid: string;
30
+ placementName?: string;
31
+ region?: string;
32
+ gcoder?: AdWhaleBannerGcoder;
33
+ /**
34
+ * 배너 광고 사이즈
35
+ * @example adSize={AdWhaleAdSize.BANNER_320x50}
36
+ */
37
+ adSize: AdWhaleAdSize;
38
+ /**
39
+ * ADAPTIVE_ANCHOR 사이즈 사용 시 배너의 너비(dp)를 지정합니다.
40
+ * 0을 입력하면 디바이스 전체 가로 길이가 적용됩니다.
41
+ * @default 0 (디바이스 전체 너비)
42
+ * @example adaptiveAnchorWidth={360}
43
+ */
44
+ adaptiveAnchorWidth?: number;
45
+ loadAd?: boolean;
46
+ onAdLoaded?: () => void;
47
+ onAdLoadFailed?: (event: AdWhaleBannerError) => void;
48
+ onAdClicked?: () => void;
49
+ }
50
+ export declare const AdWhaleAdView: React.FC<AdWhaleBannerViewProps>;
51
+ //# sourceMappingURL=AdWhaleAdView.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdWhaleAdView.d.ts","sourceRoot":"","sources":["../../../src/AdWhaleAdView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAC;AAE1B,OAAO,KAAK,EAAwB,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAE/E;;;GAGG;AACH,oBAAY,aAAa;IACvB,qBAAqB;IACrB,aAAa,iBAAiB;IAC9B,qBAAqB;IACrB,cAAc,kBAAkB;IAChC,wBAAwB;IACxB,cAAc,kBAAkB;IAChC,qBAAqB;IACrB,cAAc,kBAAkB;IAChC,6CAA6C;IAC7C,eAAe,oBAAoB;CACpC;AAED,MAAM,WAAW,kBAAkB;IACjC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,mBAAmB,CAAC;IAC7B;;;OAGG;IACH,MAAM,EAAE,aAAa,CAAC;IACtB;;;;;OAKG;IACH,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,MAAM,CAAC,EAAE,OAAO,CAAC;IAEjB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAC;IACrD,WAAW,CAAC,EAAE,MAAM,IAAI,CAAC;CAC1B;AAYD,eAAO,MAAM,aAAa,EAAE,KAAK,CAAC,EAAE,CAAC,sBAAsB,CAsB1D,CAAC"}
@@ -1,5 +1,6 @@
1
- export declare const AdWhaleMediationSdk: {
2
- initialize(): Promise<number>;
1
+ import { type InitializeResult } from './NativeAdwhaleSdkReactNative';
2
+ export declare const AdWhaleMediationAds: {
3
+ initialize(): Promise<InitializeResult>;
3
4
  setLoggerEnabled(enabled: boolean): void;
4
5
  getLogLevel(): Promise<string>;
5
6
  setCoppa(enabled: boolean): void;
@@ -15,4 +16,4 @@ export declare const AdWhaleMediationSdk: {
15
16
  resetGdprConsentStatus(): void;
16
17
  setGdpr(consent: boolean): void;
17
18
  };
18
- //# sourceMappingURL=AdWhaleMediationSdk.d.ts.map
19
+ //# sourceMappingURL=AdWhaleMediationAds.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AdWhaleMediationAds.d.ts","sourceRoot":"","sources":["../../../src/AdWhaleMediationAds.ts"],"names":[],"mappings":"AAEA,OAAoC,EAAE,KAAK,gBAAgB,EAAE,MAAM,+BAA+B,CAAC;AAmCnG,eAAO,MAAM,mBAAmB;kBAChB,OAAO,CAAC,gBAAgB,CAAC;8BAOb,OAAO;mBAIlB,OAAO,CAAC,MAAM,CAAC;sBASZ,OAAO;0BASH,OAAO,CAAC;QAAE,SAAS,EAAE,OAAO,CAAC;QAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC;wBAUlD,OAAO,CAAC;QAC1B,KAAK,EAAE,OAAO,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,mBAAmB,EAAE,OAAO,CAAC;KAC9B,CAAC;;qBAmBe,OAAO;CAQzB,CAAC"}
@@ -11,10 +11,10 @@ export interface AdWhaleNativeCustomGcoder {
11
11
  export interface AdWhaleNativeCustomViewProps {
12
12
  style?: StyleProp<ViewStyle>;
13
13
  placementUid: string;
14
+ factoryId: string;
14
15
  placementName?: string;
15
16
  region?: string;
16
17
  gcoder?: AdWhaleNativeCustomGcoder;
17
- layoutName: string;
18
18
  onAdLoaded?: () => void;
19
19
  onAdFailedToLoad?: (event: AdWhaleNativeCustomError) => void;
20
20
  }
@@ -1 +1 @@
1
- {"version":3,"file":"AdWhaleNativeCustomView.d.ts","sourceRoot":"","sources":["../../../src/AdWhaleNativeCustomView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAEvE,OAAO,KAAK,EAEV,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,yBAAyB,CAAC;IACnC,UAAU,EAAE,MAAM,CAAC;IAEnB,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,IAAI,IAAI,CAAC;CAChB;AAcD,eAAO,MAAM,uBAAuB,gHA8ClC,CAAC"}
1
+ {"version":3,"file":"AdWhaleNativeCustomView.d.ts","sourceRoot":"","sources":["../../../src/AdWhaleNativeCustomView.tsx"],"names":[],"mappings":"AACA,OAAO,KAAkD,MAAM,OAAO,CAAC;AAEvE,OAAO,KAAK,EAEV,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAEtB,MAAM,WAAW,wBAAwB;IACvC,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,yBAAyB;IACxC,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;CACb;AAED,MAAM,WAAW,4BAA4B;IAC3C,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,yBAAyB,CAAC;IAEnC,UAAU,CAAC,EAAE,MAAM,IAAI,CAAC;IACxB,gBAAgB,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,KAAK,IAAI,CAAC;CAC9D;AAED,MAAM,WAAW,yBAAyB;IACxC,MAAM,IAAI,IAAI,CAAC;IACf,MAAM,IAAI,IAAI,CAAC;CAChB;AAcD,eAAO,MAAM,uBAAuB,gHA8ClC,CAAC"}
@@ -1,6 +1,11 @@
1
1
  import { type TurboModule } from 'react-native';
2
+ export interface InitializeResult {
3
+ statusCode: number;
4
+ message: string;
5
+ isSuccess: boolean;
6
+ }
2
7
  export interface Spec extends TurboModule {
3
- initialize(): Promise<number>;
8
+ initialize(): Promise<InitializeResult>;
4
9
  setCoppa(isEnabled: boolean): void;
5
10
  requestGdprConsent(): Promise<{
6
11
  isSuccess: boolean;
@@ -1 +1 @@
1
- {"version":3,"file":"NativeAdwhaleSdkReactNative.d.ts","sourceRoot":"","sources":["../../../src/NativeAdwhaleSdkReactNative.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC,CAAC;IAC9B,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,kBAAkB,IAAI,OAAO,CAAC;QAC5B,SAAS,EAAE,OAAO,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,gBAAgB,IAAI,OAAO,CAAC;QAC1B,KAAK,EAAE,OAAO,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,mBAAmB,EAAE,OAAO,CAAC;KAC9B,CAAC,CAAC;IACH,sBAAsB,IAAI,IAAI,CAAC;IAC/B,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACjC;;AAED,wBAA+E"}
1
+ {"version":3,"file":"NativeAdwhaleSdkReactNative.d.ts","sourceRoot":"","sources":["../../../src/NativeAdwhaleSdkReactNative.ts"],"names":[],"mappings":"AAAA,OAAO,EAAuB,KAAK,WAAW,EAAE,MAAM,cAAc,CAAC;AAErE,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;CACpB;AAED,MAAM,WAAW,IAAK,SAAQ,WAAW;IACvC,UAAU,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACxC,QAAQ,CAAC,SAAS,EAAE,OAAO,GAAG,IAAI,CAAC;IACnC,kBAAkB,IAAI,OAAO,CAAC;QAC5B,SAAS,EAAE,OAAO,CAAC;QACnB,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC,CAAC;IACH,gBAAgB,IAAI,OAAO,CAAC;QAC1B,KAAK,EAAE,OAAO,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;QACb,mBAAmB,EAAE,OAAO,CAAC;KAC9B,CAAC,CAAC;IACH,sBAAsB,IAAI,IAAI,CAAC;IAC/B,OAAO,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI,CAAC;CACjC;;AAED,wBAA+E"}
@@ -1,6 +1,6 @@
1
- export { AdWhaleMediationSdk } from './AdWhaleMediationSdk';
2
- export { AdWhaleBannerView, } from './AdWhaleBannerView';
3
- export type { AdWhaleBannerSize, AdWhaleBannerError, AdWhaleBannerViewProps, } from './AdWhaleBannerView';
1
+ export { AdWhaleMediationAds } from './AdWhaleMediationAds';
2
+ export { AdWhaleAdView, AdWhaleAdSize, } from './AdWhaleAdView';
3
+ export type { AdWhaleBannerError, AdWhaleBannerViewProps, } from './AdWhaleAdView';
4
4
  export { AdWhaleInterstitialAd, } from './AdWhaleInterstitialAd';
5
5
  export type { AdWhaleInterstitialErrorEvent, AdWhaleInterstitialEvents, } from './AdWhaleInterstitialAd';
6
6
  export { AdWhaleRewardAd, } from './AdWhaleRewardAd';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAY5D,OAAO,EACL,iBAAiB,GAClB,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EACV,iBAAiB,EACjB,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,qBAAqB,CAAC;AAI7B,OAAO,EACL,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,6BAA6B,EAC7B,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EACL,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EACL,uBAAuB,GACxB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,yBAAyB,EACzB,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AAMnC,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/index.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC;AAY5D,OAAO,EACL,aAAa,EACb,aAAa,GACd,MAAM,iBAAiB,CAAC;AACzB,YAAY,EACV,kBAAkB,EAClB,sBAAsB,GACvB,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EACL,qBAAqB,GACtB,MAAM,yBAAyB,CAAC;AACjC,YAAY,EACV,6BAA6B,EAC7B,yBAAyB,GAC1B,MAAM,yBAAyB,CAAC;AAIjC,OAAO,EACL,eAAe,GAChB,MAAM,mBAAmB,CAAC;AAC3B,YAAY,EACV,uBAAuB,EACvB,8BAA8B,EAC9B,mBAAmB,GACpB,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EACL,gBAAgB,GACjB,MAAM,oBAAoB,CAAC;AAC5B,YAAY,EACV,wBAAwB,EACxB,oBAAoB,GACrB,MAAM,oBAAoB,CAAC;AAI5B,OAAO,EACL,uBAAuB,GACxB,MAAM,2BAA2B,CAAC;AACnC,YAAY,EACV,yBAAyB,EACzB,4BAA4B,EAC5B,wBAAwB,GACzB,MAAM,2BAA2B,CAAC;AAMnC,cAAc,6BAA6B,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "adwhale-sdk-react-native",
3
- "version": "2.7.202",
3
+ "version": "2.7.204",
4
4
  "description": "Adwhale SDK React Native library for integrating Adwhale advertising mediation SDK into React Native applications.",
5
5
  "main": "./lib/module/index.js",
6
6
  "react-native": "./lib/module/index.js",
@@ -59,14 +59,14 @@
59
59
  ],
60
60
  "repository": {
61
61
  "type": "git",
62
- "url": "git+https://github.com/dev-adwhale/adwhale-sdk-react-native.git"
62
+ "url": "git+https://github.com/dev-adwhale/ADwhaleMediationSDK-ReactNative.git"
63
63
  },
64
64
  "author": "adwhale_sdk <adwhale_sdk@fsn.co.kr> (https://github.com/dev-adwhale)",
65
65
  "license": "MIT",
66
66
  "bugs": {
67
- "url": "https://github.com/dev-adwhale/adwhale-sdk-react-native/issues"
67
+ "url": "https://github.com/dev-adwhale/ADwhaleMediationSDK-ReactNative/issues"
68
68
  },
69
- "homepage": "https://github.com/dev-adwhale/adwhale-sdk-react-native#readme",
69
+ "homepage": "https://adwhale.gitbook.io/adwhale-mediation-sdk/react-native/sdk",
70
70
  "publishConfig": {
71
71
  "registry": "https://registry.npmjs.org/"
72
72
  },
package/plugin/index.js CHANGED
@@ -1,369 +1,16 @@
1
- /**
2
- * AdWhale Expo Config Plugin (Most Compatible)
3
- *
4
- * 목적:
5
- * - Expo Bare / Managed / prebuild / EAS Dev Client 환경에서
6
- * AdWhale Android SDK 의존성을 정상적으로 resolve 하기 위해
7
- * Maven Repository들을 자동으로 Gradle 설정에 추가한다.
8
- *
9
- * 왜 withDangerousMod?
10
- * - 일부 Expo/@expo config-plugins 버전에서는 Android 전용 helper(mod)
11
- * (예: withAndroidBuildGradle, withAndroidProjectBuildGradle)가 존재하지 않는다.
12
- * - helper에 의존하면 TypeError가 발생할 수 있다.
13
- * - withDangerousMod는 폭넓게 제공되며, 파일을 직접 수정하는 방식이라 호환성이 가장 높다.
14
- *
15
- * 적용 정책:
16
- * 1) android/settings.gradle
17
- * - rootProject.name 바로 다음 줄에
18
- * dependencyResolutionManagement { repositories { ... } } 추가/보강
19
- *
20
- * 2) android/build.gradle (project-level)
21
- * - allprojects { repositories { ... } } 안에만 Maven repo 추가
22
- * - buildscript.repositories에는 절대 추가하지 않음
23
- *
24
- * 3) 단, settings.gradle에
25
- * repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
26
- * 가 존재하면:
27
- * - project-level repo 사용이 금지될 수 있으므로
28
- * - build.gradle 수정은 생략
29
- */
30
-
31
- const fs = require('fs');
32
- const path = require('path');
33
- const { createRunOncePlugin, withDangerousMod } = require('expo/config-plugins');
1
+ // plugin/index.js
2
+ const { createRunOncePlugin } = require('expo/config-plugins');
34
3
  const pkg = require('../package.json');
35
4
 
36
- /**
37
- * 추가할 Maven Repository 목록
38
- */
39
- const MAVEN_REPOSITORIES = [
40
- {
41
- comment: 'AdWhale SDK Repository Public Access Info',
42
- url: 'https://dev-adwhale.github.io/adwhale-sdk-android-maven/maven-repo',
43
- },
44
- {
45
- comment: 'Cauly SDK Repository Public Access Info',
46
- url: 'https://cauly.github.io/cauly-sdk-android-maven/maven-repo',
47
- },
48
- {
49
- comment: 'Admize SDK Repository Public Access Info',
50
- url: 'https://cauly.github.io/admize-sdk-android-maven/maven-repo',
51
- },
52
- {
53
- comment: 'AdFit SDK Repository Public Access Info',
54
- url: 'https://devrepo.kakao.com/nexus/content/groups/public/',
55
- },
56
- ];
57
-
58
- /**
59
- * 중복 삽입 방지를 위한 마커
60
- */
61
- const MARKER_BEGIN = '// [AdWhale] BEGIN Maven Repositories';
62
- const MARKER_END = '// [AdWhale] END Maven Repositories';
63
-
64
- function readTextIfExists(filePath) {
65
- if (!fs.existsSync(filePath)) return null;
66
- return fs.readFileSync(filePath, 'utf8');
67
- }
68
-
69
- function writeText(filePath, contents) {
70
- fs.writeFileSync(filePath, contents, 'utf8');
71
- }
72
-
73
- /**
74
- * settings.gradle에 FAIL_ON_PROJECT_REPOS가 있으면
75
- * project-level repo를 금지할 수 있으므로 build.gradle 수정은 생략한다.
76
- */
77
- function hasFailOnProjectRepos(contents) {
78
- return /repositoriesMode\.set\s*\(\s*RepositoriesMode\.FAIL_ON_PROJECT_REPOS\s*\)/.test(
79
- contents
80
- );
81
- }
82
-
83
- /**
84
- * Maven repo 블록 생성 (마커 포함)
85
- */
86
- function generateReposBlock(indent = ' ') {
87
- const body = MAVEN_REPOSITORIES.map(
88
- (repo) => `${indent}// ${repo.comment}\n${indent}maven { url "${repo.url}" }`
89
- ).join('\n\n');
90
-
91
- return `${indent}${MARKER_BEGIN}\n\n${body}\n\n${indent}${MARKER_END}`;
92
- }
93
-
94
- /**
95
- * repositories { ... } 내부에 google(), mavenCentral()이 없으면 추가해준다.
96
- * (resolve 기본 repo 안전장치)
97
- */
98
- function ensureGoogleAndMavenCentral(reposBlock, indent = ' ') {
99
- if (!/google\(\)/.test(reposBlock)) {
100
- reposBlock = reposBlock.replace(/repositories\s*\{\s*/m, (m) => `${m}\n${indent}google()`);
101
- }
102
-
103
- if (!/mavenCentral\(\)/.test(reposBlock)) {
104
- if (/google\(\)/.test(reposBlock)) {
105
- reposBlock = reposBlock.replace(/(google\(\))/m, `$1\n${indent}mavenCentral()`);
106
- } else {
107
- reposBlock = reposBlock.replace(
108
- /repositories\s*\{\s*/m,
109
- (m) => `${m}\n${indent}mavenCentral()`
110
- );
111
- }
112
- }
113
-
114
- return reposBlock;
115
- }
116
-
117
- /**
118
- * '{'의 매칭 '}' 범위를 찾아서 {start,end} 반환 (end inclusive)
119
- * - 중첩 brace를 고려하여 깊이(depth)로 스캔
120
- */
121
- function extractBraceRange(text, openBraceIndex) {
122
- if (openBraceIndex < 0 || openBraceIndex >= text.length) return null;
123
- if (text[openBraceIndex] !== '{') return null;
124
-
125
- let depth = 1;
126
- let i = openBraceIndex;
127
-
128
- while (i + 1 < text.length && depth > 0) {
129
- i++;
130
- const ch = text[i];
131
- if (ch === '{') depth++;
132
- else if (ch === '}') depth--;
133
- }
134
-
135
- if (depth !== 0) return null;
136
- return { start: openBraceIndex, end: i };
137
- }
138
-
139
- /**
140
- * android/settings.gradle 패치
141
- *
142
- * 목표:
143
- * - rootProject.name 바로 다음 줄에 dependencyResolutionManagement 블록을 추가한다.
144
- * - 이미 DRM이 존재하면:
145
- * - repositories 블록이 없으면 추가
146
- * - 있으면 missing repo만 추가
147
- */
148
- function patchSettingsGradle(contents) {
149
- const failOnProjectRepos = hasFailOnProjectRepos(contents);
150
-
151
- // 이미 적용되어 있으면 스킵
152
- if (contents.includes(MARKER_BEGIN)) {
153
- return { contents, failOnProjectRepos, changed: false };
154
- }
155
-
156
- // DRM 자체가 없으면 rootProject.name 다음에 삽입
157
- if (!/dependencyResolutionManagement\s*\{/.test(contents)) {
158
- const drmBlock =
159
- `\n\ndependencyResolutionManagement {\n` +
160
- ` repositories {\n` +
161
- ` google()\n` +
162
- ` mavenCentral()\n\n` +
163
- `${generateReposBlock(' ')}\n` +
164
- ` }\n` +
165
- `}\n`;
166
-
167
- const rootNameRegex = /(^\s*rootProject\.name\s*=\s*['"][^'"]+['"]\s*$)/m;
168
- const match = contents.match(rootNameRegex);
169
-
170
- if (match) {
171
- const insertAt = match.index + match[0].length;
172
- const newContents = contents.slice(0, insertAt) + drmBlock + contents.slice(insertAt);
173
- return { contents: newContents, failOnProjectRepos, changed: true };
174
- }
175
-
176
- // rootProject.name이 없으면 파일 끝에 append
177
- return { contents: contents + drmBlock, failOnProjectRepos, changed: true };
178
- }
179
-
180
- // DRM은 있으나 repositories 블록이 없으면 추가
181
- const drmHasRepos = /dependencyResolutionManagement\s*\{[\s\S]*?repositories\s*\{/.test(
182
- contents
183
- );
184
- if (!drmHasRepos) {
185
- const newContents = contents.replace(
186
- /(dependencyResolutionManagement\s*\{)/m,
187
- `$1\n repositories {\n google()\n mavenCentral()\n\n${generateReposBlock(
188
- ' '
189
- )}\n }\n`
190
- );
191
- return { contents: newContents, failOnProjectRepos, changed: true };
192
- }
193
-
194
- // DRM.repositories 존재: missing repo만 추가
195
- const missing = MAVEN_REPOSITORIES.filter((r) => !contents.includes(r.url));
196
- if (missing.length === 0) {
197
- return { contents, failOnProjectRepos, changed: false };
198
- }
199
-
200
- const indent = ' ';
201
- const body = missing
202
- .map((r) => `${indent}// ${r.comment}\n${indent}maven { url "${r.url}" }`)
203
- .join('\n\n');
204
- const block = `${indent}${MARKER_BEGIN}\n\n${body}\n\n${indent}${MARKER_END}`;
205
-
206
- let newContents = contents;
207
-
208
- // 가장 무난한 위치: repositories 내 mavenCentral() 다음
209
- if (/mavenCentral\(\)/.test(newContents)) {
210
- newContents = newContents.replace(/(mavenCentral\(\)\s*)/m, `$1\n\n${block}\n`);
211
- } else {
212
- // mavenCentral이 아예 없으면 repositories { 다음에 삽입
213
- newContents = newContents.replace(/(repositories\s*\{\s*)/m, `$1\n${block}\n`);
214
- }
215
-
216
- return { contents: newContents, failOnProjectRepos, changed: true };
217
- }
218
-
219
- /**
220
- * android/build.gradle(project-level) 패치
221
- *
222
- * 목표:
223
- * - buildscript.repositories는 절대 건드리지 않는다.
224
- * - allprojects { repositories { ... } } 블록 "안"에만 repo를 삽입한다.
225
- *
226
- * 삽입 위치 정책:
227
- * - jitpack이 있으면: jitpack 다음 줄에 삽입 (원하는 형태)
228
- * - jitpack이 없으면: repositories 블록 닫기 직전에 삽입
229
- */
230
- function patchProjectBuildGradle(contents) {
231
- // 이미 적용되어 있으면 스킵
232
- if (contents.includes(MARKER_BEGIN)) return { contents, changed: false };
233
-
234
- // allprojects 없으면 append
235
- if (!/allprojects\s*\{/.test(contents)) {
236
- const block =
237
- `\n\nallprojects {\n` +
238
- ` repositories {\n` +
239
- ` google()\n` +
240
- ` mavenCentral()\n` +
241
- ` maven { url 'https://www.jitpack.io' }\n\n` +
242
- `${generateReposBlock(' ')}\n` +
243
- ` }\n` +
244
- `}\n`;
245
- return { contents: contents + block, changed: true };
246
- }
247
-
248
- // allprojects 헤더와 '{' 찾기
249
- const apHeader = contents.match(/allprojects\s*\{/m);
250
- if (!apHeader) return { contents, changed: false };
251
-
252
- const apOpen = apHeader.index + apHeader[0].length - 1; // '{'
253
- const apRange = extractBraceRange(contents, apOpen);
254
- if (!apRange) return { contents, changed: false };
255
-
256
- const apText = contents.slice(apHeader.index, apRange.end + 1); // "allprojects { ... }"
257
-
258
- // allprojects 안에 repositories가 없으면 삽입
259
- if (!/repositories\s*\{/.test(apText)) {
260
- const insertion =
261
- `\n repositories {\n` +
262
- ` google()\n` +
263
- ` mavenCentral()\n` +
264
- ` maven { url 'https://www.jitpack.io' }\n\n` +
265
- `${generateReposBlock(' ')}\n` +
266
- ` }\n`;
267
-
268
- const newApText = apText.replace(/allprojects\s*\{\s*/m, (m) => m + insertion);
269
-
270
- const newContents =
271
- contents.slice(0, apHeader.index) + newApText + contents.slice(apRange.end + 1);
272
-
273
- return { contents: newContents, changed: true };
274
- }
275
-
276
- // allprojects.repositories 헤더와 '{' 찾기 (apText 내부 인덱스)
277
- const repoHeader = apText.match(/repositories\s*\{/m);
278
- if (!repoHeader) return { contents, changed: false };
279
-
280
- const repoOpenInAp = repoHeader.index + repoHeader[0].length - 1;
281
- const repoRangeInAp = extractBraceRange(apText, repoOpenInAp);
282
- if (!repoRangeInAp) return { contents, changed: false };
283
-
284
- let repoBlockText = apText.slice(repoHeader.index, repoRangeInAp.end + 1); // "repositories { ... }"
285
-
286
- // 안전장치: 기본 repo 보장
287
- repoBlockText = ensureGoogleAndMavenCentral(repoBlockText, ' ');
288
-
289
- // missing만 추가
290
- const missing = MAVEN_REPOSITORIES.filter((r) => !repoBlockText.includes(r.url));
291
- if (missing.length === 0) return { contents, changed: false };
292
-
293
- const indent = ' ';
294
- const body = missing
295
- .map((r) => `${indent}// ${r.comment}\n${indent}maven { url "${r.url}" }`)
296
- .join('\n\n');
297
- const markerBlock = `${indent}${MARKER_BEGIN}\n\n${body}\n\n${indent}${MARKER_END}`;
298
-
299
- const jitpackRegex = /maven\s*\{\s*url\s*['"]https:\/\/www\.jitpack\.io['"]\s*\}/m;
300
-
301
- if (jitpackRegex.test(repoBlockText)) {
302
- // jitpack 다음에 삽입
303
- repoBlockText = repoBlockText.replace(jitpackRegex, (m) => `${m}\n\n${markerBlock}`);
304
- } else {
305
- // repositories 닫기 직전에 삽입
306
- repoBlockText = repoBlockText.replace(/\}\s*$/m, `\n\n${markerBlock}\n}`);
307
- }
308
-
309
- // apText 안에서 repositories 블록 교체
310
- const newApText =
311
- apText.slice(0, repoHeader.index) + repoBlockText + apText.slice(repoRangeInAp.end + 1);
312
-
313
- // 전체 파일에서 allprojects 블록 교체
314
- const newContents =
315
- contents.slice(0, apHeader.index) + newApText + contents.slice(apRange.end + 1);
316
-
317
- return { contents: newContents, changed: true };
318
- }
319
-
320
- /**
321
- * 실제 적용 플러그인
322
- *
323
- * - android 폴더가 생성된 이후(prebuild) 시점에 실행되며,
324
- * - settings.gradle 먼저 수정하여 정책을 판단하고,
325
- * - FAIL_ON_PROJECT_REPOS가 아니면 build.gradle도 수정한다.
326
- */
327
- function withAdwhaleMavenRepositories(config) {
328
- return withDangerousMod(config, [
329
- 'android',
330
- async (config) => {
331
- const projectRoot = config.modRequest.projectRoot;
332
- const androidDir = path.join(projectRoot, 'android');
333
-
334
- const settingsGradlePath = path.join(androidDir, 'settings.gradle');
335
- const buildGradlePath = path.join(androidDir, 'build.gradle');
336
-
337
- // 1) settings.gradle 패치
338
- const settingsText = readTextIfExists(settingsGradlePath);
339
- let failOnProjectRepos = false;
340
-
341
- if (settingsText) {
342
- const res = patchSettingsGradle(settingsText);
343
- failOnProjectRepos = res.failOnProjectRepos;
344
-
345
- if (res.changed) {
346
- writeText(settingsGradlePath, res.contents);
347
- }
348
- }
349
-
350
- // 2) FAIL_ON_PROJECT_REPOS이면 build.gradle 수정 생략
351
- if (failOnProjectRepos) {
352
- return config;
353
- }
354
-
355
- // 3) build.gradle(project) 패치
356
- const buildText = readTextIfExists(buildGradlePath);
357
- if (buildText) {
358
- const res2 = patchProjectBuildGradle(buildText);
359
- if (res2.changed) {
360
- writeText(buildGradlePath, res2.contents);
361
- }
362
- }
363
-
364
- return config;
365
- },
366
- ]);
367
- }
368
-
369
- module.exports = createRunOncePlugin(withAdwhaleMavenRepositories, pkg.name, pkg.version);
5
+ // Expo config plugin (no-op)
6
+ // - Expo prebuild 호출되지만, 실제로는 config를 그대로 반환만 함.
7
+ const withAdwhaleSdkReactNative = (config, props = {}) => {
8
+ // 나중에 필요해지면 여기서 AndroidManifest / build.gradle 수정 로직만 추가하면 됨.
9
+ return config;
10
+ };
11
+
12
+ module.exports = createRunOncePlugin(
13
+ withAdwhaleSdkReactNative,
14
+ pkg.name,
15
+ pkg.version
16
+ );
@@ -0,0 +1,90 @@
1
+ // src/AdWhaleAdView.tsx
2
+ import React from 'react';
3
+ import { requireNativeComponent } from 'react-native';
4
+ import type { NativeSyntheticEvent, StyleProp, ViewStyle } from 'react-native';
5
+
6
+ /**
7
+ * AdWhale 배너 광고 사이즈 enum
8
+ * Android SDK의 ADWHALE_AD_SIZE enum과 매핑됩니다.
9
+ */
10
+ export enum AdWhaleAdSize {
11
+ /** 표준 배너 (320x50) */
12
+ BANNER_320x50 = 'BANNER320x50',
13
+ /** 큰 배너 (320x100) */
14
+ BANNER_320x100 = 'BANNER320x100',
15
+ /** 중간 직사각형 (300x250) */
16
+ BANNER_300x250 = 'BANNER300x250',
17
+ /** 정사각형 (250x250) */
18
+ BANNER_250x250 = 'BANNER250x250',
19
+ /** 적응형 앵커 배너 - adaptiveAnchorWidth와 함께 사용 */
20
+ ADAPTIVE_ANCHOR = 'ADAPTIVE_ANCHOR',
21
+ }
22
+
23
+ export interface AdWhaleBannerError {
24
+ statusCode: number;
25
+ message: string;
26
+ }
27
+
28
+ export interface AdWhaleBannerGcoder {
29
+ lt: number;
30
+ lng: number;
31
+ }
32
+
33
+ export interface AdWhaleBannerViewProps {
34
+ style?: StyleProp<ViewStyle>;
35
+ placementUid: string;
36
+ placementName?: string;
37
+ region?: string;
38
+ gcoder?: AdWhaleBannerGcoder;
39
+ /**
40
+ * 배너 광고 사이즈
41
+ * @example adSize={AdWhaleAdSize.BANNER_320x50}
42
+ */
43
+ adSize: AdWhaleAdSize;
44
+ /**
45
+ * ADAPTIVE_ANCHOR 사이즈 사용 시 배너의 너비(dp)를 지정합니다.
46
+ * 0을 입력하면 디바이스 전체 가로 길이가 적용됩니다.
47
+ * @default 0 (디바이스 전체 너비)
48
+ * @example adaptiveAnchorWidth={360}
49
+ */
50
+ adaptiveAnchorWidth?: number;
51
+ loadAd?: boolean;
52
+
53
+ onAdLoaded?: () => void;
54
+ onAdLoadFailed?: (event: AdWhaleBannerError) => void;
55
+ onAdClicked?: () => void;
56
+ }
57
+
58
+ type NativeErrorEvent = NativeSyntheticEvent<AdWhaleBannerError>;
59
+
60
+ interface NativeAdViewProps
61
+ extends Omit<AdWhaleBannerViewProps, 'onAdLoadFailed'> {
62
+ onAdLoadFailed?: (e: NativeErrorEvent) => void;
63
+ }
64
+
65
+ const RNAdWhaleMediationAdView =
66
+ requireNativeComponent<NativeAdViewProps>('RNAdWhaleMediationAdView');
67
+
68
+ export const AdWhaleAdView: React.FC<AdWhaleBannerViewProps> = ({
69
+ onAdLoadFailed,
70
+ adSize,
71
+ adaptiveAnchorWidth,
72
+ ...restProps
73
+ }) => {
74
+ const handleAdLoadFailed: NativeAdViewProps['onAdLoadFailed'] = e => {
75
+ onAdLoadFailed?.(e.nativeEvent);
76
+ };
77
+
78
+ // ADAPTIVE_ANCHOR일 때만 adaptiveAnchorWidth를 네이티브로 전달
79
+ const nativeAdaptiveAnchorWidth =
80
+ adSize === AdWhaleAdSize.ADAPTIVE_ANCHOR ? adaptiveAnchorWidth : undefined;
81
+
82
+ return (
83
+ <RNAdWhaleMediationAdView
84
+ {...restProps}
85
+ adSize={adSize}
86
+ adaptiveAnchorWidth={nativeAdaptiveAnchorWidth}
87
+ onAdLoadFailed={onAdLoadFailed ? handleAdLoadFailed : undefined}
88
+ />
89
+ );
90
+ };