├── .circleci └── config.yml ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── .prettierrc ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── ReactNativeAdsFacebook.podspec ├── android ├── app │ ├── .gitignore │ ├── build.gradle │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── java │ │ └── suraj │ │ │ └── tiwari │ │ │ └── reactnativefbads │ │ │ ├── AdIconViewManager.java │ │ │ ├── AdSettingsManager.java │ │ │ ├── BannerView.java │ │ │ ├── BannerViewManager.java │ │ │ ├── FBAdsPackage.java │ │ │ ├── InterstitialAdManager.java │ │ │ ├── MediaViewManager.java │ │ │ ├── NativeAdChoicesView.java │ │ │ ├── NativeAdChoicesViewManager.java │ │ │ ├── NativeAdManager.java │ │ │ ├── NativeAdView.java │ │ │ └── NativeAdViewManager.java │ │ └── res │ │ └── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── styles.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── app.plugin.js ├── example ├── .eslintrc.js ├── .gitignore ├── .prettierrc.js ├── App.js ├── __tests__ │ └── App-test.js ├── android │ ├── app │ │ ├── BUCK │ │ ├── build.gradle │ │ ├── build_defs.bzl │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ ├── AndroidManifest.xml │ │ │ └── java │ │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── ReactNativeFlipper.java │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── assets │ │ │ └── fonts │ │ │ │ ├── AntDesign.ttf │ │ │ │ ├── Entypo.ttf │ │ │ │ ├── EvilIcons.ttf │ │ │ │ ├── Feather.ttf │ │ │ │ ├── FontAwesome.ttf │ │ │ │ ├── FontAwesome5_Brands.ttf │ │ │ │ ├── FontAwesome5_Regular.ttf │ │ │ │ ├── FontAwesome5_Solid.ttf │ │ │ │ ├── Fontisto.ttf │ │ │ │ ├── Foundation.ttf │ │ │ │ ├── Ionicons.ttf │ │ │ │ ├── MaterialCommunityIcons.ttf │ │ │ │ ├── MaterialIcons.ttf │ │ │ │ ├── Octicons.ttf │ │ │ │ ├── Roboto.ttf │ │ │ │ ├── Roboto_medium.ttf │ │ │ │ ├── SimpleLineIcons.ttf │ │ │ │ ├── Zocial.ttf │ │ │ │ └── rubicon-icon-font.ttf │ │ │ ├── java │ │ │ └── com │ │ │ │ └── example │ │ │ │ ├── MainActivity.java │ │ │ │ └── MainApplication.java │ │ │ └── res │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.png │ │ │ └── ic_launcher_round.png │ │ │ └── values │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── babel.config.js ├── index.js ├── ios │ ├── File.swift │ ├── Podfile │ ├── Podfile.lock │ ├── example-Bridging-Header.h │ ├── example-tvOS │ │ └── Info.plist │ ├── example-tvOSTests │ │ └── Info.plist │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ ├── example-tvOS.xcscheme │ │ │ └── example.xcscheme │ ├── example.xcworkspace │ │ └── contents.xcworkspacedata │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Info.plist │ │ ├── LaunchScreen.storyboard │ │ └── main.m │ └── exampleTests │ │ ├── Info.plist │ │ └── exampleTests.m ├── metro.config.js ├── package.json ├── src │ ├── BannerAds │ │ └── index.js │ ├── InterstitialAds │ │ └── index.js │ ├── NativeAds │ │ ├── NativeAdView.js │ │ └── index.js │ ├── Variables │ │ └── index.js │ └── index.js └── yarn.lock ├── images ├── nativeAd.png └── threed_mockup_fbNativeads.png ├── ios ├── ReactNativeAdsFacebook.xcodeproj │ └── project.pbxproj └── ReactNativeAdsFacebook │ ├── AdChoiceManager.h │ ├── AdChoiceManager.m │ ├── AdChoiceView.h │ ├── AdChoiceView.m │ ├── EXAdIconViewManager.h │ ├── EXAdIconViewManager.m │ ├── EXAdSettingsManager.h │ ├── EXAdSettingsManager.m │ ├── EXBannerView.h │ ├── EXBannerView.m │ ├── EXBannerViewManager.h │ ├── EXBannerViewManager.m │ ├── EXInterstitialAdManager.h │ ├── EXInterstitialAdManager.m │ ├── EXNativeAdEmitter.h │ ├── EXNativeAdEmitter.m │ ├── EXNativeAdManager.h │ ├── EXNativeAdManager.m │ ├── EXNativeAdView.h │ ├── EXNativeAdView.m │ ├── EXNativeMediaViewManager.h │ ├── EXNativeMediaViewManager.m │ └── EXUnversioned.h ├── package.json ├── plugin ├── src │ └── withReactNativeFbads.ts └── tsconfig.json ├── react-native.config.js ├── src ├── AdSettings.ts ├── BannerViewManager.tsx ├── InterstitialAdManager.ts ├── index.ts ├── native-ads │ ├── AdChoicesManager.tsx │ ├── AdIconViewManager.tsx │ ├── MediaViewManager.tsx │ ├── NativeAdsManager.ts │ ├── TriggerableView.tsx │ ├── contexts.ts │ ├── nativeAd.ts │ └── withNativeAd.tsx └── util │ └── areSetsEqual.ts ├── tsconfig.json ├── tslint.json └── yarn.lock /.circleci/config.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | 3 | defaults: &defaults 4 | docker: 5 | - image: circleci/node:10 6 | working_directory: ~/react-native-fbads 7 | 8 | jobs: 9 | install-dependencies: 10 | <<: *defaults 11 | steps: 12 | - checkout 13 | - attach_workspace: 14 | at: ~/react-native-fbads 15 | - restore_cache: 16 | keys: 17 | - dependencies-{{ checksum "package.json" }} 18 | - dependencies- 19 | - run: | 20 | yarn install --frozen-lockfile 21 | - save_cache: 22 | key: dependencies-{{ checksum "package.json" }} 23 | paths: node_modules 24 | - persist_to_workspace: 25 | root: . 26 | paths: . 27 | lint: 28 | <<: *defaults 29 | steps: 30 | - attach_workspace: 31 | at: ~/react-native-fbads 32 | - run: | 33 | yarn lint 34 | flow: 35 | <<: *defaults 36 | steps: 37 | - attach_workspace: 38 | at: ~/react-native-fbads 39 | - run: | 40 | yarn flow 41 | workflows: 42 | version: 2 43 | build-and-test: 44 | jobs: 45 | - install-dependencies 46 | - lint: 47 | requires: 48 | - install-dependencies 49 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Report a bug with his library 4 | --- 5 | 6 | # Bug Report 7 | 8 | **Before opening** 9 | 10 | - [ ] Did you try the latest release? 11 | - [ ] Did you look for existing issues? 12 | 13 | **Ad Modules** 14 | 15 | 16 | 17 | 18 | 19 | 20 | **Platforms** 21 | 22 | 23 | 24 | 25 | 26 | **Versions** 27 | 28 | - Android: 29 | - iOS: 30 | - react-native-fbads: 31 | - react-native: 32 | 33 | **Ads Environment** 34 | 35 | 36 | 37 | - Facebook app installed: 38 | - Happens in test (dev build + device is marked as a test device): 39 | - Happens in production (release build + **not** a test device): 40 | - Facebook review status: [not submitted | approved] 41 | 42 | **Current Behaviour** 43 | 44 | 45 | 46 | **Expected Behaviour** 47 | 48 | 49 | 50 | **Steps to Reproduce** 51 | 52 | 53 | 54 | **Additionals** 55 | 56 | 57 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Typescript generated files 2 | dist/ 3 | 4 | # OSX 5 | # 6 | .vscode 7 | .DS_Store 8 | 9 | # Xcode 10 | # 11 | build/ 12 | *.pbxuser 13 | !default.pbxuser 14 | *.mode1v3 15 | !default.mode1v3 16 | *.mode2v3 17 | !default.mode2v3 18 | *.perspectivev3 19 | !default.perspectivev3 20 | xcuserdata 21 | *.xccheckout 22 | *.moved-aside 23 | DerivedData 24 | *.hmap 25 | *.ipa 26 | *.xcuserstate 27 | project.xcworkspace 28 | 29 | # Android/IJ 30 | # 31 | *.iml 32 | .idea 33 | .gradle 34 | local.properties 35 | 36 | # node.js 37 | # 38 | node_modules/ 39 | npm-debug.log 40 | 41 | # BUCK 42 | buck-out/ 43 | \.buckd/ 44 | android/app/libs 45 | android/keystores/debug.keystore 46 | .babelrc 47 | .buckconfig 48 | .flowconfig 49 | .gitattributes 50 | .watchmanconfig 51 | android/app/src/main/res/mipmap-hdpi/ 52 | android/app/src/main/res/mipmap-mdpi/ 53 | android/app/src/main/res/mipmap-xhdpi/ 54 | android/app/src/main/res/mipmap-xxhdpi/ 55 | android/app/src/main/res/mipmap-anydpi-v26/ 56 | android/app/src/main/res/drawable/ 57 | android/app/src/main/res/drawable-v24/ 58 | ios/example-tvOS/ 59 | ios/example-tvOSTests/ 60 | ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme 61 | ios/example/Images.xcassets/Contents.json 62 | ios/exampleTests/ 63 | -------------------------------------------------------------------------------- /.prettierrc: -------------------------------------------------------------------------------- 1 | { 2 | "singleQuote": true 3 | } 4 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | ### Contributing 2 | 3 | Pull requests are always welcome! 4 | Before opening a PR, please: 5 | 6 | - Ensure Typescript compiles without errors 7 | - Run TSLint and fix any outstanding issues 8 | - Ensure you've formatted your code with Prettier 9 | 10 | Thanks for contributing! -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | Copyright (c) 2016 Callstack.io 3 | 4 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 5 | 6 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 7 | 8 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 9 | -------------------------------------------------------------------------------- /ReactNativeAdsFacebook.podspec: -------------------------------------------------------------------------------- 1 | require 'json' 2 | package = JSON.parse(File.read(File.join(__dir__, './', 'package.json'))) 3 | 4 | Pod::Spec.new do |s| 5 | s.name = 'ReactNativeAdsFacebook' 6 | s.version = package['version'] 7 | s.summary = package['description'] 8 | # s.requires_arc = true 9 | s.author = { 'abhaynpai' => 'abhaypai2611@gmail.com' } 10 | s.license = package['license'] 11 | s.homepage = package['homepage'] 12 | s.source = { :git => 'https://github.com/callstack/react-native-fbads', :tag => "v#{package['version']}" } 13 | s.platform = :ios, '7.0' 14 | s.dependency 'React' 15 | s.dependency 'FBAudienceNetwork' 16 | 17 | s.source_files = 'ios/**/*.{h,m}' 18 | end -------------------------------------------------------------------------------- /android/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | def safeExtGet(prop, fallback) { 4 | rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback 5 | } 6 | 7 | android { 8 | compileSdkVersion safeExtGet('compileSdkVersion', 26) 9 | buildToolsVersion safeExtGet('buildToolsVersion', '26.0.3') 10 | 11 | defaultConfig { 12 | minSdkVersion safeExtGet('minSdkVersion', 16) 13 | targetSdkVersion safeExtGet('targetSdkVersion', 26) 14 | versionCode 1 15 | versionName "1.0" 16 | ndk { 17 | abiFilters "armeabi-v7a", "x86" 18 | } 19 | } 20 | } 21 | 22 | repositories { 23 | jcenter() 24 | maven { url 'https://maven.google.com' } 25 | } 26 | 27 | dependencies { 28 | implementation 'com.facebook.react:react-native:+' 29 | implementation "com.android.support:recyclerview-v7:${safeExtGet('supportLibVersion', '26.1.0')}" 30 | implementation 'com.facebook.android:audience-network-sdk:6.2.+' 31 | implementation 'com.facebook.android:facebook-android-sdk:6.+' 32 | } 33 | -------------------------------------------------------------------------------- /android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile 22 | -------------------------------------------------------------------------------- /android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/AdIconViewManager.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | import com.facebook.ads.MediaView; 4 | import com.facebook.react.uimanager.SimpleViewManager; 5 | import com.facebook.react.uimanager.ThemedReactContext; 6 | 7 | public class AdIconViewManager extends SimpleViewManager { 8 | private static final String REACT_CLASS = "AdIconView"; 9 | 10 | @Override 11 | public String getName() { 12 | return REACT_CLASS; 13 | } 14 | 15 | @Override 16 | protected MediaView createViewInstance(ThemedReactContext reactContext) { 17 | return new MediaView(reactContext); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/AdSettingsManager.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | import android.content.SharedPreferences; 4 | import android.util.Log; 5 | 6 | import com.facebook.FacebookSdk; 7 | import com.facebook.ads.AdSettings; 8 | import com.facebook.react.bridge.LifecycleEventListener; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 11 | import com.facebook.react.bridge.ReactMethod; 12 | 13 | import java.util.HashMap; 14 | import java.util.Map; 15 | import java.util.HashSet; 16 | 17 | public class AdSettingsManager extends ReactContextBaseJavaModule implements LifecycleEventListener { 18 | 19 | final static private String TAG = AdSettingsManager.class.getName(); 20 | 21 | private HashSet mTestDeviceHashes = new HashSet<>(); 22 | private boolean mIsChildDirected = false; 23 | private String mMediationService = null; 24 | private String mUrlPrefix = null; 25 | 26 | public AdSettingsManager(ReactApplicationContext reactContext) { 27 | super(reactContext); 28 | reactContext.addLifecycleEventListener(this); 29 | } 30 | 31 | @Override 32 | public String getName() { 33 | return "CTKAdSettingsManager"; 34 | } 35 | 36 | @ReactMethod 37 | public void addTestDevice(String deviceHash) { 38 | AdSettings.addTestDevice(deviceHash); 39 | mTestDeviceHashes.add(deviceHash); 40 | } 41 | 42 | @ReactMethod 43 | public void clearTestDevices() { 44 | AdSettings.clearTestDevices(); 45 | mTestDeviceHashes.clear(); 46 | } 47 | 48 | @ReactMethod 49 | public void setLogLevel(String logLevel) { 50 | Log.w(TAG, "This method is not supported on Android"); 51 | } 52 | 53 | @ReactMethod 54 | public void setIsChildDirected(boolean isChildDirected) { 55 | AdSettings.setMixedAudience(isChildDirected); 56 | mIsChildDirected = isChildDirected; 57 | } 58 | 59 | @ReactMethod 60 | public void setMediationService(String mediationService) { 61 | AdSettings.setMediationService(mediationService); 62 | mMediationService = mediationService; 63 | } 64 | 65 | @ReactMethod 66 | public void setUrlPrefix(String urlPrefix) { 67 | AdSettings.setUrlPrefix(urlPrefix); 68 | mUrlPrefix = urlPrefix; 69 | } 70 | 71 | @ReactMethod 72 | public void setAdvertiserIDCollectionEnabled(boolean enabled) { 73 | FacebookSdk.setAdvertiserIDCollectionEnabled(enabled); 74 | } 75 | 76 | private void restoreSettings() { 77 | for (String hash: mTestDeviceHashes) { 78 | AdSettings.addTestDevice(hash); 79 | } 80 | 81 | AdSettings.setMixedAudience(mIsChildDirected); 82 | AdSettings.setMediationService(mMediationService); 83 | AdSettings.setUrlPrefix(mUrlPrefix); 84 | } 85 | 86 | private void clearSettings() { 87 | AdSettings.clearTestDevices(); 88 | AdSettings.setMixedAudience(false); 89 | AdSettings.setMediationService(null); 90 | AdSettings.setUrlPrefix(null); 91 | } 92 | 93 | @Override 94 | public void onHostResume() { 95 | restoreSettings(); 96 | } 97 | 98 | @Override 99 | public void onHostPause() { 100 | clearSettings(); 101 | } 102 | 103 | @Override 104 | public void onHostDestroy() { 105 | clearSettings(); 106 | } 107 | 108 | @Override 109 | public Map getConstants() { 110 | final Map constants = new HashMap<>(); 111 | SharedPreferences sp = getReactApplicationContext().getSharedPreferences("FBAdPrefs", 0); 112 | String deviceHashedId = sp.getString("deviceIdHash", null); 113 | 114 | constants.put("currentDeviceHash", deviceHashedId); 115 | 116 | return constants; 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/BannerView.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | import android.content.res.Resources; 4 | import android.util.DisplayMetrics; 5 | import android.util.TypedValue; 6 | 7 | import com.facebook.ads.Ad; 8 | import com.facebook.ads.AdError; 9 | import com.facebook.ads.AdListener; 10 | import com.facebook.ads.AdSize; 11 | import com.facebook.ads.AdView; 12 | import com.facebook.react.bridge.Arguments; 13 | import com.facebook.react.bridge.LifecycleEventListener; 14 | import com.facebook.react.bridge.ReactContext; 15 | import com.facebook.react.bridge.WritableMap; 16 | import com.facebook.react.uimanager.ThemedReactContext; 17 | import com.facebook.react.uimanager.events.RCTEventEmitter; 18 | import com.facebook.react.views.view.ReactViewGroup; 19 | 20 | 21 | public class BannerView extends ReactViewGroup implements AdListener, LifecycleEventListener { 22 | private ReactContext mContext; 23 | private AdView myAdView; 24 | private String mPlacementId; 25 | private AdSize mSize; 26 | private RCTEventEmitter mEventEmitter; 27 | 28 | public BannerView(ThemedReactContext context) { 29 | super(context); 30 | mContext = context; 31 | mContext.addLifecycleEventListener(this); 32 | mEventEmitter = mContext.getJSModule(RCTEventEmitter.class); 33 | } 34 | 35 | public void setPlacementId(String placementId) { 36 | mPlacementId = placementId; 37 | createAdViewIfCan(); 38 | } 39 | 40 | public void setSize(AdSize size) { 41 | mSize = size; 42 | createAdViewIfCan(); 43 | } 44 | 45 | @Override 46 | public void onError(Ad ad, AdError adError) { 47 | WritableMap event = Arguments.createMap(); 48 | 49 | event.putInt("errorCode", adError.getErrorCode()); 50 | event.putString("errorMessage", adError.getErrorMessage()); 51 | mEventEmitter.receiveEvent(getId(), "onAdError", event); 52 | 53 | myAdView = null; 54 | } 55 | 56 | @Override 57 | public void onAdLoaded(Ad ad) { 58 | this.removeAllViews(); 59 | 60 | Resources r = mContext.getResources(); 61 | DisplayMetrics dm = r.getDisplayMetrics(); 62 | int pxW = mSize.getWidth() > 0 ? 63 | dp2px(mSize.getWidth(), dm) 64 | : r.getDisplayMetrics().widthPixels; 65 | int pxH = dp2px(mSize.getHeight(), dm); 66 | 67 | myAdView.measure(pxW, pxH); 68 | myAdView.layout(0, 0, pxW, pxH); 69 | 70 | addView(myAdView); 71 | 72 | mEventEmitter.receiveEvent(getId(), "onAdLoad", null); 73 | } 74 | 75 | @Override 76 | public void onAdClicked(Ad ad) { 77 | mEventEmitter.receiveEvent(getId(), "onAdPress", null); 78 | } 79 | 80 | @Override 81 | public void onLoggingImpression(Ad ad) { 82 | mEventEmitter.receiveEvent(getId(), "onLoggingImpression", null); 83 | } 84 | 85 | private void createAdViewIfCan() { 86 | if (myAdView == null && mPlacementId != null && mSize != null) { 87 | myAdView = new AdView(this.getContext(), mPlacementId, mSize); 88 | myAdView.loadAd(myAdView.buildLoadAdConfig().withAdListener(this).build()); 89 | } 90 | } 91 | 92 | private int dp2px(int dp, DisplayMetrics dm) { 93 | return Math.round(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dp, dm)); 94 | } 95 | 96 | @Override 97 | public void onHostResume() { 98 | 99 | } 100 | 101 | @Override 102 | public void onHostPause() { 103 | 104 | } 105 | 106 | @Override 107 | public void onHostDestroy() { 108 | if (myAdView != null) { 109 | myAdView.destroy(); 110 | } 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/BannerViewManager.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | 4 | import androidx.annotation.Nullable; 5 | 6 | import com.facebook.ads.AdSize; 7 | import com.facebook.react.common.MapBuilder; 8 | import com.facebook.react.uimanager.SimpleViewManager; 9 | import com.facebook.react.uimanager.ThemedReactContext; 10 | import com.facebook.react.uimanager.annotations.ReactProp; 11 | 12 | import java.util.Map; 13 | 14 | public class BannerViewManager extends SimpleViewManager { 15 | @ReactProp(name = "placementId") 16 | public void setPlacementId(BannerView view, String placementId) { 17 | view.setPlacementId(placementId); 18 | } 19 | 20 | @ReactProp(name = "size") 21 | public void setSize(BannerView view, int size) { 22 | AdSize adSize = null; 23 | switch (size) { 24 | case 90: 25 | adSize = AdSize.BANNER_HEIGHT_90; 26 | break; 27 | case 50: 28 | default: 29 | adSize = AdSize.BANNER_HEIGHT_50; 30 | break; 31 | } 32 | view.setSize(adSize); 33 | } 34 | 35 | @Override 36 | protected BannerView createViewInstance(ThemedReactContext reactContext) { 37 | return new BannerView(reactContext); 38 | } 39 | 40 | @Override 41 | public @Nullable Map getExportedCustomDirectEventTypeConstants() { 42 | return MapBuilder.of( 43 | "onAdPress", 44 | MapBuilder.of("registrationName", "onAdPress"), 45 | "onAdError", 46 | MapBuilder.of("registrationName", "onAdError"), 47 | "onLoggingImpression", 48 | MapBuilder.of("registrationName", "onLoggingImpression"), 49 | "onAdLoad", 50 | MapBuilder.of("registrationName", "onAdLoad") 51 | ); 52 | } 53 | 54 | @Override 55 | public String getName() { 56 | return "CTKBannerView"; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/FBAdsPackage.java: -------------------------------------------------------------------------------- 1 | /** 2 | * FBAdsPackage.java 3 | * suraj.tiwari.reactnativefbads 4 | * 5 | * Created by Suraj Tiwari on 07/08/18. 6 | * Copyright © 2018 Suraj Tiwari All rights reserved. 7 | */ 8 | package suraj.tiwari.reactnativefbads; 9 | 10 | import com.facebook.react.ReactPackage; 11 | import com.facebook.react.bridge.JavaScriptModule; 12 | import com.facebook.react.bridge.NativeModule; 13 | import com.facebook.react.bridge.ReactApplicationContext; 14 | import com.facebook.react.uimanager.ViewManager; 15 | 16 | import java.util.Arrays; 17 | import java.util.Collections; 18 | import java.util.List; 19 | 20 | /** 21 | * Main package exporting native modules and views 22 | */ 23 | public class FBAdsPackage implements ReactPackage { 24 | @Override 25 | public List createNativeModules(ReactApplicationContext reactContext) { 26 | return Arrays.asList( 27 | new NativeAdManager(reactContext), 28 | new AdSettingsManager(reactContext), 29 | new InterstitialAdManager(reactContext), 30 | new NativeAdChoicesViewManager(reactContext) 31 | ); 32 | } 33 | 34 | // Deprecated RN 0.47 35 | public List> createJSModules() { 36 | return Collections.emptyList(); 37 | } 38 | 39 | @Override 40 | public List createViewManagers(ReactApplicationContext reactContext) { 41 | return Arrays.asList( 42 | new NativeAdViewManager(), 43 | new BannerViewManager(), 44 | new AdIconViewManager(), 45 | new MediaViewManager(), 46 | new NativeAdChoicesViewManager(reactContext) 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/InterstitialAdManager.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | import com.facebook.ads.Ad; 4 | import com.facebook.ads.AdError; 5 | import com.facebook.ads.InterstitialAd; 6 | import com.facebook.ads.InterstitialAdListener; 7 | import com.facebook.react.bridge.LifecycleEventListener; 8 | import com.facebook.react.bridge.Promise; 9 | import com.facebook.react.bridge.ReactApplicationContext; 10 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 11 | import com.facebook.react.bridge.ReactMethod; 12 | 13 | public class InterstitialAdManager extends ReactContextBaseJavaModule implements InterstitialAdListener, LifecycleEventListener { 14 | 15 | private Promise mPromise; 16 | private boolean mDidClick = false; 17 | private boolean mDidLoad = false; 18 | private boolean mViewAtOnce = false; 19 | 20 | private InterstitialAd mInterstitial; 21 | 22 | public InterstitialAdManager(ReactApplicationContext reactContext) { 23 | super(reactContext); 24 | reactContext.addLifecycleEventListener(this); 25 | } 26 | 27 | @ReactMethod 28 | public void loadAd(String placementId, Promise p) { 29 | 30 | ReactApplicationContext reactContext = this.getReactApplicationContext(); 31 | 32 | mViewAtOnce = true; 33 | mPromise = p; 34 | mInterstitial = new InterstitialAd(reactContext, placementId); 35 | mInterstitial.loadAd(); 36 | } 37 | 38 | @ReactMethod 39 | public void showAd(String placementId, Promise p) { 40 | if (mPromise != null) { 41 | p.reject("E_FAILED_TO_SHOW", "Only one `showAd` can be called at once"); 42 | return; 43 | } 44 | ReactApplicationContext reactContext = this.getReactApplicationContext(); 45 | 46 | mViewAtOnce = true; 47 | mPromise = p; 48 | mInterstitial = new InterstitialAd(reactContext, placementId); 49 | mInterstitial.loadAd(mInterstitial.buildLoadAdConfig().withAdListener(this).build()); 50 | } 51 | 52 | @ReactMethod 53 | public void preloadAd(String placementId, Promise p) { 54 | if (mPromise != null) { 55 | p.reject("E_FAILED_TO_SHOW", "Only one `preloadAd` can be called at once"); 56 | return; 57 | } 58 | ReactApplicationContext reactContext = this.getReactApplicationContext(); 59 | 60 | mViewAtOnce = false; 61 | mPromise = p; 62 | mInterstitial = new InterstitialAd(reactContext, placementId); 63 | mInterstitial.loadAd(mInterstitial.buildLoadAdConfig().withAdListener(this).build()); 64 | } 65 | 66 | @ReactMethod 67 | public void showPreloadedAd(String placementId, Promise p) { 68 | if (mDidLoad) { 69 | mInterstitial.show(); 70 | } else { 71 | mViewAtOnce = true; 72 | } 73 | } 74 | 75 | @Override 76 | public String getName() { 77 | return "CTKInterstitialAdManager"; 78 | } 79 | 80 | @Override 81 | public void onError(Ad ad, AdError adError) { 82 | mPromise.reject("E_FAILED_TO_LOAD", adError.getErrorMessage()); 83 | cleanUp(); 84 | } 85 | 86 | @Override 87 | public void onAdLoaded(Ad ad) { 88 | if (ad == mInterstitial && mViewAtOnce) { 89 | mInterstitial.show(); 90 | } else { 91 | mDidLoad = true; 92 | } 93 | } 94 | 95 | @Override 96 | public void onAdClicked(Ad ad) { 97 | mDidClick = true; 98 | } 99 | 100 | @Override 101 | public void onInterstitialDismissed(Ad ad) { 102 | mPromise.resolve(mDidClick); 103 | cleanUp(); 104 | } 105 | 106 | @Override 107 | public void onInterstitialDisplayed(Ad ad) { 108 | 109 | } 110 | 111 | @Override 112 | public void onLoggingImpression(Ad ad) { 113 | } 114 | 115 | private void cleanUp() { 116 | mPromise = null; 117 | mDidClick = false; 118 | mDidLoad = false; 119 | mViewAtOnce = false; 120 | 121 | if (mInterstitial != null) { 122 | mInterstitial.destroy(); 123 | mInterstitial = null; 124 | } 125 | } 126 | 127 | @Override 128 | public void onHostResume() { 129 | 130 | } 131 | 132 | @Override 133 | public void onHostPause() { 134 | 135 | } 136 | 137 | @Override 138 | public void onHostDestroy() { 139 | cleanUp(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/MediaViewManager.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | import com.facebook.ads.MediaView; 4 | import com.facebook.react.uimanager.SimpleViewManager; 5 | import com.facebook.react.uimanager.ThemedReactContext; 6 | 7 | public class MediaViewManager extends SimpleViewManager { 8 | private static final String REACT_CLASS = "MediaView"; 9 | 10 | @Override 11 | public String getName() { 12 | return REACT_CLASS; 13 | } 14 | 15 | @Override 16 | protected MediaView createViewInstance(ThemedReactContext reactContext) { 17 | return new MediaView(reactContext); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/NativeAdChoicesView.java: -------------------------------------------------------------------------------- 1 | /** 2 | * NativeAdChoicesView.java 3 | * suraj.tiwari.reactnativefbads 4 | * 5 | * Created by Suraj Tiwari on 07/08/18. 6 | * Copyright © 2018 Suraj Tiwari All rights reserved. 7 | */ 8 | package suraj.tiwari.reactnativefbads; 9 | 10 | import com.facebook.ads.AdOptionsView; 11 | import com.facebook.ads.NativeAd; 12 | import com.facebook.react.bridge.ReactContext; 13 | import com.facebook.react.uimanager.ThemedReactContext; 14 | import com.facebook.react.views.view.ReactViewGroup; 15 | 16 | public class NativeAdChoicesView extends ReactViewGroup { 17 | 18 | private ReactContext mContext; 19 | public NativeAdChoicesView(ThemedReactContext context) { 20 | super(context); 21 | mContext = context; 22 | } 23 | 24 | public void setNativeAd(NativeAd nativeAd) { 25 | AdOptionsView adChoicesView = new AdOptionsView(mContext, nativeAd, null); 26 | adChoicesView.measure(adChoicesView.getMeasuredWidth(), adChoicesView.getMeasuredHeight()); 27 | adChoicesView.layout(0, 0, adChoicesView.getMeasuredWidth(), adChoicesView.getMeasuredHeight()); 28 | adChoicesView.bringToFront(); 29 | addView(adChoicesView); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/NativeAdChoicesViewManager.java: -------------------------------------------------------------------------------- 1 | /** 2 | * NativeAdChoiceViewManager.java 3 | * suraj.tiwari.reactnativefbads 4 | * 5 | * Created by Suraj Tiwari on 07/08/18. 6 | * Copyright © 2018 Suraj Tiwari All rights reserved. 7 | */ 8 | 9 | package suraj.tiwari.reactnativefbads; 10 | 11 | import com.facebook.ads.NativeAdsManager; 12 | import com.facebook.react.bridge.ReactApplicationContext; 13 | import com.facebook.react.uimanager.SimpleViewManager; 14 | import com.facebook.react.uimanager.ThemedReactContext; 15 | import com.facebook.react.uimanager.annotations.ReactProp; 16 | 17 | public class NativeAdChoicesViewManager extends SimpleViewManager { 18 | ReactApplicationContext mReactContext; 19 | 20 | private static final String REACT_CLASS = "AdChoicesView"; 21 | 22 | @Override 23 | public String getName() { 24 | return REACT_CLASS; 25 | } 26 | 27 | public NativeAdChoicesViewManager(ReactApplicationContext context) { 28 | super(); 29 | mReactContext = context; 30 | } 31 | 32 | @ReactProp(name = "placementId") 33 | public void adsManager(NativeAdChoicesView view, String adsManagerId) { 34 | NativeAdManager adManager = mReactContext.getNativeModule(NativeAdManager.class); 35 | NativeAdsManager adsManager = adManager.getFBAdsManager(adsManagerId); 36 | 37 | view.setNativeAd(adsManager.nextNativeAd()); 38 | } 39 | 40 | @Override 41 | protected NativeAdChoicesView createViewInstance(ThemedReactContext reactContext) { 42 | return new NativeAdChoicesView(reactContext); 43 | } 44 | } -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/NativeAdManager.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | import android.util.Log; 4 | import android.view.View; 5 | 6 | import com.facebook.ads.AdError; 7 | import com.facebook.ads.MediaView; 8 | import com.facebook.ads.NativeAdsManager; 9 | import com.facebook.react.bridge.Arguments; 10 | import com.facebook.react.bridge.Promise; 11 | import com.facebook.react.bridge.ReactApplicationContext; 12 | import com.facebook.react.bridge.ReactContextBaseJavaModule; 13 | import com.facebook.react.bridge.ReactMethod; 14 | import com.facebook.react.bridge.ReadableArray; 15 | import com.facebook.react.bridge.UiThreadUtil; 16 | import com.facebook.react.bridge.WritableMap; 17 | import com.facebook.react.modules.core.RCTNativeAppEventEmitter; 18 | import com.facebook.react.module.annotations.ReactModule; 19 | import com.facebook.react.uimanager.IllegalViewOperationException; 20 | import com.facebook.react.uimanager.NativeViewHierarchyManager; 21 | import com.facebook.react.uimanager.UIBlock; 22 | import com.facebook.react.uimanager.UIManagerModule; 23 | 24 | import java.util.ArrayList; 25 | import java.util.HashMap; 26 | import java.util.List; 27 | import java.util.Map; 28 | import java.util.Locale; 29 | 30 | @ReactModule(name = "CTKNativeAdManager") 31 | public class NativeAdManager extends ReactContextBaseJavaModule implements NativeAdsManager.Listener { 32 | /** 33 | * @{Map} with all registered fb ads managers 34 | **/ 35 | private Map mAdsManagers = new HashMap<>(); 36 | 37 | public NativeAdManager(ReactApplicationContext reactContext) { 38 | super(reactContext); 39 | } 40 | 41 | @Override 42 | public String getName() { 43 | return "CTKNativeAdManager"; 44 | } 45 | 46 | /** 47 | * Initialises native ad manager for a given placement id and ads to request. 48 | * This method is run on the UI thread 49 | * 50 | * @param placementId 51 | * @param adsToRequest 52 | */ 53 | @ReactMethod 54 | public void init(final String placementId, final int adsToRequest) { 55 | final ReactApplicationContext reactContext = this.getReactApplicationContext(); 56 | 57 | UiThreadUtil.runOnUiThread(new Runnable() { 58 | @Override 59 | public void run() { 60 | final NativeAdsManager adsManager = new NativeAdsManager(reactContext, placementId, adsToRequest); 61 | 62 | adsManager.setListener(NativeAdManager.this); 63 | 64 | mAdsManagers.put(placementId, adsManager); 65 | 66 | adsManager.loadAds(); 67 | } 68 | }); 69 | } 70 | 71 | /** 72 | * Disables auto refresh 73 | * 74 | * @param placementId 75 | */ 76 | @ReactMethod 77 | public void disableAutoRefresh(String placementId) { 78 | mAdsManagers.get(placementId).disableAutoRefresh(); 79 | } 80 | 81 | /** 82 | * Sets media cache policy 83 | * 84 | * @param placementId 85 | * @param cachePolicy 86 | */ 87 | @ReactMethod 88 | public void setMediaCachePolicy(String placementId, String cachePolicy) { 89 | Log.w("NativeAdManager", "This method is not supported on Android"); 90 | } 91 | 92 | /** 93 | * Called when one of the registered ads managers loads ads. Sends state of all 94 | * managers back to JS 95 | */ 96 | @Override 97 | public void onAdsLoaded() { 98 | WritableMap adsManagersState = Arguments.createMap(); 99 | 100 | for (String key : mAdsManagers.keySet()) { 101 | NativeAdsManager adsManager = mAdsManagers.get(key); 102 | adsManagersState.putBoolean(key, adsManager.isLoaded()); 103 | } 104 | 105 | sendAppEvent("CTKNativeAdsManagersChanged", adsManagersState); 106 | } 107 | 108 | @Override 109 | public void onAdError(AdError adError) { 110 | String error = String.format("%d: %s", adError.getErrorCode(), adError.getErrorMessage(), Locale.ENGLISH); 111 | 112 | this.sendAppEvent("onAdError", error); 113 | } 114 | 115 | /** 116 | * Returns FBAdsManager for a given placement id 117 | * 118 | * @param placementId 119 | * @return 120 | */ 121 | public NativeAdsManager getFBAdsManager(String placementId) { 122 | return mAdsManagers.get(placementId); 123 | } 124 | 125 | /** 126 | * Helper for sending events back to Javascript. 127 | * 128 | * @param eventName 129 | * @param params 130 | */ 131 | private void sendAppEvent(String eventName, Object params) { 132 | ReactApplicationContext context = this.getReactApplicationContext(); 133 | 134 | if (context == null || !context.hasActiveCatalystInstance()) { 135 | return; 136 | } 137 | 138 | context 139 | .getJSModule(RCTNativeAppEventEmitter.class) 140 | .emit(eventName, params); 141 | } 142 | 143 | @ReactMethod 144 | public void registerViewsForInteraction(final int adTag, 145 | final int mediaViewTag, 146 | final int adIconViewTag, 147 | final ReadableArray clickableViewsTags, 148 | final Promise promise) { 149 | getReactApplicationContext().getNativeModule(UIManagerModule.class).addUIBlock(new UIBlock() { 150 | @Override 151 | public void execute(NativeViewHierarchyManager nativeViewHierarchyManager) { 152 | try { 153 | NativeAdView nativeAdView = null; 154 | MediaView mediaView = null; 155 | MediaView adIconView = null; 156 | 157 | if (adTag != -1) { 158 | nativeAdView = (NativeAdView) nativeViewHierarchyManager.resolveView(adTag); 159 | } else { 160 | throw new Exception("Native Ad View was not set!"); 161 | } 162 | 163 | if (mediaViewTag != -1) { 164 | mediaView = (MediaView) nativeViewHierarchyManager.resolveView(mediaViewTag); 165 | } 166 | 167 | if (adIconViewTag != -1) { 168 | adIconView = (MediaView) nativeViewHierarchyManager.resolveView(adIconViewTag); 169 | } 170 | 171 | List clickableViews = new ArrayList<>(); 172 | 173 | for (int i = 0; i < clickableViewsTags.size(); ++i) { 174 | View view = nativeViewHierarchyManager.resolveView(clickableViewsTags.getInt(i)); 175 | clickableViews.add(view); 176 | } 177 | 178 | Log.w("ClickableViewsTags", Integer.toString(clickableViewsTags.size())); 179 | Log.w("ClickableViews", Integer.toString(clickableViews.size()) ); 180 | 181 | nativeAdView.registerViewsForInteraction(mediaView, adIconView, clickableViews); 182 | promise.resolve(null); 183 | } catch (ClassCastException e) { 184 | promise.reject("E_CANNOT_CAST", e); 185 | } catch (IllegalViewOperationException e) { 186 | promise.reject("E_INVALID_TAG_ERROR", e); 187 | } catch (NullPointerException e) { 188 | promise.reject("E_NO_NATIVE_AD_VIEW", e); 189 | } catch (Exception e) { 190 | promise.reject("E_AD_REGISTER_ERROR", e); 191 | } 192 | } 193 | }); 194 | } 195 | } 196 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/NativeAdView.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | import android.view.View; 4 | import com.facebook.ads.MediaView; 5 | import com.facebook.ads.NativeAd; 6 | import com.facebook.react.bridge.Arguments; 7 | import com.facebook.react.bridge.WritableMap; 8 | import com.facebook.react.uimanager.ThemedReactContext; 9 | import com.facebook.react.uimanager.events.RCTEventEmitter; 10 | import com.facebook.react.views.view.ReactViewGroup; 11 | 12 | import java.util.List; 13 | 14 | public class NativeAdView extends ReactViewGroup { 15 | /** 16 | * @{NativeAd} received from the ads manager 17 | **/ 18 | private NativeAd mNativeAd; 19 | 20 | /** 21 | * @{RCTEventEmitter} instance used for sending events back to JS 22 | **/ 23 | private RCTEventEmitter mEventEmitter; 24 | 25 | /** 26 | * Creates new NativeAdView instance and retrieves event emitter 27 | * 28 | * @param context 29 | */ 30 | public NativeAdView(ThemedReactContext context) { 31 | super(context); 32 | 33 | mEventEmitter = context.getJSModule(RCTEventEmitter.class); 34 | } 35 | 36 | /** 37 | * Called by the view manager when adsManager prop is set. Sends serialised 38 | * version of a native ad back to Javascript. 39 | * 40 | * @param nativeAd 41 | */ 42 | public void setNativeAd(NativeAd nativeAd) { 43 | if (mNativeAd != null) { 44 | mNativeAd.unregisterView(); 45 | } 46 | 47 | mNativeAd = nativeAd; 48 | 49 | if (nativeAd == null) { 50 | mEventEmitter.receiveEvent(getId(), "onAdLoaded", null); 51 | return; 52 | } 53 | 54 | WritableMap event = Arguments.createMap(); 55 | event.putString("headline", nativeAd.getAdHeadline()); 56 | event.putString("socialContext", nativeAd.getAdSocialContext()); 57 | event.putString("bodyText", nativeAd.getAdBodyText()); 58 | event.putString("callToActionText", nativeAd.getAdCallToAction()); 59 | event.putString("sponsoredTranslation", nativeAd.getSponsoredTranslation()); 60 | event.putString("advertiserName", nativeAd.getAdvertiserName()); 61 | event.putString("promotedTranslation", nativeAd.getPromotedTranslation()); 62 | event.putString("translation", nativeAd.getAdTranslation()); 63 | event.putString("linkDescription", nativeAd.getAdLinkDescription()); 64 | 65 | mEventEmitter.receiveEvent(getId(), "onAdLoaded", event); 66 | } 67 | 68 | public void registerViewsForInteraction(MediaView mediaView, MediaView adIconView, List clickableViews) { 69 | mNativeAd.registerViewForInteraction(this, mediaView, adIconView, clickableViews); 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /android/app/src/main/java/suraj/tiwari/reactnativefbads/NativeAdViewManager.java: -------------------------------------------------------------------------------- 1 | package suraj.tiwari.reactnativefbads; 2 | 3 | import android.content.Context; 4 | import android.util.Log; 5 | import android.view.View; 6 | 7 | import androidx.annotation.NonNull; 8 | import androidx.annotation.Nullable; 9 | 10 | import com.facebook.ads.NativeAdsManager; 11 | import com.facebook.react.bridge.ReactContext; 12 | import com.facebook.react.common.MapBuilder; 13 | import com.facebook.react.uimanager.ThemedReactContext; 14 | import com.facebook.react.uimanager.ViewGroupManager; 15 | import com.facebook.react.uimanager.annotations.ReactProp; 16 | 17 | import java.util.Map; 18 | 19 | public class NativeAdViewManager extends ViewGroupManager { 20 | private static String NAME = "CTKNativeAd"; 21 | 22 | @Override 23 | public String getName() { 24 | return NAME; 25 | } 26 | 27 | @NonNull 28 | @Override 29 | protected NativeAdView createViewInstance(@NonNull ThemedReactContext reactContext) { 30 | return new NativeAdView(reactContext); 31 | } 32 | 33 | @ReactProp(name = "adsManager") 34 | public void setAdsManager(NativeAdView view, String adsManagerId) { 35 | Context viewContext = view.getContext(); 36 | if (viewContext instanceof ReactContext) { 37 | ReactContext reactContext = (ReactContext) viewContext; 38 | NativeAdManager adManager = reactContext.getNativeModule(NativeAdManager.class); 39 | NativeAdsManager adsManager = adManager.getFBAdsManager(adsManagerId); 40 | 41 | view.setNativeAd(adsManager.nextNativeAd()); 42 | } else { 43 | Log.e("E_NOT_RCT_CONTEXT", "View's context is not a ReactContext, so it's not possible to get NativeAdManager."); 44 | } 45 | } 46 | 47 | @Override 48 | @Nullable 49 | public Map getExportedCustomDirectEventTypeConstants() { 50 | return MapBuilder.of( 51 | "onAdLoaded", 52 | MapBuilder.of("registrationName", "onAdLoaded"), 53 | "onAdFailed", 54 | MapBuilder.of("registrationName", "onAdFailed") 55 | ); 56 | } 57 | 58 | @Override 59 | public void addView(NativeAdView parent, View child, int index) { 60 | parent.addView(child, index); 61 | } 62 | 63 | @Override 64 | public int getChildCount(NativeAdView parent) { 65 | return parent.getChildCount(); 66 | } 67 | 68 | @Override 69 | public View getChildAt(NativeAdView parent, int index) { 70 | return parent.getChildAt(index); 71 | } 72 | 73 | @Override 74 | public void removeViewAt(NativeAdView parent, int index) { 75 | parent.removeViewAt(index); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #3F51B5 4 | #303F9F 5 | #FF4081 6 | 7 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | ReactNativeFbAds 3 | 4 | -------------------------------------------------------------------------------- /android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /android/build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | // google() 4 | jcenter() 5 | maven { url 'https://maven.google.com' } 6 | } 7 | dependencies { 8 | classpath 'com.android.tools.build:gradle:3.0.1' 9 | } 10 | } 11 | 12 | allprojects { 13 | repositories { 14 | jcenter() 15 | maven { url 'https://maven.google.com' } 16 | } 17 | } 18 | 19 | task clean(type: Delete) { 20 | delete rootProject.buildDir 21 | } 22 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | org.gradle.jvmargs=-Xmx1536m 13 | 14 | # When configured, Gradle will run in incubating parallel mode. 15 | # This option should only be used with decoupled projects. More details, visit 16 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 17 | # org.gradle.parallel=true 18 | android.useAndroidX=true 19 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Oct 02 14:21:13 IDT 2018 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-4.4-all.zip 7 | -------------------------------------------------------------------------------- /android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # Attempt to set APP_HOME 46 | # Resolve links: $0 may be a link 47 | PRG="$0" 48 | # Need this for relative symlinks. 49 | while [ -h "$PRG" ] ; do 50 | ls=`ls -ld "$PRG"` 51 | link=`expr "$ls" : '.*-> \(.*\)$'` 52 | if expr "$link" : '/.*' > /dev/null; then 53 | PRG="$link" 54 | else 55 | PRG=`dirname "$PRG"`"/$link" 56 | fi 57 | done 58 | SAVED="`pwd`" 59 | cd "`dirname \"$PRG\"`/" >/dev/null 60 | APP_HOME="`pwd -P`" 61 | cd "$SAVED" >/dev/null 62 | 63 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 64 | 65 | # Determine the Java command to use to start the JVM. 66 | if [ -n "$JAVA_HOME" ] ; then 67 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 68 | # IBM's JDK on AIX uses strange locations for the executables 69 | JAVACMD="$JAVA_HOME/jre/sh/java" 70 | else 71 | JAVACMD="$JAVA_HOME/bin/java" 72 | fi 73 | if [ ! -x "$JAVACMD" ] ; then 74 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 75 | 76 | Please set the JAVA_HOME variable in your environment to match the 77 | location of your Java installation." 78 | fi 79 | else 80 | JAVACMD="java" 81 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 82 | 83 | Please set the JAVA_HOME variable in your environment to match the 84 | location of your Java installation." 85 | fi 86 | 87 | # Increase the maximum file descriptors if we can. 88 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 89 | MAX_FD_LIMIT=`ulimit -H -n` 90 | if [ $? -eq 0 ] ; then 91 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 92 | MAX_FD="$MAX_FD_LIMIT" 93 | fi 94 | ulimit -n $MAX_FD 95 | if [ $? -ne 0 ] ; then 96 | warn "Could not set maximum file descriptor limit: $MAX_FD" 97 | fi 98 | else 99 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 100 | fi 101 | fi 102 | 103 | # For Darwin, add options to specify how the application appears in the dock 104 | if $darwin; then 105 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 106 | fi 107 | 108 | # For Cygwin, switch paths to Windows format before running java 109 | if $cygwin ; then 110 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 111 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 112 | JAVACMD=`cygpath --unix "$JAVACMD"` 113 | 114 | # We build the pattern for arguments to be converted via cygpath 115 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 116 | SEP="" 117 | for dir in $ROOTDIRSRAW ; do 118 | ROOTDIRS="$ROOTDIRS$SEP$dir" 119 | SEP="|" 120 | done 121 | OURCYGPATTERN="(^($ROOTDIRS))" 122 | # Add a user-defined pattern to the cygpath arguments 123 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 124 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 125 | fi 126 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 127 | i=0 128 | for arg in "$@" ; do 129 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 130 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 131 | 132 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 133 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 134 | else 135 | eval `echo args$i`="\"$arg\"" 136 | fi 137 | i=$((i+1)) 138 | done 139 | case $i in 140 | (0) set -- ;; 141 | (1) set -- "$args0" ;; 142 | (2) set -- "$args0" "$args1" ;; 143 | (3) set -- "$args0" "$args1" "$args2" ;; 144 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 145 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 146 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 147 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 148 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 149 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 150 | esac 151 | fi 152 | 153 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 154 | function splitJvmOpts() { 155 | JVM_OPTS=("$@") 156 | } 157 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 158 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 159 | 160 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 161 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /android/settings.gradle: -------------------------------------------------------------------------------- 1 | include ':app' 2 | -------------------------------------------------------------------------------- /app.plugin.js: -------------------------------------------------------------------------------- 1 | module.exports = require("./plugin/build/withReactNativeFbads"); -------------------------------------------------------------------------------- /example/.eslintrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | extends: '@react-native-community', 4 | }; 5 | -------------------------------------------------------------------------------- /example/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | 24 | # Android/IntelliJ 25 | # 26 | build/ 27 | .idea 28 | .gradle 29 | local.properties 30 | *.iml 31 | 32 | # node.js 33 | # 34 | node_modules/ 35 | npm-debug.log 36 | yarn-error.log 37 | 38 | # BUCK 39 | buck-out/ 40 | \.buckd/ 41 | *.keystore 42 | !debug.keystore 43 | 44 | # fastlane 45 | # 46 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 47 | # screenshots whenever they are needed. 48 | # For more information about the recommended setup visit: 49 | # https://docs.fastlane.tools/best-practices/source-control/ 50 | 51 | */fastlane/report.xml 52 | */fastlane/Preview.html 53 | */fastlane/screenshots 54 | 55 | # Bundle artifact 56 | *.jsbundle 57 | 58 | # CocoaPods 59 | /ios/Pods/ 60 | -------------------------------------------------------------------------------- /example/.prettierrc.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | bracketSpacing: false, 3 | jsxBracketSameLine: true, 4 | singleQuote: true, 5 | trailingComma: 'all', 6 | }; 7 | -------------------------------------------------------------------------------- /example/App.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Container} from 'native-base'; 3 | import {Scene, Router} from 'react-native-router-flux'; 4 | 5 | import Main from './src'; 6 | import NativeAd from './src/NativeAds'; 7 | import BannerAd from './src/BannerAds'; 8 | import InterstitialAd from './src/InterstitialAds'; 9 | 10 | export default class App extends Component { 11 | render() { 12 | return ( 13 | 14 | 15 | 16 | 23 | 30 | 37 | 44 | 45 | 46 | 47 | ); 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /example/__tests__/App-test.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import 'react-native'; 6 | import React from 'react'; 7 | import App from '../App'; 8 | 9 | // Note: test renderer must be required after react-native. 10 | import renderer from 'react-test-renderer'; 11 | 12 | it('renders correctly', () => { 13 | renderer.create(); 14 | }); 15 | -------------------------------------------------------------------------------- /example/android/app/BUCK: -------------------------------------------------------------------------------- 1 | # To learn about Buck see [Docs](https://buckbuild.com/). 2 | # To run your application with Buck: 3 | # - install Buck 4 | # - `npm start` - to start the packager 5 | # - `cd android` 6 | # - `keytool -genkey -v -keystore keystores/debug.keystore -storepass android -alias androiddebugkey -keypass android -dname "CN=Android Debug,O=Android,C=US"` 7 | # - `./gradlew :app:copyDownloadableDepsToLibs` - make all Gradle compile dependencies available to Buck 8 | # - `buck install -r android/app` - compile, install and run application 9 | # 10 | 11 | load(":build_defs.bzl", "create_aar_targets", "create_jar_targets") 12 | 13 | lib_deps = [] 14 | 15 | create_aar_targets(glob(["libs/*.aar"])) 16 | 17 | create_jar_targets(glob(["libs/*.jar"])) 18 | 19 | android_library( 20 | name = "all-libs", 21 | exported_deps = lib_deps, 22 | ) 23 | 24 | android_library( 25 | name = "app-code", 26 | srcs = glob([ 27 | "src/main/java/**/*.java", 28 | ]), 29 | deps = [ 30 | ":all-libs", 31 | ":build_config", 32 | ":res", 33 | ], 34 | ) 35 | 36 | android_build_config( 37 | name = "build_config", 38 | package = "com.example", 39 | ) 40 | 41 | android_resource( 42 | name = "res", 43 | package = "com.example", 44 | res = "src/main/res", 45 | ) 46 | 47 | android_binary( 48 | name = "app", 49 | keystore = "//android/keystores:debug", 50 | manifest = "src/main/AndroidManifest.xml", 51 | package_type = "debug", 52 | deps = [ 53 | ":app-code", 54 | ], 55 | ) 56 | -------------------------------------------------------------------------------- /example/android/app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: "com.android.application" 2 | 3 | import com.android.build.OutputFile 4 | 5 | /** 6 | * The react.gradle file registers a task for each build variant (e.g. bundleDebugJsAndAssets 7 | * and bundleReleaseJsAndAssets). 8 | * These basically call `react-native bundle` with the correct arguments during the Android build 9 | * cycle. By default, bundleDebugJsAndAssets is skipped, as in debug/dev mode we prefer to load the 10 | * bundle directly from the development server. Below you can see all the possible configurations 11 | * and their defaults. If you decide to add a configuration block, make sure to add it before the 12 | * `apply from: "../../node_modules/react-native/react.gradle"` line. 13 | * 14 | * project.ext.react = [ 15 | * // the name of the generated asset file containing your JS bundle 16 | * bundleAssetName: "index.android.bundle", 17 | * 18 | * // the entry file for bundle generation. If none specified and 19 | * // "index.android.js" exists, it will be used. Otherwise "index.js" is 20 | * // default. Can be overridden with ENTRY_FILE environment variable. 21 | * entryFile: "index.android.js", 22 | * 23 | * // https://reactnative.dev/docs/performance#enable-the-ram-format 24 | * bundleCommand: "ram-bundle", 25 | * 26 | * // whether to bundle JS and assets in debug mode 27 | * bundleInDebug: false, 28 | * 29 | * // whether to bundle JS and assets in release mode 30 | * bundleInRelease: true, 31 | * 32 | * // whether to bundle JS and assets in another build variant (if configured). 33 | * // See http://tools.android.com/tech-docs/new-build-system/user-guide#TOC-Build-Variants 34 | * // The configuration property can be in the following formats 35 | * // 'bundleIn${productFlavor}${buildType}' 36 | * // 'bundleIn${buildType}' 37 | * // bundleInFreeDebug: true, 38 | * // bundleInPaidRelease: true, 39 | * // bundleInBeta: true, 40 | * 41 | * // whether to disable dev mode in custom build variants (by default only disabled in release) 42 | * // for example: to disable dev mode in the staging build type (if configured) 43 | * devDisabledInStaging: true, 44 | * // The configuration property can be in the following formats 45 | * // 'devDisabledIn${productFlavor}${buildType}' 46 | * // 'devDisabledIn${buildType}' 47 | * 48 | * // the root of your project, i.e. where "package.json" lives 49 | * root: "../../", 50 | * 51 | * // where to put the JS bundle asset in debug mode 52 | * jsBundleDirDebug: "$buildDir/intermediates/assets/debug", 53 | * 54 | * // where to put the JS bundle asset in release mode 55 | * jsBundleDirRelease: "$buildDir/intermediates/assets/release", 56 | * 57 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 58 | * // require('./image.png')), in debug mode 59 | * resourcesDirDebug: "$buildDir/intermediates/res/merged/debug", 60 | * 61 | * // where to put drawable resources / React Native assets, e.g. the ones you use via 62 | * // require('./image.png')), in release mode 63 | * resourcesDirRelease: "$buildDir/intermediates/res/merged/release", 64 | * 65 | * // by default the gradle tasks are skipped if none of the JS files or assets change; this means 66 | * // that we don't look at files in android/ or ios/ to determine whether the tasks are up to 67 | * // date; if you have any other folders that you want to ignore for performance reasons (gradle 68 | * // indexes the entire tree), add them here. Alternatively, if you have JS files in android/ 69 | * // for example, you might want to remove it from here. 70 | * inputExcludes: ["android/**", "ios/**"], 71 | * 72 | * // override which node gets called and with what additional arguments 73 | * nodeExecutableAndArgs: ["node"], 74 | * 75 | * // supply additional arguments to the packager 76 | * extraPackagerArgs: [] 77 | * ] 78 | */ 79 | 80 | project.ext.react = [ 81 | enableHermes: false, // clean and rebuild if changing 82 | ] 83 | 84 | apply from: "../../node_modules/react-native/react.gradle" 85 | 86 | /** 87 | * Set this to true to create two separate APKs instead of one: 88 | * - An APK that only works on ARM devices 89 | * - An APK that only works on x86 devices 90 | * The advantage is the size of the APK is reduced by about 4MB. 91 | * Upload all the APKs to the Play Store and people will download 92 | * the correct one based on the CPU architecture of their device. 93 | */ 94 | def enableSeparateBuildPerCPUArchitecture = false 95 | 96 | /** 97 | * Run Proguard to shrink the Java bytecode in release builds. 98 | */ 99 | def enableProguardInReleaseBuilds = false 100 | 101 | /** 102 | * The preferred build flavor of JavaScriptCore. 103 | * 104 | * For example, to use the international variant, you can use: 105 | * `def jscFlavor = 'org.webkit:android-jsc-intl:+'` 106 | * 107 | * The international variant includes ICU i18n library and necessary data 108 | * allowing to use e.g. `Date.toLocaleString` and `String.localeCompare` that 109 | * give correct results when using with locales other than en-US. Note that 110 | * this variant is about 6MiB larger per architecture than default. 111 | */ 112 | def jscFlavor = 'org.webkit:android-jsc:+' 113 | 114 | /** 115 | * Whether to enable the Hermes VM. 116 | * 117 | * This should be set on project.ext.react and mirrored here. If it is not set 118 | * on project.ext.react, JavaScript will not be compiled to Hermes Bytecode 119 | * and the benefits of using Hermes will therefore be sharply reduced. 120 | */ 121 | def enableHermes = project.ext.react.get("enableHermes", false); 122 | 123 | android { 124 | compileSdkVersion rootProject.ext.compileSdkVersion 125 | 126 | compileOptions { 127 | sourceCompatibility JavaVersion.VERSION_1_8 128 | targetCompatibility JavaVersion.VERSION_1_8 129 | } 130 | 131 | defaultConfig { 132 | applicationId "com.example" 133 | minSdkVersion rootProject.ext.minSdkVersion 134 | targetSdkVersion rootProject.ext.targetSdkVersion 135 | versionCode 1 136 | versionName "1.0" 137 | multiDexEnabled true 138 | } 139 | splits { 140 | abi { 141 | reset() 142 | enable enableSeparateBuildPerCPUArchitecture 143 | universalApk false // If true, also generate a universal APK 144 | include "armeabi-v7a", "x86", "arm64-v8a", "x86_64" 145 | } 146 | } 147 | buildTypes { 148 | release { 149 | // Caution! In production, you need to generate your own keystore file. 150 | // see https://reactnative.dev/docs/signed-apk-android. 151 | signingConfig signingConfigs.debug 152 | minifyEnabled enableProguardInReleaseBuilds 153 | proguardFiles getDefaultProguardFile("proguard-android.txt"), "proguard-rules.pro" 154 | } 155 | } 156 | 157 | // applicationVariants are e.g. debug, release 158 | applicationVariants.all { variant -> 159 | variant.outputs.each { output -> 160 | // For each separate APK per architecture, set a unique version code as described here: 161 | // https://developer.android.com/studio/build/configure-apk-splits.html 162 | def versionCodes = ["armeabi-v7a": 1, "x86": 2, "arm64-v8a": 3, "x86_64": 4] 163 | def abi = output.getFilter(OutputFile.ABI) 164 | if (abi != null) { // null for the universal-debug, universal-release variants 165 | output.versionCodeOverride = 166 | versionCodes.get(abi) * 1048576 + defaultConfig.versionCode 167 | } 168 | 169 | } 170 | } 171 | } 172 | 173 | dependencies { 174 | implementation fileTree(dir: "libs", include: ["*.jar"]) 175 | //noinspection GradleDynamicVersion 176 | implementation "com.facebook.react:react-native:+" // From node_modules 177 | 178 | implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.0.0" 179 | 180 | implementation 'com.facebook.android:facebook-android-sdk:[5,6)' 181 | 182 | implementation 'com.android.support:multidex:1.0.3' 183 | 184 | debugImplementation("com.facebook.flipper:flipper:${FLIPPER_VERSION}") { 185 | exclude group:'com.facebook.fbjni' 186 | } 187 | 188 | debugImplementation("com.facebook.flipper:flipper-network-plugin:${FLIPPER_VERSION}") { 189 | exclude group:'com.facebook.flipper' 190 | exclude group:'com.squareup.okhttp3', module:'okhttp' 191 | } 192 | 193 | debugImplementation("com.facebook.flipper:flipper-fresco-plugin:${FLIPPER_VERSION}") { 194 | exclude group:'com.facebook.flipper' 195 | } 196 | 197 | if (enableHermes) { 198 | def hermesPath = "../../node_modules/hermes-engine/android/"; 199 | debugImplementation files(hermesPath + "hermes-debug.aar") 200 | releaseImplementation files(hermesPath + "hermes-release.aar") 201 | } else { 202 | implementation jscFlavor 203 | } 204 | } 205 | 206 | // Run this once to be able to run the application with BUCK 207 | // puts all compile dependencies into folder libs for BUCK to use 208 | task copyDownloadableDepsToLibs(type: Copy) { 209 | from configurations.compile 210 | into 'libs' 211 | } 212 | 213 | apply from: file("../../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesAppBuildGradle(project) 214 | -------------------------------------------------------------------------------- /example/android/app/build_defs.bzl: -------------------------------------------------------------------------------- 1 | """Helper definitions to glob .aar and .jar targets""" 2 | 3 | def create_aar_targets(aarfiles): 4 | for aarfile in aarfiles: 5 | name = "aars__" + aarfile[aarfile.rindex("/") + 1:aarfile.rindex(".aar")] 6 | lib_deps.append(":" + name) 7 | android_prebuilt_aar( 8 | name = name, 9 | aar = aarfile, 10 | ) 11 | 12 | def create_jar_targets(jarfiles): 13 | for jarfile in jarfiles: 14 | name = "jars__" + jarfile[jarfile.rindex("/") + 1:jarfile.rindex(".jar")] 15 | lib_deps.append(":" + name) 16 | prebuilt_jar( 17 | name = name, 18 | binary_jar = jarfile, 19 | ) 20 | -------------------------------------------------------------------------------- /example/android/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/debug.keystore -------------------------------------------------------------------------------- /example/android/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | -------------------------------------------------------------------------------- /example/android/app/src/debug/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/app/src/debug/java/com/example/ReactNativeFlipper.java: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Facebook, Inc. and its affiliates. 3 | * 4 | *

This source code is licensed under the MIT license found in the LICENSE file in the root 5 | * directory of this source tree. 6 | */ 7 | package com.example; 8 | 9 | import android.content.Context; 10 | import com.facebook.flipper.android.AndroidFlipperClient; 11 | import com.facebook.flipper.android.utils.FlipperUtils; 12 | import com.facebook.flipper.core.FlipperClient; 13 | import com.facebook.flipper.plugins.crashreporter.CrashReporterPlugin; 14 | import com.facebook.flipper.plugins.databases.DatabasesFlipperPlugin; 15 | import com.facebook.flipper.plugins.fresco.FrescoFlipperPlugin; 16 | import com.facebook.flipper.plugins.inspector.DescriptorMapping; 17 | import com.facebook.flipper.plugins.inspector.InspectorFlipperPlugin; 18 | import com.facebook.flipper.plugins.network.FlipperOkhttpInterceptor; 19 | import com.facebook.flipper.plugins.network.NetworkFlipperPlugin; 20 | import com.facebook.flipper.plugins.react.ReactFlipperPlugin; 21 | import com.facebook.flipper.plugins.sharedpreferences.SharedPreferencesFlipperPlugin; 22 | import com.facebook.react.ReactInstanceManager; 23 | import com.facebook.react.bridge.ReactContext; 24 | import com.facebook.react.modules.network.NetworkingModule; 25 | import okhttp3.OkHttpClient; 26 | 27 | public class ReactNativeFlipper { 28 | public static void initializeFlipper(Context context, ReactInstanceManager reactInstanceManager) { 29 | if (FlipperUtils.shouldEnableFlipper(context)) { 30 | final FlipperClient client = AndroidFlipperClient.getInstance(context); 31 | 32 | client.addPlugin(new InspectorFlipperPlugin(context, DescriptorMapping.withDefaults())); 33 | client.addPlugin(new ReactFlipperPlugin()); 34 | client.addPlugin(new DatabasesFlipperPlugin(context)); 35 | client.addPlugin(new SharedPreferencesFlipperPlugin(context)); 36 | client.addPlugin(CrashReporterPlugin.getInstance()); 37 | 38 | NetworkFlipperPlugin networkFlipperPlugin = new NetworkFlipperPlugin(); 39 | NetworkingModule.setCustomClientBuilder( 40 | new NetworkingModule.CustomClientBuilder() { 41 | @Override 42 | public void apply(OkHttpClient.Builder builder) { 43 | builder.addNetworkInterceptor(new FlipperOkhttpInterceptor(networkFlipperPlugin)); 44 | } 45 | }); 46 | client.addPlugin(networkFlipperPlugin); 47 | client.start(); 48 | 49 | // Fresco Plugin needs to ensure that ImagePipelineFactory is initialized 50 | // Hence we run if after all native modules have been initialized 51 | ReactContext reactContext = reactInstanceManager.getCurrentReactContext(); 52 | if (reactContext == null) { 53 | reactInstanceManager.addReactInstanceEventListener( 54 | new ReactInstanceManager.ReactInstanceEventListener() { 55 | @Override 56 | public void onReactContextInitialized(ReactContext reactContext) { 57 | reactInstanceManager.removeReactInstanceEventListener(this); 58 | reactContext.runOnNativeModulesQueueThread( 59 | new Runnable() { 60 | @Override 61 | public void run() { 62 | client.addPlugin(new FrescoFlipperPlugin()); 63 | } 64 | }); 65 | } 66 | }); 67 | } else { 68 | client.addPlugin(new FrescoFlipperPlugin()); 69 | } 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /example/android/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 13 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/AntDesign.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/AntDesign.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Entypo.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Entypo.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/EvilIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/EvilIcons.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Feather.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Feather.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/FontAwesome.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/FontAwesome.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/FontAwesome5_Brands.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/FontAwesome5_Regular.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/FontAwesome5_Solid.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Fontisto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Fontisto.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Foundation.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Foundation.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Ionicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Ionicons.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/MaterialCommunityIcons.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/MaterialIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/MaterialIcons.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Octicons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Octicons.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Roboto.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Roboto.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Roboto_medium.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Roboto_medium.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/SimpleLineIcons.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/SimpleLineIcons.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/Zocial.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/Zocial.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/assets/fonts/rubicon-icon-font.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/assets/fonts/rubicon-icon-font.ttf -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainActivity.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import com.facebook.react.ReactActivity; 4 | 5 | public class MainActivity extends ReactActivity { 6 | 7 | /** 8 | * Returns the name of the main component registered from JavaScript. This is used to schedule 9 | * rendering of the component. 10 | */ 11 | @Override 12 | protected String getMainComponentName() { 13 | return "example"; 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /example/android/app/src/main/java/com/example/MainApplication.java: -------------------------------------------------------------------------------- 1 | package com.example; 2 | 3 | import android.app.Application; 4 | import android.content.Context; 5 | import com.facebook.react.PackageList; 6 | import com.facebook.react.ReactApplication; 7 | import com.facebook.reactnative.androidsdk.FBSDKPackage; 8 | import suraj.tiwari.reactnativefbads.FBAdsPackage; 9 | import com.facebook.react.ReactInstanceManager; 10 | import com.facebook.react.ReactNativeHost; 11 | import com.facebook.react.ReactPackage; 12 | import com.facebook.soloader.SoLoader; 13 | import java.lang.reflect.InvocationTargetException; 14 | import java.util.List; 15 | 16 | public class MainApplication extends Application implements ReactApplication { 17 | 18 | private final ReactNativeHost mReactNativeHost = 19 | new ReactNativeHost(this) { 20 | @Override 21 | public boolean getUseDeveloperSupport() { 22 | return BuildConfig.DEBUG; 23 | } 24 | 25 | @Override 26 | protected List getPackages() { 27 | @SuppressWarnings("UnnecessaryLocalVariable") 28 | List packages = new PackageList(this).getPackages(); 29 | // Packages that cannot be autolinked yet can be added manually here, for example: 30 | // packages.add(new MyReactNativePackage()); 31 | return packages; 32 | } 33 | 34 | @Override 35 | protected String getJSMainModuleName() { 36 | return "index"; 37 | } 38 | }; 39 | 40 | @Override 41 | public ReactNativeHost getReactNativeHost() { 42 | return mReactNativeHost; 43 | } 44 | 45 | @Override 46 | public void onCreate() { 47 | super.onCreate(); 48 | SoLoader.init(this, /* native exopackage */ false); 49 | initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 50 | } 51 | 52 | /** 53 | * Loads Flipper in React Native templates. Call this in the onCreate method with something like 54 | * initializeFlipper(this, getReactNativeHost().getReactInstanceManager()); 55 | * 56 | * @param context 57 | * @param reactInstanceManager 58 | */ 59 | private static void initializeFlipper( 60 | Context context, ReactInstanceManager reactInstanceManager) { 61 | if (BuildConfig.DEBUG) { 62 | try { 63 | /* 64 | We use reflection here to pick up the class that initializes Flipper, 65 | since Flipper library is not available in release mode 66 | */ 67 | Class aClass = Class.forName("com.example.ReactNativeFlipper"); 68 | aClass 69 | .getMethod("initializeFlipper", Context.class, ReactInstanceManager.class) 70 | .invoke(null, context, reactInstanceManager); 71 | } catch (ClassNotFoundException e) { 72 | e.printStackTrace(); 73 | } catch (NoSuchMethodException e) { 74 | e.printStackTrace(); 75 | } catch (IllegalAccessException e) { 76 | e.printStackTrace(); 77 | } catch (InvocationTargetException e) { 78 | e.printStackTrace(); 79 | } 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | example 3 | Facebook App ID 4 | 5 | -------------------------------------------------------------------------------- /example/android/app/src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /example/android/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | 3 | buildscript { 4 | ext { 5 | buildToolsVersion = "29.0.2" 6 | minSdkVersion = 16 7 | compileSdkVersion = 29 8 | targetSdkVersion = 29 9 | } 10 | repositories { 11 | google() 12 | jcenter() 13 | mavenCentral() 14 | } 15 | dependencies { 16 | classpath("com.android.tools.build:gradle:3.5.3") 17 | // NOTE: Do not place your application dependencies here; they belong 18 | // in the individual module build.gradle files 19 | } 20 | } 21 | 22 | allprojects { 23 | repositories { 24 | mavenLocal() 25 | maven { 26 | // All of React Native (JS, Obj-C sources, Android binaries) is installed from npm 27 | url("$rootDir/../node_modules/react-native/android") 28 | } 29 | maven { 30 | // Android JSC is installed from npm 31 | url("$rootDir/../node_modules/jsc-android/dist") 32 | } 33 | 34 | google() 35 | jcenter() 36 | maven { url 'https://www.jitpack.io' } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /example/android/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | 3 | # IDE (e.g. Android Studio) users: 4 | # Gradle settings configured through the IDE *will override* 5 | # any settings specified in this file. 6 | 7 | # For more details on how to configure your build environment visit 8 | # http://www.gradle.org/docs/current/userguide/build_environment.html 9 | 10 | # Specifies the JVM arguments used for the daemon process. 11 | # The setting is particularly useful for tweaking memory settings. 12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m 13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8 14 | 15 | # When configured, Gradle will run in incubating parallel mode. 16 | # This option should only be used with decoupled projects. More details, visit 17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 18 | # org.gradle.parallel=true 19 | 20 | # AndroidX package structure to make it clearer which packages are bundled with the 21 | # Android operating system, and which are packaged with your app's APK 22 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 23 | android.useAndroidX=true 24 | # Automatically convert third-party libraries to use AndroidX 25 | android.enableJetifier=true 26 | 27 | # Version of flipper SDK to use with React Native 28 | FLIPPER_VERSION=0.54.0 29 | -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/example/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /example/android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-6.2-all.zip 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | -------------------------------------------------------------------------------- /example/android/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | # Determine the Java command to use to start the JVM. 86 | if [ -n "$JAVA_HOME" ] ; then 87 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 88 | # IBM's JDK on AIX uses strange locations for the executables 89 | JAVACMD="$JAVA_HOME/jre/sh/java" 90 | else 91 | JAVACMD="$JAVA_HOME/bin/java" 92 | fi 93 | if [ ! -x "$JAVACMD" ] ; then 94 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 95 | 96 | Please set the JAVA_HOME variable in your environment to match the 97 | location of your Java installation." 98 | fi 99 | else 100 | JAVACMD="java" 101 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 102 | 103 | Please set the JAVA_HOME variable in your environment to match the 104 | location of your Java installation." 105 | fi 106 | 107 | # Increase the maximum file descriptors if we can. 108 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 109 | MAX_FD_LIMIT=`ulimit -H -n` 110 | if [ $? -eq 0 ] ; then 111 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 112 | MAX_FD="$MAX_FD_LIMIT" 113 | fi 114 | ulimit -n $MAX_FD 115 | if [ $? -ne 0 ] ; then 116 | warn "Could not set maximum file descriptor limit: $MAX_FD" 117 | fi 118 | else 119 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 120 | fi 121 | fi 122 | 123 | # For Darwin, add options to specify how the application appears in the dock 124 | if $darwin; then 125 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 126 | fi 127 | 128 | # For Cygwin or MSYS, switch paths to Windows format before running java 129 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 130 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 131 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 132 | JAVACMD=`cygpath --unix "$JAVACMD"` 133 | 134 | # We build the pattern for arguments to be converted via cygpath 135 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 136 | SEP="" 137 | for dir in $ROOTDIRSRAW ; do 138 | ROOTDIRS="$ROOTDIRS$SEP$dir" 139 | SEP="|" 140 | done 141 | OURCYGPATTERN="(^($ROOTDIRS))" 142 | # Add a user-defined pattern to the cygpath arguments 143 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 144 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 145 | fi 146 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 147 | i=0 148 | for arg in "$@" ; do 149 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 150 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 151 | 152 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 153 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 154 | else 155 | eval `echo args$i`="\"$arg\"" 156 | fi 157 | i=`expr $i + 1` 158 | done 159 | case $i in 160 | 0) set -- ;; 161 | 1) set -- "$args0" ;; 162 | 2) set -- "$args0" "$args1" ;; 163 | 3) set -- "$args0" "$args1" "$args2" ;; 164 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 165 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 166 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 167 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 168 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 169 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 170 | esac 171 | fi 172 | 173 | # Escape application args 174 | save () { 175 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 176 | echo " " 177 | } 178 | APP_ARGS=`save "$@"` 179 | 180 | # Collect all arguments for the java command, following the shell quoting and substitution rules 181 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 182 | 183 | exec "$JAVACMD" "$@" 184 | -------------------------------------------------------------------------------- /example/android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto init 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto init 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :init 68 | @rem Get command-line arguments, handling Windows variants 69 | 70 | if not "%OS%" == "Windows_NT" goto win9xME_args 71 | 72 | :win9xME_args 73 | @rem Slurp the command line arguments. 74 | set CMD_LINE_ARGS= 75 | set _SKIP=2 76 | 77 | :win9xME_args_slurp 78 | if "x%~1" == "x" goto execute 79 | 80 | set CMD_LINE_ARGS=%* 81 | 82 | :execute 83 | @rem Setup the command line 84 | 85 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 86 | 87 | @rem Execute Gradle 88 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 89 | 90 | :end 91 | @rem End local scope for the variables with windows NT shell 92 | if "%ERRORLEVEL%"=="0" goto mainEnd 93 | 94 | :fail 95 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 96 | rem the _cmd.exe /c_ return code! 97 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 98 | exit /b 1 99 | 100 | :mainEnd 101 | if "%OS%"=="Windows_NT" endlocal 102 | 103 | :omega 104 | -------------------------------------------------------------------------------- /example/android/settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'example' 2 | apply from: file("../node_modules/@react-native-community/cli-platform-android/native_modules.gradle"); applyNativeModulesSettingsGradle(settings) 3 | include ':app' 4 | -------------------------------------------------------------------------------- /example/app.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "displayName": "example" 4 | } -------------------------------------------------------------------------------- /example/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /example/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @format 3 | */ 4 | 5 | import {AppRegistry} from 'react-native'; 6 | import App from './App'; 7 | import {name as appName} from './app.json'; 8 | 9 | AppRegistry.registerComponent(appName, () => App); 10 | -------------------------------------------------------------------------------- /example/ios/File.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require_relative '../node_modules/react-native/scripts/react_native_pods' 2 | require_relative '../node_modules/@react-native-community/cli-platform-ios/native_modules' 3 | 4 | platform :ios, '10.0' 5 | 6 | target 'example' do 7 | 8 | config = use_native_modules! 9 | 10 | use_react_native!(:path => config["reactNativePath"]) 11 | 12 | target 'exampleTests' do 13 | inherit! :complete 14 | # Pods for testing 15 | end 16 | 17 | # Enables Flipper. 18 | # 19 | # Note that if you have use_frameworks! enabled, Flipper will not work and 20 | # you should disable these next few lines. 21 | use_flipper! 22 | post_install do |installer| 23 | flipper_post_install(installer) 24 | end 25 | end 26 | 27 | target 'example-tvOS' do 28 | # Pods for example-tvOS 29 | 30 | target 'example-tvOSTests' do 31 | inherit! :search_paths 32 | # Pods for testing 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /example/ios/example-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | 5 | -------------------------------------------------------------------------------- /example/ios/example-tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | NSAppTransportSecurity 26 | 27 | NSExceptionDomains 28 | 29 | localhost 30 | 31 | NSExceptionAllowsInsecureHTTPLoads 32 | 33 | 34 | 35 | 36 | NSLocationWhenInUseUsageDescription 37 | 38 | UILaunchStoryboardName 39 | LaunchScreen 40 | UIRequiredDeviceCapabilities 41 | 42 | armv7 43 | 44 | UISupportedInterfaceOrientations 45 | 46 | UIInterfaceOrientationPortrait 47 | UIInterfaceOrientationLandscapeLeft 48 | UIInterfaceOrientationLandscapeRight 49 | 50 | UIViewControllerBasedStatusBarAppearance 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /example/ios/example-tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example-tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface AppDelegate : UIResponder 5 | 6 | @property (nonatomic, strong) UIWindow *window; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.m: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | #import 6 | 7 | #ifdef FB_SONARKIT_ENABLED 8 | #import 9 | #import 10 | #import 11 | #import 12 | #import 13 | #import 14 | 15 | static void InitializeFlipper(UIApplication *application) { 16 | FlipperClient *client = [FlipperClient sharedClient]; 17 | SKDescriptorMapper *layoutDescriptorMapper = [[SKDescriptorMapper alloc] initWithDefaults]; 18 | [client addPlugin:[[FlipperKitLayoutPlugin alloc] initWithRootNode:application withDescriptorMapper:layoutDescriptorMapper]]; 19 | [client addPlugin:[[FKUserDefaultsPlugin alloc] initWithSuiteName:nil]]; 20 | [client addPlugin:[FlipperKitReactPlugin new]]; 21 | [client addPlugin:[[FlipperKitNetworkPlugin alloc] initWithNetworkAdapter:[SKIOSNetworkAdapter new]]]; 22 | [client start]; 23 | } 24 | #endif 25 | 26 | @implementation AppDelegate 27 | 28 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 29 | { 30 | #ifdef FB_SONARKIT_ENABLED 31 | InitializeFlipper(application); 32 | #endif 33 | 34 | RCTBridge *bridge = [[RCTBridge alloc] initWithDelegate:self launchOptions:launchOptions]; 35 | RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge 36 | moduleName:@"example" 37 | initialProperties:nil]; 38 | 39 | rootView.backgroundColor = [[UIColor alloc] initWithRed:1.0f green:1.0f blue:1.0f alpha:1]; 40 | 41 | self.window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; 42 | UIViewController *rootViewController = [UIViewController new]; 43 | rootViewController.view = rootView; 44 | self.window.rootViewController = rootViewController; 45 | [self.window makeKeyAndVisible]; 46 | return YES; 47 | } 48 | 49 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 50 | { 51 | #if DEBUG 52 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@"index" fallbackResource:nil]; 53 | #else 54 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 55 | #endif 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ios-marketing", 45 | "scale" : "1x", 46 | "size" : "1024x1024" 47 | } 48 | ], 49 | "info" : { 50 | "author" : "xcode", 51 | "version" : 1 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | LSRequiresIPhoneOS 26 | 27 | NSAppTransportSecurity 28 | 29 | NSAllowsArbitraryLoads 30 | 31 | NSExceptionDomains 32 | 33 | localhost 34 | 35 | NSExceptionAllowsInsecureHTTPLoads 36 | 37 | 38 | 39 | 40 | NSLocationWhenInUseUsageDescription 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIRequiredDeviceCapabilities 45 | 46 | armv7 47 | 48 | UISupportedInterfaceOrientations 49 | 50 | UIInterfaceOrientationPortrait 51 | UIInterfaceOrientationLandscapeLeft 52 | UIInterfaceOrientationLandscapeRight 53 | 54 | UIViewControllerBasedStatusBarAppearance 55 | 56 | NSUserTrackingUsageDescription 57 | Testing purposes 58 | UIAppFonts 59 | 60 | AntDesign.ttf 61 | Entypo.ttf 62 | EvilIcons.ttf 63 | Feather.ttf 64 | FontAwesome.ttf 65 | FontAwesome5_Brands.ttf 66 | FontAwesome5_Regular.ttf 67 | FontAwesome5_Solid.ttf 68 | Fontisto.ttf 69 | Foundation.ttf 70 | Ionicons.ttf 71 | MaterialCommunityIcons.ttf 72 | MaterialIcons.ttf 73 | Octicons.ttf 74 | Roboto_medium.ttf 75 | Roboto.ttf 76 | rubicon-icon-font.ttf 77 | SimpleLineIcons.ttf 78 | Zocial.ttf 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /example/ios/example/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 31 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /example/ios/exampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /example/ios/exampleTests/exampleTests.m: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | #import 5 | #import 6 | 7 | #define TIMEOUT_SECONDS 600 8 | #define TEXT_TO_LOOK_FOR @"Welcome to React" 9 | 10 | @interface exampleTests : XCTestCase 11 | 12 | @end 13 | 14 | @implementation exampleTests 15 | 16 | - (BOOL)findSubviewInView:(UIView *)view matching:(BOOL(^)(UIView *view))test 17 | { 18 | if (test(view)) { 19 | return YES; 20 | } 21 | for (UIView *subview in [view subviews]) { 22 | if ([self findSubviewInView:subview matching:test]) { 23 | return YES; 24 | } 25 | } 26 | return NO; 27 | } 28 | 29 | - (void)testRendersWelcomeScreen 30 | { 31 | UIViewController *vc = [[[RCTSharedApplication() delegate] window] rootViewController]; 32 | NSDate *date = [NSDate dateWithTimeIntervalSinceNow:TIMEOUT_SECONDS]; 33 | BOOL foundElement = NO; 34 | 35 | __block NSString *redboxError = nil; 36 | #ifdef DEBUG 37 | RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) { 38 | if (level >= RCTLogLevelError) { 39 | redboxError = message; 40 | } 41 | }); 42 | #endif 43 | 44 | while ([date timeIntervalSinceNow] > 0 && !foundElement && !redboxError) { 45 | [[NSRunLoop mainRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 46 | [[NSRunLoop mainRunLoop] runMode:NSRunLoopCommonModes beforeDate:[NSDate dateWithTimeIntervalSinceNow:0.1]]; 47 | 48 | foundElement = [self findSubviewInView:vc.view matching:^BOOL(UIView *view) { 49 | if ([view.accessibilityLabel isEqualToString:TEXT_TO_LOOK_FOR]) { 50 | return YES; 51 | } 52 | return NO; 53 | }]; 54 | } 55 | 56 | #ifdef DEBUG 57 | RCTSetLogFunction(RCTDefaultLogFunction); 58 | #endif 59 | 60 | XCTAssertNil(redboxError, @"RedBox error: %@", redboxError); 61 | XCTAssertTrue(foundElement, @"Couldn't find element with text '%@' in %d seconds", TEXT_TO_LOOK_FOR, TIMEOUT_SECONDS); 62 | } 63 | 64 | 65 | @end 66 | -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Metro configuration for React Native 3 | * https://github.com/facebook/react-native 4 | * 5 | * @format 6 | */ 7 | 8 | module.exports = { 9 | transformer: { 10 | getTransformOptions: async () => ({ 11 | transform: { 12 | experimentalImportSupport: false, 13 | inlineRequires: false, 14 | }, 15 | }), 16 | }, 17 | }; 18 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "version": "0.0.1", 4 | "private": true, 5 | "scripts": { 6 | "android": "react-native run-android", 7 | "ios": "react-native run-ios", 8 | "start": "react-native start", 9 | "test": "jest", 10 | "lint": "eslint ." 11 | }, 12 | "dependencies": { 13 | "native-base": "^2.8.0", 14 | "react": "16.13.1", 15 | "react-native": "0.63.4", 16 | "react-native-fbads": "file:..", 17 | "react-native-fbsdk": "^3.0.0", 18 | "react-native-gesture-handler": "^1.9.0", 19 | "react-native-reanimated": "^1.13.2", 20 | "react-native-router-flux": "^4.0.1", 21 | "react-native-screens": "^2.15.2" 22 | }, 23 | "devDependencies": { 24 | "@babel/core": "^7.12.10", 25 | "@babel/runtime": "^7.12.5", 26 | "@react-native-community/eslint-config": "^2.0.0", 27 | "babel-jest": "^26.6.3", 28 | "eslint": "^7.18.0", 29 | "jest": "^26.6.3", 30 | "metro-react-native-babel-preset": "^0.64.0", 31 | "react-test-renderer": "16.13.1" 32 | }, 33 | "jest": { 34 | "preset": "react-native" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /example/src/BannerAds/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {StyleSheet, View} from 'react-native'; 3 | import {Container} from 'native-base'; 4 | import {BannerView} from 'react-native-fbads'; 5 | import {bannerAdPlacementId} from '../Variables'; 6 | 7 | export default class BannerAd extends Component { 8 | render() { 9 | return ( 10 | 11 | 12 | console.log('click')} 16 | onError={(err) => console.log('error', err)} 17 | /> 18 | 19 | 20 | console.log('click')} 24 | onError={(err) => console.log('error', err)} 25 | /> 26 | 27 | 28 | ); 29 | } 30 | } 31 | 32 | let styles = StyleSheet.create({ 33 | container: { 34 | justifyContent: 'center', 35 | }, 36 | bannerContainer: { 37 | marginVertical: 10, 38 | }, 39 | }); 40 | -------------------------------------------------------------------------------- /example/src/InterstitialAds/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {StyleSheet, Dimensions, TouchableHighlight} from 'react-native'; 3 | import {Container, Text} from 'native-base'; 4 | import {InterstitialAdManager} from 'react-native-fbads'; 5 | import {InterstitialAdPlacementId} from '../Variables'; 6 | 7 | const {width} = Dimensions.get('window'); 8 | 9 | export default class InterstitialAd extends Component { 10 | render() { 11 | return ( 12 | 13 | this._showAd()}> 17 | Show Interstitial Ad 18 | 19 | 20 | ); 21 | } 22 | 23 | _showAd() { 24 | InterstitialAdManager.showAd(InterstitialAdPlacementId) 25 | .then(() => {}) 26 | .catch(() => {}); 27 | } 28 | } 29 | 30 | let styles = StyleSheet.create({ 31 | container: { 32 | width, 33 | position: 'absolute', 34 | justifyContent: 'center', 35 | alignItems: 'center', 36 | }, 37 | button: { 38 | elevation: 3, 39 | borderRadius: 10, 40 | paddingVertical: 10, 41 | width: width - 80, 42 | alignItems: 'center', 43 | marginVertical: 5, 44 | backgroundColor: '#fff', 45 | justifyContent: 'center', 46 | }, 47 | buttonText: { 48 | color: '#a70f0a', 49 | }, 50 | }); 51 | -------------------------------------------------------------------------------- /example/src/NativeAds/NativeAdView.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {Text, View, Dimensions} from 'react-native'; 3 | import { 4 | withNativeAd, 5 | AdIconView, 6 | TriggerableView, 7 | MediaView, 8 | } from 'react-native-fbads'; 9 | 10 | const {width} = Dimensions.get('window'); 11 | 12 | class NativeAdView extends Component { 13 | render() { 14 | return ( 15 | 16 | 17 | 18 | 19 | 21 | 22 | {this.props.nativeAd.headline} 23 | 24 | {this.props.nativeAd.sponsoredTranslation} 25 | 26 | {this.props.nativeAd.linkDescription} 27 | 28 | 29 | 30 | 31 | 42 | {this.props.nativeAd.callToActionText} 43 | 44 | 45 | 46 | ); 47 | } 48 | } 49 | 50 | export default withNativeAd(NativeAdView); 51 | -------------------------------------------------------------------------------- /example/src/NativeAds/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {NativeAdsManager, AdSettings} from 'react-native-fbads'; 3 | import {Container} from 'native-base'; 4 | import {nativeAdPlacementId} from '../Variables'; 5 | 6 | import NativeAdView from './NativeAdView'; 7 | 8 | export default class NativeAd extends Component { 9 | adsManager = new NativeAdsManager(nativeAdPlacementId); 10 | 11 | render() { 12 | return ( 13 | 19 | 23 | 24 | ); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /example/src/Variables/index.js: -------------------------------------------------------------------------------- 1 | const nativeAdPlacementId = '2122084034714087_2122088321380325'; 2 | const bannerAdPlacementId = ''; 3 | const InterstitialAdPlacementId = ''; 4 | 5 | export {nativeAdPlacementId, bannerAdPlacementId, InterstitialAdPlacementId}; 6 | -------------------------------------------------------------------------------- /example/src/index.js: -------------------------------------------------------------------------------- 1 | import React, {Component} from 'react'; 2 | import {StyleSheet, Dimensions, TouchableHighlight} from 'react-native'; 3 | import {Container, Text} from 'native-base'; 4 | import {Actions} from 'react-native-router-flux'; 5 | import {AdSettings} from 'react-native-fbads'; 6 | 7 | const {width} = Dimensions.get('window'); 8 | 9 | export default class Main extends Component { 10 | async componentDidMount() { 11 | AdSettings.setLogLevel('debug'); 12 | AdSettings.addTestDevice(AdSettings.currentDeviceHash); 13 | const requestedStatus = await AdSettings.requestTrackingPermission(); 14 | 15 | if (requestedStatus === 'authorized' || requestedStatus === 'unavailable') { 16 | AdSettings.setAdvertiserIDCollectionEnabled(true); 17 | // Both calls are not related to each other 18 | // FB won't deliver any ads if this is set to false or not called at all. 19 | AdSettings.setAdvertiserTrackingEnabled(true); 20 | } 21 | } 22 | 23 | componentWillUnmount() { 24 | AdSettings.clearTestDevices(); 25 | } 26 | render() { 27 | return ( 28 | 29 | Actions.nativeAd()}> 33 | Native Ad 34 | 35 | Actions.bannerAd()} 38 | style={styles.button}> 39 | Banner Ad 40 | 41 | Actions.interstitialAd()} 44 | style={styles.button}> 45 | Interstitial Ad 46 | 47 | 48 | ); 49 | } 50 | } 51 | 52 | let styles = StyleSheet.create({ 53 | container: { 54 | width, 55 | position: 'absolute', 56 | justifyContent: 'center', 57 | alignItems: 'center', 58 | }, 59 | button: { 60 | elevation: 3, 61 | borderRadius: 10, 62 | paddingVertical: 10, 63 | width: width - 80, 64 | alignItems: 'center', 65 | marginVertical: 5, 66 | backgroundColor: '#fff', 67 | justifyContent: 'center', 68 | }, 69 | buttonText: { 70 | color: '#a70f0a', 71 | }, 72 | }); 73 | -------------------------------------------------------------------------------- /images/nativeAd.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/images/nativeAd.png -------------------------------------------------------------------------------- /images/threed_mockup_fbNativeads.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/callstack/react-native-fbads/78935205b95cbd572dd1db3f849fc8fee4d5b5cf/images/threed_mockup_fbNativeads.png -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/AdChoiceManager.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdChoiceManager.h 3 | // ReactNativeAdsFacebook 4 | // 5 | // Created by Suraj Tiwari on 14/08/18. 6 | // Copyright © 2018 Suraj Tiwari . All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | 13 | @interface AdChoiceManager : RCTViewManager 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/AdChoiceManager.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdChoiceManager.m 3 | // ReactNativeAdsFacebook 4 | // 5 | // Created by Suraj Tiwari on 14/08/18. 6 | // Copyright © 2018 Suraj Tiwari . All rights reserved. 7 | // 8 | #import "AdChoiceManager.h" 9 | #import "EXNativeAdManager.h" 10 | #import "AdChoiceView.h" 11 | #import 12 | 13 | @implementation RCTConvert (AdChoiceView) 14 | 15 | RCT_ENUM_CONVERTER(UIRectCorner, (@{ 16 | @"topRight": @(UIRectCornerTopRight), 17 | @"topLeft": @(UIRectCornerTopLeft), 18 | @"bottomLeft": @(UIRectCornerBottomLeft), 19 | @"bottomRight": @(UIRectCornerBottomRight) 20 | }), UIRectCornerTopRight, intValue ) 21 | @end 22 | 23 | @implementation AdChoiceManager 24 | 25 | RCT_EXPORT_MODULE(AdChoicesView) 26 | 27 | @synthesize bridge = _bridge; 28 | 29 | - (instancetype)init 30 | { 31 | self = [super init]; 32 | return self; 33 | } 34 | 35 | - (UIView *)view 36 | { 37 | return [[AdChoiceView new] initWithBridge:_bridge]; 38 | } 39 | 40 | + (BOOL) requiresMainQueueSetup { 41 | return YES; 42 | } 43 | 44 | RCT_EXPORT_VIEW_PROPERTY(placementId,NSString); 45 | RCT_EXPORT_VIEW_PROPERTY(location,UIRectCorner); 46 | RCT_EXPORT_VIEW_PROPERTY(expandable,BOOL); 47 | 48 | @end 49 | 50 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/AdChoiceView.h: -------------------------------------------------------------------------------- 1 | // 2 | // AdChoiceView.h 3 | // ReactNativeAdsFacebook 4 | // 5 | // Created by Suraj Tiwari on 14/08/18. 6 | // Copyright © 2018 Suraj Tiwari . All rights reserved. 7 | // 8 | 9 | #import 10 | #import 11 | #import 12 | #import 13 | 14 | @interface AdChoiceView : RCTView 15 | 16 | @property (nonatomic, strong) NSString *placementId; 17 | @property (nonatomic) UIRectCorner location; 18 | @property (nonatomic) BOOL *expandable; 19 | 20 | - (instancetype)initWithBridge:(RCTBridge *)bridge; 21 | 22 | @end 23 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/AdChoiceView.m: -------------------------------------------------------------------------------- 1 | // 2 | // AdChoiceView.m 3 | // ReactNativeAdsFacebook 4 | // 5 | // Created by Suraj Tiwari on 14/08/18. 6 | // Copyright © 2018 Suraj Tiwari . All rights reserved. 7 | // 8 | 9 | #import "AdChoiceView.h" 10 | #import 11 | #import "EXNativeAdManager.h" 12 | 13 | @interface AdChoiceView () 14 | @property (nonatomic, strong) RCTBridge *bridge; 15 | @end 16 | 17 | @implementation AdChoiceView 18 | 19 | - (instancetype)initWithBridge:(RCTBridge *)bridge 20 | { 21 | if (self = [super init]) { 22 | _bridge = bridge; 23 | } 24 | return self; 25 | } 26 | 27 | - (void)setPlacementId:(NSString *)placementId 28 | { 29 | _placementId = placementId; 30 | [self createViewIfCan:_placementId:_location: _expandable]; 31 | } 32 | 33 | - (void)setLocation:(UIRectCorner)location 34 | { 35 | _location = location; 36 | [self createViewIfCan:_placementId:_location: _expandable]; 37 | } 38 | 39 | - (void)setExpandable:(BOOL *)expandable 40 | { 41 | _expandable = expandable; 42 | [self createViewIfCan:_placementId :_location :_expandable]; 43 | } 44 | 45 | - (void)createViewIfCan:(NSString *)placementId :(UIRectCorner) location :(BOOL) expandable 46 | { 47 | if (!location || !placementId) { 48 | return; 49 | } 50 | EXNativeAdManager *nativeAdManager = [_bridge moduleForClass:[EXNativeAdManager class]]; 51 | FBNativeAdsManager *_adsManager = [nativeAdManager getFBAdsManager:placementId]; 52 | FBNativeAd *nativeAd = [_adsManager nextNativeAd]; 53 | FBAdChoicesView *adChoicesView = [[FBAdChoicesView alloc] initWithNativeAd:nativeAd expandable:expandable]; 54 | [adChoicesView updateFrameFromSuperview:location]; 55 | [self addSubview:adChoicesView]; 56 | } 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXAdIconViewManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface EXAdIconViewManager : RCTViewManager 6 | @end 7 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXAdIconViewManager.m: -------------------------------------------------------------------------------- 1 | #import "EXAdIconViewManager.h" 2 | 3 | @implementation EXAdIconViewManager 4 | 5 | RCT_EXPORT_MODULE(AdIconViewManager) 6 | 7 | - (UIView *)view 8 | { 9 | return [[FBMediaView alloc] init]; 10 | } 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXAdSettingsManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface EXAdSettingsManager : NSObject 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXAdSettingsManager.m: -------------------------------------------------------------------------------- 1 | #import "EXAdSettingsManager.h" 2 | #import "EXUnversioned.h" 3 | 4 | //#import 5 | #import 6 | #import 7 | #import 8 | #import 9 | 10 | #import 11 | 12 | @implementation RCTConvert (EXNativeAdView) 13 | 14 | RCT_ENUM_CONVERTER(FBAdLogLevel, (@{ 15 | @"none": @(FBAdLogLevelNone), 16 | @"debug": @(FBAdLogLevelDebug), 17 | @"verbose": @(FBAdLogLevelVerbose), 18 | @"warning": @(FBAdLogLevelWarning), 19 | @"notification": @(FBAdLogLevelNotification), 20 | @"error": @(FBAdLogLevelError), 21 | }), FBAdLogLevelLog, integerValue) 22 | 23 | @end 24 | 25 | @interface EXAdSettingsManager () 26 | 27 | @property (nonatomic) BOOL isChildDirected; 28 | @property (nonatomic, strong) NSString *mediationService; 29 | @property (nonatomic, strong) NSString *urlPrefix; 30 | @property (nonatomic) FBAdLogLevel logLevel; 31 | @property (nonatomic, strong) NSMutableArray *testDevices; 32 | 33 | @end 34 | 35 | @implementation EXAdSettingsManager 36 | 37 | @synthesize bridge = _bridge; 38 | 39 | RCT_EXPORT_MODULE(CTKAdSettingsManager) 40 | 41 | - (instancetype)init { 42 | if (self = [super init]) { 43 | _testDevices = [NSMutableArray new]; 44 | _urlPrefix = @""; 45 | _mediationService = @""; 46 | } 47 | return self; 48 | } 49 | 50 | + (BOOL)requiresMainQueueSetup 51 | { 52 | return NO; 53 | } 54 | 55 | - (void)setBridge:(RCTBridge *)bridge 56 | { 57 | _bridge = bridge; 58 | 59 | [[NSNotificationCenter defaultCenter] addObserver:self 60 | selector:@selector(bridgeDidForeground:) 61 | name:EX_UNVERSIONED(@"EXKernelBridgeDidForegroundNotification") 62 | object:self.bridge]; 63 | 64 | [[NSNotificationCenter defaultCenter] addObserver:self 65 | selector:@selector(bridgeDidBackground:) 66 | name:EX_UNVERSIONED(@"EXKernelBridgeDidBackgroundNotification") 67 | object:self.bridge]; 68 | } 69 | 70 | RCT_EXPORT_METHOD(addTestDevice:(NSString *)deviceHash) 71 | { 72 | [FBAdSettings addTestDevice:deviceHash]; 73 | [_testDevices addObject:deviceHash]; 74 | } 75 | 76 | RCT_EXPORT_METHOD(clearTestDevices) 77 | { 78 | [FBAdSettings clearTestDevices]; 79 | [_testDevices removeAllObjects]; 80 | } 81 | 82 | RCT_EXPORT_METHOD(setLogLevel:(FBAdLogLevel)logLevel) 83 | { 84 | [FBAdSettings setLogLevel:logLevel]; 85 | _logLevel = logLevel; 86 | } 87 | 88 | RCT_EXPORT_METHOD(setIsChildDirected:(BOOL)isDirected) 89 | { 90 | [FBAdSettings setMixedAudience:isDirected]; 91 | _isChildDirected = isDirected; 92 | } 93 | 94 | RCT_EXPORT_METHOD(setMediationService:(NSString *)mediationService) 95 | { 96 | [FBAdSettings setMediationService:mediationService]; 97 | _mediationService = mediationService; 98 | } 99 | 100 | RCT_EXPORT_METHOD(setUrlPrefix:(NSString *)urlPrefix) 101 | { 102 | [FBAdSettings setUrlPrefix:urlPrefix]; 103 | _urlPrefix = urlPrefix; 104 | } 105 | 106 | 107 | RCT_EXPORT_METHOD(getTrackingStatus:(RCTPromiseResolveBlock)resolve rejector:(RCTPromiseRejectBlock)reject) 108 | { 109 | if (@available(iOS 14, *)) { 110 | resolve([EXAdSettingsManager convertTrackingStatusToString:[ATTrackingManager trackingAuthorizationStatus]]); 111 | } else { 112 | resolve(@"unavailable"); 113 | } 114 | } 115 | 116 | RCT_EXPORT_METHOD(requestTrackingPermission:(RCTPromiseResolveBlock)resolve rejector:(RCTPromiseRejectBlock)reject) 117 | { 118 | if (@available(iOS 14, *)) { 119 | [ATTrackingManager requestTrackingAuthorizationWithCompletionHandler:^(ATTrackingManagerAuthorizationStatus status) { 120 | resolve([EXAdSettingsManager convertTrackingStatusToString:status]); 121 | }]; 122 | } else { 123 | resolve(@"unavailable"); 124 | } 125 | } 126 | 127 | RCT_EXPORT_METHOD(setAdvertiserIDCollectionEnabled:(BOOL)enabled) 128 | { 129 | [FBSDKSettings setAdvertiserIDCollectionEnabled:enabled]; 130 | } 131 | 132 | 133 | RCT_EXPORT_METHOD(setAdvertiserTrackingEnabled:(BOOL)enabled) 134 | { 135 | [FBAdSettings setAdvertiserTrackingEnabled:enabled]; 136 | } 137 | 138 | 139 | - (void)bridgeDidForeground:(NSNotification *)notification 140 | { 141 | [FBAdSettings setMixedAudience:_isChildDirected]; 142 | [FBAdSettings setMediationService:_mediationService]; 143 | [FBAdSettings setUrlPrefix:_urlPrefix]; 144 | [FBAdSettings setLogLevel:_logLevel]; 145 | [FBAdSettings addTestDevices:_testDevices]; 146 | } 147 | 148 | - (void)bridgeDidBackground:(NSNotification *)notification 149 | { 150 | [FBAdSettings setMixedAudience:NO]; 151 | [FBAdSettings setMediationService:@""]; 152 | [FBAdSettings setUrlPrefix:@""]; 153 | [FBAdSettings setLogLevel:FBAdLogLevelLog]; 154 | [FBAdSettings clearTestDevices]; 155 | } 156 | 157 | - (void)dealloc 158 | { 159 | [[NSNotificationCenter defaultCenter] removeObserver:self]; 160 | } 161 | 162 | - (NSDictionary *)constantsToExport 163 | { 164 | return @{ @"currentDeviceHash": [FBAdSettings testDeviceHash] }; 165 | } 166 | 167 | + (NSString *) convertTrackingStatusToString:(ATTrackingManagerAuthorizationStatus) status API_AVAILABLE(ios(14)) { 168 | switch (status) { 169 | case ATTrackingManagerAuthorizationStatusDenied: 170 | return @"denied"; 171 | case ATTrackingManagerAuthorizationStatusAuthorized: 172 | return @"authorized"; 173 | case ATTrackingManagerAuthorizationStatusRestricted: 174 | return @"restricted"; 175 | case ATTrackingManagerAuthorizationStatusNotDetermined: 176 | return @"not-determined"; 177 | } 178 | } 179 | 180 | @end 181 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXBannerView.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface EXBannerView : RCTView 5 | 6 | @property (nonatomic, copy) RCTBubblingEventBlock onAdPress; 7 | @property (nonatomic, copy) RCTBubblingEventBlock onAdError; 8 | @property (nonatomic, copy) RCTBubblingEventBlock onAdLoad; 9 | 10 | @property (nonatomic, strong) NSNumber *size; 11 | @property (nonatomic, strong) NSString *placementId; 12 | 13 | @end 14 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXBannerView.m: -------------------------------------------------------------------------------- 1 | #import "EXBannerView.h" 2 | 3 | #import 4 | #import 5 | 6 | @interface EXBannerView () 7 | 8 | @property (nonatomic, strong) FBAdView *adView; 9 | 10 | @end 11 | 12 | @implementation EXBannerView 13 | 14 | - (void)setSize:(NSNumber *)size 15 | { 16 | _size = size; 17 | [self createViewIfCan]; 18 | } 19 | 20 | - (void)setPlacementId:(NSString *)placementId 21 | { 22 | _placementId = placementId; 23 | [self createViewIfCan]; 24 | } 25 | 26 | // Initialise BannerAdView as soon as all the props are set 27 | - (void)createViewIfCan 28 | { 29 | if (!_placementId || !_size) { 30 | return; 31 | } 32 | 33 | if (_adView) { 34 | [_adView removeFromSuperview]; 35 | } 36 | 37 | FBAdSize fbAdSize = [self fbAdSizeForHeight:_size]; 38 | 39 | FBAdView *adView = [[FBAdView alloc] initWithPlacementID:_placementId 40 | adSize:fbAdSize 41 | rootViewController:RCTPresentedViewController()]; 42 | 43 | adView.frame = CGRectMake(0, 0, adView.bounds.size.width, adView.bounds.size.height); 44 | // adView.autoresizingMask = UIViewAutoresizingFlexibleWidth; 45 | adView.delegate = self; 46 | 47 | [adView loadAd]; 48 | 49 | [self addSubview:adView]; 50 | 51 | _adView = adView; 52 | } 53 | 54 | - (FBAdSize)fbAdSizeForHeight:(NSNumber *)height 55 | { 56 | switch ([height intValue]) { 57 | case 90: 58 | return kFBAdSizeHeight90Banner; 59 | case 50: 60 | default: 61 | return kFBAdSizeHeight50Banner; 62 | } 63 | } 64 | 65 | # pragma mark - FBAdViewDelegate 66 | 67 | - (void)adViewDidClick:(FBAdView *)adView 68 | { 69 | if (_onAdPress) { 70 | _onAdPress(nil); 71 | } 72 | } 73 | 74 | - (void)adView:(FBAdView *)adView didFailWithError:(NSError *)error 75 | { 76 | if (_onAdError) { 77 | _onAdError(RCTJSErrorFromNSError(error)); 78 | } else { 79 | RCTMakeAndLogError(error.localizedDescription, nil, error.userInfo); 80 | } 81 | } 82 | 83 | - (void)adViewDidLoad:(FBAdView *)adView 84 | { 85 | NSLog(@"Ad was loaded and ready to be displayed"); 86 | 87 | if (_onAdLoad) { 88 | _onAdLoad(nil); 89 | } 90 | } 91 | 92 | 93 | - (void)adViewDidFinishHandlingClick:(FBAdView *)adView {} 94 | - (void)adViewWillLogImpression:(FBAdView *)adView {} 95 | 96 | @end 97 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXBannerViewManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface EXBannerViewManager : RCTViewManager 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXBannerViewManager.m: -------------------------------------------------------------------------------- 1 | #import "EXBannerViewManager.h" 2 | #import "EXBannerView.h" 3 | 4 | @implementation EXBannerViewManager 5 | 6 | RCT_EXPORT_MODULE(CTKBannerViewManager) 7 | 8 | @synthesize bridge = _bridge; 9 | 10 | - (UIView *)view 11 | { 12 | // if (![EXFacebook facebookAppIdFromNSBundle]) { 13 | // RCTLogWarn(@"No Facebook app id is specified. Facebook ads may have undefined behavior."); 14 | // } 15 | return [EXBannerView new]; 16 | } 17 | 18 | RCT_EXPORT_VIEW_PROPERTY(size, NSNumber) 19 | RCT_EXPORT_VIEW_PROPERTY(placementId, NSString) 20 | RCT_EXPORT_VIEW_PROPERTY(onAdLoad, RCTBubblingEventBlock) 21 | RCT_EXPORT_VIEW_PROPERTY(onAdPress, RCTBubblingEventBlock) 22 | RCT_EXPORT_VIEW_PROPERTY(onAdError, RCTBubblingEventBlock) 23 | 24 | @end 25 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXInterstitialAdManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface EXInterstitialAdManager : NSObject 4 | 5 | @end 6 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXInterstitialAdManager.m: -------------------------------------------------------------------------------- 1 | #import "EXInterstitialAdManager.h" 2 | #import "EXUnversioned.h" 3 | 4 | #import 5 | #import 6 | #import 7 | 8 | @interface EXInterstitialAdManager () 9 | 10 | @property (nonatomic, strong) RCTPromiseResolveBlock resolve; 11 | @property (nonatomic, strong) RCTPromiseRejectBlock reject; 12 | @property (nonatomic, strong) FBInterstitialAd *interstitialAd; 13 | @property (nonatomic, strong) UIViewController *adViewController; 14 | @property (nonatomic) bool didClick; 15 | @property (nonatomic) bool didLoad; 16 | @property (nonatomic) bool showWhenLoaded; 17 | @property (nonatomic) bool isBackground; 18 | 19 | @end 20 | 21 | @implementation EXInterstitialAdManager 22 | 23 | @synthesize bridge = _bridge; 24 | 25 | RCT_EXPORT_MODULE(CTKInterstitialAdManager) 26 | 27 | - (void)setBridge:(RCTBridge *)bridge 28 | { 29 | _bridge = bridge; 30 | 31 | [[NSNotificationCenter defaultCenter] addObserver:self 32 | selector:@selector(bridgeDidForeground:) 33 | name:EX_UNVERSIONED(@"EXKernelBridgeDidForegroundNotification") 34 | object:self.bridge]; 35 | 36 | [[NSNotificationCenter defaultCenter] addObserver:self 37 | selector:@selector(bridgeDidBackground:) 38 | name:EX_UNVERSIONED(@"EXKernelBridgeDidBackgroundNotification") 39 | object:self.bridge]; 40 | } 41 | 42 | RCT_EXPORT_METHOD( 43 | showAd:(NSString *)placementId 44 | resolver:(RCTPromiseResolveBlock)resolve 45 | rejecter:(RCTPromiseRejectBlock)reject 46 | ) 47 | { 48 | RCTAssert(_resolve == nil && _reject == nil, @"Only one `showAd` can be called at once"); 49 | RCTAssert(_isBackground == false, @"`showAd` can be called only when experience is running in foreground"); 50 | // if (![EXFacebook facebookAppIdFromNSBundle]) { 51 | // RCTLogWarn(@"No Facebook app id is specified. Facebook ads may have undefined behavior."); 52 | // } 53 | 54 | _resolve = resolve; 55 | _reject = reject; 56 | 57 | _interstitialAd = [[FBInterstitialAd alloc] initWithPlacementID:placementId]; 58 | _interstitialAd.delegate = self; 59 | _showWhenLoaded = true; 60 | // [EXUtil performSynchronouslyOnMainThread:^{ 61 | [self->_interstitialAd loadAd]; 62 | // }]; 63 | } 64 | 65 | 66 | RCT_EXPORT_METHOD( 67 | preloadAd:(NSString *)placementId 68 | resolver:(RCTPromiseResolveBlock)resolve 69 | rejecter:(RCTPromiseRejectBlock)reject 70 | ) 71 | { 72 | RCTAssert(_resolve == nil && _reject == nil, @"Only one `preloadAd` can be called at once"); 73 | RCTAssert(_isBackground == false, @"`preloadAd` can be called only when experience is running in foreground"); 74 | 75 | _resolve = resolve; 76 | _reject = reject; 77 | 78 | _interstitialAd = [[FBInterstitialAd alloc] initWithPlacementID:placementId]; 79 | _interstitialAd.delegate = self; 80 | _showWhenLoaded = false; 81 | [self->_interstitialAd loadAd]; 82 | } 83 | 84 | RCT_EXPORT_METHOD( 85 | showPreloadedAd:(NSString *)placementId 86 | resolver:(RCTPromiseResolveBlock)resolve 87 | rejecter:(RCTPromiseRejectBlock)reject 88 | ) { 89 | // If already loaded, show it 90 | if(_didLoad) { 91 | dispatch_async(dispatch_get_main_queue(), ^{ 92 | [_interstitialAd showAdFromRootViewController:RCTPresentedViewController()]; 93 | }); 94 | } else { 95 | // If not make sure its shown asap 96 | _showWhenLoaded = true; 97 | } 98 | resolve(nil); 99 | } 100 | 101 | #pragma mark - FBInterstitialAdDelegate 102 | 103 | - (void)interstitialAdDidLoad:(__unused FBInterstitialAd *)interstitialAd 104 | { 105 | _didLoad = true; 106 | if (_showWhenLoaded) { 107 | [_interstitialAd showAdFromRootViewController:RCTPresentedViewController()]; 108 | } 109 | 110 | } 111 | 112 | - (void)interstitialAd:(FBInterstitialAd *)interstitialAd didFailWithError:(NSError *)error 113 | { 114 | _reject(@"E_FAILED_TO_LOAD", [error localizedDescription], error); 115 | 116 | [self cleanUpAd]; 117 | } 118 | 119 | - (void)interstitialAdDidClick:(FBInterstitialAd *)interstitialAd 120 | { 121 | _didClick = true; 122 | } 123 | 124 | - (void)interstitialAdDidClose:(FBInterstitialAd *)interstitialAd 125 | { 126 | _resolve(@(_didClick)); 127 | 128 | [self cleanUpAd]; 129 | } 130 | 131 | - (void)bridgeDidForeground:(NSNotification *)notification 132 | { 133 | _isBackground = false; 134 | 135 | if (_adViewController) { 136 | [RCTPresentedViewController() presentViewController:_adViewController animated:NO completion:nil]; 137 | _adViewController = nil; 138 | } 139 | } 140 | 141 | - (void)bridgeDidBackground:(NSNotification *)notification 142 | { 143 | _isBackground = true; 144 | 145 | if (_interstitialAd) { 146 | _adViewController = RCTPresentedViewController(); 147 | [_adViewController dismissViewControllerAnimated:NO completion:nil]; 148 | } 149 | } 150 | 151 | - (void)cleanUpAd 152 | { 153 | _reject = nil; 154 | _resolve = nil; 155 | _interstitialAd = nil; 156 | _adViewController = nil; 157 | _didClick = false; 158 | _didLoad = false; 159 | _showWhenLoaded = false; 160 | } 161 | 162 | @end 163 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXNativeAdEmitter.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface EXNativeAdEmitter : RCTEventEmitter 4 | 5 | - (void)sendManagersState:(NSDictionary *)adManagersState; 6 | - (void)sendError:(NSString *)error; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXNativeAdEmitter.m: -------------------------------------------------------------------------------- 1 | #import "EXNativeAdEmitter.h" 2 | 3 | @implementation EXNativeAdEmitter 4 | 5 | RCT_EXPORT_MODULE(CTKNativeAdEmitter) 6 | 7 | - (NSArray *)supportedEvents 8 | { 9 | return @[@"CTKNativeAdsManagersChanged", @"onAdError"]; 10 | } 11 | 12 | - (void)sendManagersState:(NSDictionary *)adManagersState { 13 | [self sendEventWithName:@"CTKNativeAdsManagersChanged" body:adManagersState]; 14 | } 15 | 16 | - (void)sendError:(NSString *)error { 17 | [self sendEventWithName:@"onAdError" body:error]; 18 | } 19 | 20 | @end 21 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXNativeAdManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | 4 | @interface EXNativeAdManager : RCTViewManager 5 | 6 | - (FBNativeAdsManager *) getFBAdsManager:(NSString *)placementId; 7 | 8 | @end 9 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXNativeAdManager.m: -------------------------------------------------------------------------------- 1 | #import "EXNativeAdManager.h" 2 | #import "EXNativeAdView.h" 3 | #import "EXNativeAdEmitter.h" 4 | 5 | #import 6 | #import 7 | #import 8 | #import 9 | #import 10 | #import 11 | 12 | @implementation RCTConvert (EXNativeAdView) 13 | 14 | RCT_ENUM_CONVERTER(FBNativeAdsCachePolicy, (@{ 15 | @"none": @(FBNativeAdsCachePolicyNone), 16 | @"all": @(FBNativeAdsCachePolicyAll), 17 | }), FBNativeAdsCachePolicyNone, integerValue) 18 | 19 | @end 20 | 21 | @interface EXNativeAdManager () 22 | 23 | @property (nonatomic, strong) NSMutableDictionary *adsManagers; 24 | @property (nonatomic, strong) NSString *myAdChoiceViewPlacementId; 25 | 26 | @end 27 | 28 | @implementation EXNativeAdManager 29 | 30 | RCT_EXPORT_MODULE(CTKNativeAdManager) 31 | 32 | @synthesize bridge = _bridge; 33 | 34 | - (instancetype)init 35 | { 36 | self = [super init]; 37 | if (self) { 38 | _adsManagers = [NSMutableDictionary new]; 39 | } 40 | return self; 41 | } 42 | 43 | + (BOOL)requiresMainQueueSetup 44 | { 45 | return NO; 46 | } 47 | 48 | RCT_EXPORT_METHOD(registerViewsForInteraction:(nonnull NSNumber *)nativeAdViewTag 49 | mediaViewTag:(nonnull NSNumber *)mediaViewTag 50 | adIconViewTag:(nonnull NSNumber *)adIconViewTag 51 | clickableViewsTags:(nonnull NSArray *)tags 52 | resolve:(RCTPromiseResolveBlock)resolve 53 | reject:(RCTPromiseRejectBlock)reject) 54 | { 55 | [_bridge.uiManager addUIBlock:^(RCTUIManager *uiManager, NSDictionary *viewRegistry) { 56 | FBMediaView *mediaView = nil; 57 | FBMediaView *adIconView = nil; 58 | EXNativeAdView *nativeAdView = nil; 59 | 60 | if ([viewRegistry objectForKey:mediaViewTag] == nil) { 61 | reject(@"E_NO_VIEW_FOR_TAG", @"Could not find mediaView", nil); 62 | return; 63 | } 64 | 65 | if ([viewRegistry objectForKey:nativeAdViewTag] == nil) { 66 | reject(@"E_NO_NATIVEAD_VIEW", @"Could not find nativeAdView", nil); 67 | return; 68 | } 69 | 70 | if ([[viewRegistry objectForKey:mediaViewTag] isKindOfClass:[FBMediaView class]]) { 71 | mediaView = (FBMediaView *)[viewRegistry objectForKey:mediaViewTag]; 72 | } else { 73 | reject(@"E_INVALID_VIEW_CLASS", @"View returned for passed media view tag is not an instance of FBMediaView", nil); 74 | return; 75 | } 76 | 77 | if ([[viewRegistry objectForKey:nativeAdViewTag] isKindOfClass:[EXNativeAdView class]]) { 78 | nativeAdView = (EXNativeAdView *)[viewRegistry objectForKey:nativeAdViewTag]; 79 | } else { 80 | reject(@"E_INVALID_VIEW_CLASS", @"View returned for passed native ad view tag is not an instance of EXNativeAdView", nil); 81 | return; 82 | } 83 | 84 | if ([viewRegistry objectForKey:adIconViewTag]) { 85 | if ([[viewRegistry objectForKey:adIconViewTag] isKindOfClass:[FBMediaView class]]) { 86 | adIconView = (FBMediaView *)[viewRegistry objectForKey:adIconViewTag]; 87 | } else { 88 | reject(@"E_INVALID_VIEW_CLASS", @"View returned for passed ad icon view tag is not an instance of FBMediaView", nil); 89 | return; 90 | } 91 | } 92 | 93 | NSMutableArray *clickableViews = [NSMutableArray new]; 94 | for (id tag in tags) { 95 | if ([viewRegistry objectForKey:tag]) { 96 | [clickableViews addObject:[viewRegistry objectForKey:tag]]; 97 | } else { 98 | reject(@"E_INVALID_VIEW_TAG", [NSString stringWithFormat:@"Could not find view for tag: %@", [tag stringValue]], nil); 99 | return; 100 | } 101 | } 102 | 103 | [nativeAdView registerViewsForInteraction:mediaView adIcon:adIconView clickableViews:clickableViews]; 104 | resolve(@[]); 105 | }]; 106 | } 107 | 108 | RCT_EXPORT_METHOD(init:(NSString *)placementId withAdsToRequest:(nonnull NSNumber *)adsToRequest) 109 | { 110 | FBNativeAdsManager *adsManager = [[FBNativeAdsManager alloc] initWithPlacementID:placementId 111 | forNumAdsRequested:[adsToRequest intValue]]; 112 | _myAdChoiceViewPlacementId = placementId; 113 | [adsManager setDelegate:self]; 114 | 115 | dispatch_async(dispatch_get_main_queue(), ^{ 116 | [adsManager loadAds]; 117 | [self->_adsManagers setValue:adsManager forKey:placementId]; 118 | }); 119 | } 120 | 121 | RCT_EXPORT_METHOD(setMediaCachePolicy:(NSString*)placementId cachePolicy:(FBNativeAdsCachePolicy)cachePolicy) 122 | { 123 | [_adsManagers[placementId] setMediaCachePolicy:cachePolicy]; 124 | } 125 | 126 | RCT_EXPORT_METHOD(disableAutoRefresh:(NSString*)placementId) 127 | { 128 | [_adsManagers[placementId] disableAutoRefresh]; 129 | } 130 | 131 | - (FBNativeAdsManager *) getFBAdsManager:(NSString *)placementId 132 | { 133 | return _adsManagers[placementId]; 134 | } 135 | 136 | - (void)nativeAdsLoaded 137 | { 138 | NSMutableDictionary *adsManagersState = [NSMutableDictionary new]; 139 | 140 | [_adsManagers enumerateKeysAndObjectsUsingBlock:^(NSString* key, FBNativeAdsManager* adManager, __unused BOOL* stop) { 141 | [adsManagersState setValue:@([adManager isValid]) forKey:key]; 142 | }]; 143 | 144 | EXNativeAdEmitter *nativeAdEmitter = [_bridge moduleForClass:[EXNativeAdEmitter class]]; 145 | [nativeAdEmitter sendManagersState:adsManagersState]; 146 | } 147 | 148 | - (void)nativeAdsFailedToLoadWithError:(NSError *)errors 149 | { 150 | EXNativeAdEmitter *nativeAdEmitter = [_bridge moduleForClass:[EXNativeAdEmitter class]]; 151 | [nativeAdEmitter sendError:[NSString stringWithFormat:@"%i: %@", [errors code], [errors localizedDescription]]]; 152 | } 153 | 154 | - (UIView *)view 155 | { 156 | return [[EXNativeAdView alloc] initWithBridge:_bridge]; 157 | } 158 | 159 | RCT_EXPORT_VIEW_PROPERTY(onAdLoaded, RCTBubblingEventBlock) 160 | RCT_CUSTOM_VIEW_PROPERTY(adsManager, NSString, EXNativeAdView) 161 | { 162 | view.nativeAd = [_adsManagers[json] nextNativeAd]; 163 | } 164 | 165 | @end 166 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXNativeAdView.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | 6 | @interface EXNativeAdView : RCTView 7 | 8 | // `onAdLoaded` event called when ad has been loaded 9 | @property (nonatomic, copy) RCTBubblingEventBlock onAdLoaded; 10 | 11 | // NativeAd this view has been loaded with 12 | @property (nonatomic, strong) FBNativeAd *nativeAd; 13 | 14 | - (instancetype)initWithBridge:(RCTBridge *)bridge; 15 | - (void)registerViewsForInteraction:(FBMediaView *)mediaView adIcon:(FBMediaView *)adIconView clickableViews:(NSArray *)clickable; 16 | 17 | @end 18 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXNativeAdView.m: -------------------------------------------------------------------------------- 1 | #import "EXNativeAdView.h" 2 | #import 3 | #import 4 | 5 | @interface EXNativeAdView () 6 | 7 | @property (nonatomic, strong) RCTBridge *bridge; 8 | 9 | @end 10 | 11 | @implementation EXNativeAdView 12 | 13 | - (instancetype)initWithBridge:(RCTBridge *)bridge 14 | { 15 | if (self = [super init]) { 16 | _bridge = bridge; 17 | } 18 | return self; 19 | } 20 | 21 | - (void)setOnAdLoaded:(RCTBubblingEventBlock)onAdLoaded 22 | { 23 | _onAdLoaded = onAdLoaded; 24 | 25 | if (_nativeAd != nil) { 26 | [self callOnAdLoadedWithAd:_nativeAd]; 27 | } 28 | } 29 | 30 | - (void)setNativeAd:(FBNativeAd *)nativeAd 31 | { 32 | _nativeAd = nativeAd; 33 | [self callOnAdLoadedWithAd:_nativeAd]; 34 | } 35 | 36 | - (void)callOnAdLoadedWithAd:(FBNativeAd *)nativeAd 37 | { 38 | if (_onAdLoaded != nil) { 39 | _onAdLoaded(@{ 40 | @"advertiserName": nativeAd.advertiserName, 41 | @"sponsoredTranslation": nativeAd.sponsoredTranslation, 42 | @"bodyText": nativeAd.bodyText, 43 | @"socialContext": nativeAd.socialContext, 44 | @"callToActionText": nativeAd.callToAction, 45 | @"translation": nativeAd.adTranslation, 46 | @"linkDescription": nativeAd.linkDescription, 47 | @"promotedTranslation": nativeAd.promotedTranslation, 48 | @"headline": nativeAd.headline, 49 | }); 50 | } 51 | } 52 | 53 | - (void)registerViewsForInteraction:(FBMediaView *)mediaView adIcon:(FBMediaView *)adIconView clickableViews:(NSArray *)clickable 54 | { 55 | [_nativeAd registerViewForInteraction:self 56 | mediaView:mediaView 57 | iconView:adIconView 58 | viewController:RCTKeyWindow().rootViewController 59 | clickableViews:clickable]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXNativeMediaViewManager.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface EXNativeMediaViewManager : RCTViewManager 6 | @end 7 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXNativeMediaViewManager.m: -------------------------------------------------------------------------------- 1 | #import "EXNativeMediaViewManager.h" 2 | 3 | @implementation EXNativeMediaViewManager 4 | 5 | RCT_EXPORT_MODULE(MediaViewManager) 6 | 7 | - (UIView *)view 8 | { 9 | return [[FBMediaView alloc] init]; 10 | } 11 | 12 | @end 13 | -------------------------------------------------------------------------------- /ios/ReactNativeAdsFacebook/EXUnversioned.h: -------------------------------------------------------------------------------- 1 | @import Foundation; 2 | 3 | #define EX_UNVERSIONED(symbol) symbol 4 | 5 | @protocol EXUnversioned 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-native-fbads", 3 | "version": "7.1.0", 4 | "description": "Native Facebook Ads for React Native", 5 | "repository": { 6 | "type": "git", 7 | "url": "https://github.com/callstack/react-native-fbads" 8 | }, 9 | "homepage": "https://github.com/callstack/react-native-fbads", 10 | "main": "dist/lib/index.js", 11 | "typings": "dist/types/index.d.ts", 12 | "directories": { 13 | "example": "example" 14 | }, 15 | "scripts": { 16 | "build:plugin": "tsc --build plugin", 17 | "clean:plugin": "expo-module clean plugin", 18 | "lint:plugin": "eslint plugin/src/*", 19 | "prepare": "npm run build:plugin", 20 | "tsc": "tsc --project .", 21 | "lint": "tslint --project ." 22 | }, 23 | "author": "Suraj Tiwari ", 24 | "license": "MIT", 25 | "peerDependencies": { 26 | "react-native": "*" 27 | }, 28 | "dependencies": { 29 | "@expo/config-plugins": "^4.0.4", 30 | "fbemitter": "^2.1.1" 31 | }, 32 | "devDependencies": { 33 | "@types/fbemitter": "^2.0.32", 34 | "@types/react-native": "^0.57.4", 35 | "expo-module-scripts": "^2.0.0", 36 | "tslint": "^5.11.0", 37 | "tslint-config-airbnb": "^5.11.0", 38 | "tslint-config-prettier": "^1.15.0", 39 | "typescript": "^4.5.5" 40 | }, 41 | "keywords": [ 42 | "react-native", 43 | "facebook", 44 | "Audience Network", 45 | "Native ads" 46 | ], 47 | "files": [ 48 | "android", 49 | "ios", 50 | "src", 51 | "LICENSE", 52 | "package.json", 53 | "dist", 54 | "react-native.config.js", 55 | "ReactNativeAdsFacebook.podspec", 56 | "app.plugin.js", 57 | "plugin/build/" 58 | ] 59 | } 60 | -------------------------------------------------------------------------------- /plugin/src/withReactNativeFbads.ts: -------------------------------------------------------------------------------- 1 | import { 2 | AndroidConfig, 3 | ConfigPlugin, 4 | createRunOncePlugin, 5 | withAndroidManifest, 6 | } from '@expo/config-plugins'; 7 | const { getMainApplicationOrThrow, prefixAndroidKeys } = AndroidConfig.Manifest; 8 | 9 | const INTERSTITIAL_AD_ACTIVITY = 'com.facebook.ads.InterstitialAdActivity'; 10 | 11 | export const withFacebookManifest: ConfigPlugin = (config) => { 12 | return withAndroidManifest(config, (config) => { 13 | config.modResults = setFacebookConfig(config.modResults); 14 | return config; 15 | }); 16 | }; 17 | 18 | export function setFacebookConfig( 19 | androidManifest: AndroidConfig.Manifest.AndroidManifest 20 | ) { 21 | // eslint-disable-next-line @typescript-eslint/no-unused-vars 22 | let mainApplication = getMainApplicationOrThrow(androidManifest); 23 | mainApplication = ensureFacebookActivity({ mainApplication }); 24 | 25 | return androidManifest; 26 | } 27 | 28 | function ensureFacebookActivity({ 29 | mainApplication, 30 | }: { 31 | mainApplication: AndroidConfig.Manifest.ManifestApplication; 32 | }) { 33 | if (Array.isArray(mainApplication.activity)) { 34 | // Remove all Facebook InterstitialAdActivity first 35 | mainApplication.activity = mainApplication.activity.filter((activity) => { 36 | return activity.$?.['android:name'] !== INTERSTITIAL_AD_ACTIVITY; 37 | }); 38 | } else { 39 | mainApplication.activity = []; 40 | } 41 | 42 | mainApplication.activity.push(getFacebookAdActivity()); 43 | return mainApplication; 44 | } 45 | 46 | function buildXMLItem({ 47 | head, 48 | children, 49 | }: { 50 | head: Record; 51 | children?: Record; 52 | }) { 53 | return { ...(children ?? {}), $: head }; 54 | } 55 | 56 | function getFacebookAdActivity() { 57 | /** 58 | 62 | */ 63 | return buildXMLItem({ 64 | head: prefixAndroidKeys({ 65 | name: INTERSTITIAL_AD_ACTIVITY, 66 | configChanges: 'keyboardHidden|orientation', 67 | }), 68 | }) as AndroidConfig.Manifest.ManifestActivity; 69 | } 70 | 71 | /** 72 | * Apply react-native-fbads configuration for Expo SDK 44 projects. 73 | */ 74 | const withReactNativeFbads: ConfigPlugin = (config) => { 75 | return withFacebookManifest(config); 76 | }; 77 | 78 | const pkg = require('react-native-fbads/package.json'); 79 | 80 | export default createRunOncePlugin(withReactNativeFbads, pkg.name, pkg.version); 81 | -------------------------------------------------------------------------------- /plugin/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo-module-scripts/tsconfig.plugin", 3 | "compilerOptions": { 4 | "outDir": "./build" 5 | }, 6 | "include": ["./src"], 7 | "exclude": ["**/__mocks__/*", "**/__tests__/*"] 8 | } 9 | -------------------------------------------------------------------------------- /react-native.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | dependency: { 3 | platforms: { 4 | android: { 5 | sourceDir: 'android/app', 6 | }, 7 | }, 8 | }, 9 | }; 10 | -------------------------------------------------------------------------------- /src/AdSettings.ts: -------------------------------------------------------------------------------- 1 | import { NativeModules, Platform } from 'react-native'; 2 | 3 | const { CTKAdSettingsManager } = NativeModules; 4 | 5 | type SDKLogLevel = 6 | | 'none' 7 | | 'debug' 8 | | 'verbose' 9 | | 'warning' 10 | | 'error' 11 | | 'notification'; 12 | 13 | export type TrackingStatus = 14 | | 'unavailable' 15 | | 'denied' 16 | | 'authorized' 17 | | 'restricted' 18 | | 'not-determined'; 19 | 20 | export default { 21 | /** 22 | * Contains hash of the device id 23 | */ 24 | get currentDeviceHash(): string { 25 | return CTKAdSettingsManager.currentDeviceHash; 26 | }, 27 | 28 | /** 29 | * Registers given device with `deviceHash` to receive test Facebook ads. 30 | */ 31 | addTestDevice(deviceHash: string) { 32 | CTKAdSettingsManager.addTestDevice(deviceHash); 33 | }, 34 | /** 35 | * Clears previously set test devices 36 | */ 37 | clearTestDevices() { 38 | CTKAdSettingsManager.clearTestDevices(); 39 | }, 40 | /** 41 | * Sets current SDK log level 42 | */ 43 | setLogLevel(logLevel: SDKLogLevel) { 44 | CTKAdSettingsManager.setLogLevel(logLevel); 45 | }, 46 | /** 47 | * Specifies whether ads are treated as child-directed 48 | */ 49 | setIsChildDirected(isDirected: boolean) { 50 | CTKAdSettingsManager.setIsChildDirected(isDirected); 51 | }, 52 | /** 53 | * Sets mediation service name 54 | */ 55 | setMediationService(mediationService: string) { 56 | CTKAdSettingsManager.setMediationService(mediationService); 57 | }, 58 | /** 59 | * Sets URL prefix 60 | */ 61 | setUrlPrefix(urlPrefix: string) { 62 | CTKAdSettingsManager.setUrlPrefix(urlPrefix); 63 | }, 64 | 65 | /** 66 | * Requests permission to track the user. 67 | * 68 | * Requires a [`NSUserTrackingUsageDescription`](https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription) in your `Info.plist` 69 | * 70 | * @platform iOS 14 71 | */ 72 | async requestTrackingPermission(): Promise { 73 | if (Platform.OS !== 'ios') return 'unavailable'; 74 | return await CTKAdSettingsManager.requestTrackingPermission(); 75 | }, 76 | /** 77 | * Gets the current tracking status. 78 | * 79 | * @platform iOS 14 80 | */ 81 | async getTrackingStatus(): Promise { 82 | if (Platform.OS !== 'ios') return 'unavailable'; 83 | return await CTKAdSettingsManager.getTrackingStatus(); 84 | }, 85 | 86 | /** 87 | * Enable or disable the automatic Advertiser ID Collection. On iOS 14 it is recommended to only enable automatic Advertiser ID Collection when the user has granted permission to track. (@see `requestTrackingPermission()`) 88 | */ 89 | setAdvertiserIDCollectionEnabled(enabled: boolean): void { 90 | CTKAdSettingsManager.setAdvertiserIDCollectionEnabled(enabled); 91 | }, 92 | 93 | /** 94 | * Enable or disable ads tracking. Only works for iOS 14+. In order to ask user for tracking permission (@see `requestTrackingPermission()`). 95 | */ 96 | setAdvertiserTrackingEnabled(enabled: boolean): void { 97 | if (Platform.OS !== 'ios') { 98 | return; 99 | } 100 | CTKAdSettingsManager.setAdvertiserTrackingEnabled(enabled); 101 | }, 102 | }; 103 | -------------------------------------------------------------------------------- /src/BannerViewManager.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { requireNativeComponent, StyleProp, ViewStyle } from 'react-native'; 3 | 4 | type AdType = 'large' | 'standard'; 5 | 6 | interface NativeBannerViewProps { 7 | size: number; 8 | onAdPress: Function; 9 | onAdError: Function; 10 | onAdLoad: Function; 11 | style: StyleProp; 12 | placementId: string; 13 | } 14 | 15 | interface BannerViewProps { 16 | type: AdType; 17 | placementId: string; 18 | onPress: Function; 19 | onError: Function; 20 | onLoad: Function; 21 | style: StyleProp; 22 | } 23 | 24 | // tslint:disable-next-line:variable-name 25 | const CTKBannerView = requireNativeComponent( 26 | 'CTKBannerView' 27 | ); 28 | 29 | const sizeForType: Record = { 30 | large: 90, 31 | standard: 50, 32 | }; 33 | 34 | const getSizeForType = (type: AdType) => sizeForType[type]; 35 | 36 | // tslint:disable-next-line:variable-name 37 | const BannerView = (props: BannerViewProps) => { 38 | const { type, onPress, onError, onLoad, style, ...restProps } = props; 39 | const size = getSizeForType(type); 40 | 41 | return ( 42 | 50 | ); 51 | }; 52 | 53 | export default BannerView; 54 | -------------------------------------------------------------------------------- /src/InterstitialAdManager.ts: -------------------------------------------------------------------------------- 1 | import { NativeModules } from 'react-native'; 2 | 3 | const { CTKInterstitialAdManager } = NativeModules; 4 | 5 | export default { 6 | /** 7 | * Load interstitial ad for a given placementId and shows it 8 | */ 9 | showAd(placementId: string): Promise { 10 | return CTKInterstitialAdManager.showAd(placementId); 11 | }, 12 | 13 | /** 14 | * Preloads an interstitial ad for a given placementId 15 | */ 16 | preloadAd(placementId: string): Promise { 17 | return CTKInterstitialAdManager.preloadAd(placementId); 18 | }, 19 | 20 | /** 21 | * Shows an already preloaded Ad 22 | */ 23 | showPreloadedAd(placementId: string): Promise { 24 | return CTKInterstitialAdManager.showPreloadedAd(placementId); 25 | } 26 | }; 27 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | export { default as withNativeAd } from './native-ads/withNativeAd'; 2 | export { default as AdSettings } from './AdSettings'; 3 | export { default as NativeAdsManager } from './native-ads/NativeAdsManager'; 4 | export { default as InterstitialAdManager } from './InterstitialAdManager'; 5 | export { default as BannerView } from './BannerViewManager'; 6 | export { default as MediaView } from './native-ads/MediaViewManager'; 7 | export { default as AdIconView } from './native-ads/AdIconViewManager'; 8 | export { default as TriggerableView } from './native-ads/TriggerableView'; 9 | export { default as AdChoicesView } from './native-ads/AdChoicesManager'; 10 | export * from './native-ads/nativeAd'; 11 | -------------------------------------------------------------------------------- /src/native-ads/AdChoicesManager.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { 3 | StyleProp, 4 | ViewStyle, 5 | requireNativeComponent, 6 | StyleSheet, 7 | Platform 8 | } from 'react-native'; 9 | 10 | import { 11 | AdChoicesViewContext, 12 | AdChoicesViewContextValueType 13 | } from './contexts'; 14 | 15 | interface AdChoicesProps { 16 | location?: AdChoiceLocation; 17 | expandable?: boolean; 18 | style?: StyleProp; 19 | } 20 | 21 | // tslint:disable-next-line:variable-name 22 | const NativeAdChoicesView = requireNativeComponent< 23 | AdChoicesProps & { placementId: string } 24 | >('AdChoicesView'); 25 | 26 | type AdChoiceLocation = 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'; 27 | 28 | export default class AdChoicesView extends React.Component { 29 | static defaultProps: AdChoicesProps = { 30 | location: 'topLeft', 31 | expandable: false 32 | }; 33 | 34 | render() { 35 | return ( 36 | 37 | {(placementId: AdChoicesViewContextValueType) => ( 38 | 44 | )} 45 | 46 | ); 47 | } 48 | } 49 | 50 | const styles = StyleSheet.create({ 51 | adChoice: { 52 | backgroundColor: 'transparent', 53 | ...Platform.select({ 54 | ios: { 55 | width: 0, 56 | height: 0 57 | }, 58 | android: { 59 | width: 22, 60 | height: 22 61 | } 62 | }) 63 | } 64 | }); 65 | -------------------------------------------------------------------------------- /src/native-ads/AdIconViewManager.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { requireNativeComponent, ViewProps } from 'react-native'; 3 | 4 | import { 5 | AdIconViewContext, 6 | AdIconViewContextValueType, 7 | ComponentOrClass 8 | } from './contexts'; 9 | import { NativeAd } from './nativeAd'; 10 | 11 | export type AdIconViewProps = ViewProps; 12 | 13 | // tslint:disable-next-line:variable-name 14 | export const NativeAdIconView = requireNativeComponent( 15 | 'AdIconView' 16 | ); 17 | 18 | class AdIconViewChild extends React.Component< 19 | AdIconViewProps & AdIconViewContextValueType 20 | > { 21 | private iconView: ComponentOrClass | null = null; 22 | 23 | private handleAdIconViewRef = (ref: ComponentOrClass | null) => { 24 | if (this.iconView) { 25 | this.props.unregister(); 26 | this.iconView = null; 27 | } 28 | 29 | if (ref) { 30 | this.props.register(ref); 31 | this.iconView = ref; 32 | } 33 | }; 34 | 35 | render() { 36 | return ; 37 | } 38 | } 39 | 40 | export default class AdIconView extends React.Component { 41 | render() { 42 | return ( 43 | 44 | {(contextValue: AdIconViewContextValueType) => ( 45 | 46 | )} 47 | 48 | ); 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /src/native-ads/MediaViewManager.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { requireNativeComponent, ViewProps } from 'react-native'; 3 | import { 4 | ComponentOrClass, 5 | MediaViewContext, 6 | MediaViewContextValueType 7 | } from './contexts'; 8 | 9 | export type MediaViewProps = ViewProps; 10 | 11 | // tslint:disable-next-line:variable-name 12 | export const NativeMediaView = requireNativeComponent( 13 | 'MediaView' 14 | ); 15 | 16 | class MediaViewChild extends React.Component< 17 | MediaViewProps & MediaViewContextValueType 18 | > { 19 | private mediaView: ComponentOrClass | null = null; 20 | 21 | private handleMediaViewMount = (ref: ComponentOrClass | null) => { 22 | if (this.mediaView) { 23 | this.props.unregister(); 24 | this.mediaView = null; 25 | } 26 | 27 | if (ref) { 28 | this.props.register(ref); 29 | this.mediaView = ref; 30 | } 31 | }; 32 | 33 | render() { 34 | return ; 35 | } 36 | } 37 | 38 | export default class MediaView extends React.Component { 39 | render() { 40 | return ( 41 | 42 | {(contextValue: MediaViewContextValueType) => ( 43 | 44 | )} 45 | 46 | ); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/native-ads/NativeAdsManager.ts: -------------------------------------------------------------------------------- 1 | import { NativeModules, NativeEventEmitter } from 'react-native'; 2 | import { EventEmitter, EventSubscription } from 'fbemitter'; 3 | 4 | const { CTKNativeAdManager, CTKNativeAdEmitter } = NativeModules; 5 | 6 | const nativeAdEmitter = new NativeEventEmitter(CTKNativeAdEmitter); 7 | 8 | const EVENT_DID_BECOME_VALID = 'AdsManagerDidBecomeValid'; 9 | const EVENT_DID_BECOME_INVALID = 'AdsManagerDidBecomeInvalid'; 10 | 11 | type AdManagerCachePolicy = 'none' | 'icon' | 'image' | 'all'; 12 | 13 | export default class NativeAdsManager { 14 | private placementId: string; 15 | 16 | // Indicates whether AdsManager is ready to serve ads 17 | private isValid: boolean = false; 18 | private eventEmitter: EventEmitter = new EventEmitter(); 19 | 20 | static async registerViewsForInteractionAsync( 21 | nativeAdViewTag: number, 22 | mediaViewTag: number, 23 | adIconViewTag: number, 24 | clickable: number[] 25 | ) { 26 | if (adIconViewTag > 0 && mediaViewTag > 0) { 27 | clickable.push(mediaViewTag, adIconViewTag); 28 | } else if (mediaViewTag > 0) { 29 | clickable.push(mediaViewTag); 30 | } else if (adIconViewTag > 0) { 31 | clickable.push(adIconViewTag); 32 | } 33 | const result = await CTKNativeAdManager.registerViewsForInteraction( 34 | nativeAdViewTag, 35 | mediaViewTag, 36 | adIconViewTag, 37 | clickable 38 | ); 39 | return result; 40 | } 41 | 42 | /** 43 | * Creates an instance of AdsManager with a given placementId and adsToRequest. 44 | * Default number of ads to request is `10`. 45 | * 46 | * AdsManager will become loading ads immediately 47 | */ 48 | constructor(placementId: string, adsToRequest: number = 10) { 49 | this.placementId = placementId; 50 | 51 | this.listenForStateChanges(); 52 | this.listenForErrors(); 53 | 54 | CTKNativeAdManager.init(placementId, adsToRequest); 55 | } 56 | 57 | /** 58 | * Listens for AdManager state changes and updates internal state. When it changes, 59 | * callers will be notified of a change 60 | */ 61 | private listenForStateChanges() { 62 | nativeAdEmitter.addListener( 63 | 'CTKNativeAdsManagersChanged', 64 | (managers: Record) => { 65 | const isValidNow = managers[this.placementId]; 66 | 67 | if (this.isValid !== isValidNow && isValidNow) { 68 | this.isValid = true; 69 | this.eventEmitter.emit(EVENT_DID_BECOME_VALID); 70 | } 71 | } 72 | ); 73 | } 74 | 75 | /** 76 | * Listens for AdManager errors. When error occures, 77 | * callers will be notified of it 78 | */ 79 | private listenForErrors() { 80 | nativeAdEmitter.addListener('onAdError', (error: string) => { 81 | this.isValid = false; 82 | this.eventEmitter.emit(EVENT_DID_BECOME_INVALID, error); 83 | }); 84 | } 85 | 86 | /** 87 | * Used to listening for state changes 88 | * 89 | * If manager already became valid, it will call the function w/o registering 90 | * handler for events 91 | */ 92 | onAdsLoaded(func: Function): EventSubscription { 93 | if (this.isValid) { 94 | setTimeout(func); 95 | return { 96 | context: null, 97 | listener: () => {}, 98 | remove: () => {} 99 | }; 100 | } 101 | 102 | return this.eventEmitter.once(EVENT_DID_BECOME_VALID, func); 103 | } 104 | 105 | /** 106 | * Used to listening for errors from this native ad manager 107 | */ 108 | onAdsError(func: Function): EventSubscription { 109 | return this.eventEmitter.once(EVENT_DID_BECOME_INVALID, func); 110 | } 111 | 112 | /** 113 | * Disables auto refreshing for this native ad manager 114 | */ 115 | disableAutoRefresh() { 116 | CTKNativeAdManager.disableAutoRefresh(this.placementId); 117 | } 118 | 119 | /** 120 | * Set the native ads manager caching policy. This controls which media from 121 | * the native ads are cached before the onAdsLoaded is called. 122 | * The default is to not block on caching. 123 | */ 124 | setMediaCachePolicy(cachePolicy: AdManagerCachePolicy) { 125 | CTKNativeAdManager.setMediaCachePolicy(this.placementId, cachePolicy); 126 | } 127 | 128 | toJSON() { 129 | return this.placementId; 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /src/native-ads/TriggerableView.tsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Text, TextProps, View } from 'react-native'; 3 | import { TriggerableContext, TriggerableContextValueType } from './contexts'; 4 | 5 | class TriggerableViewChild extends React.Component< 6 | TextProps & TriggerableContextValueType 7 | > { 8 | private wrapperRef: View | null = null; 9 | 10 | private handleWrapperRef = (ref: View) => { 11 | if (this.wrapperRef) { 12 | this.props.unregister(this.wrapperRef); 13 | this.wrapperRef = null; 14 | } 15 | 16 | if (ref) { 17 | this.props.register(ref); 18 | this.wrapperRef = ref; 19 | } 20 | }; 21 | 22 | render() { 23 | return ; 24 | } 25 | } 26 | 27 | export default class TriggerableView extends React.Component { 28 | render() { 29 | return ( 30 | 31 | {(contextValue: TriggerableContextValueType) => ( 32 | 33 | )} 34 | 35 | ); 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/native-ads/contexts.ts: -------------------------------------------------------------------------------- 1 | import React, { ReactNode } from 'react'; 2 | 3 | export type ComponentOrClass = React.ComponentClass | React.Component; 4 | 5 | type Receiver = (c: ComponentOrClass) => void; 6 | export interface MultipleRegisterablesContextValueType { 7 | unregister: Receiver; 8 | register: Receiver; 9 | } 10 | 11 | export interface RegisterableContextValueType { 12 | register: Receiver; 13 | unregister: () => void; 14 | } 15 | 16 | export type TriggerableContextValueType = MultipleRegisterablesContextValueType; 17 | export type AdIconViewContextValueType = RegisterableContextValueType; 18 | export type MediaViewContextValueType = RegisterableContextValueType; 19 | export type AdChoicesViewContextValueType = string; 20 | 21 | const defaultValue = { 22 | register: () => { 23 | throw new Error('Stub!'); 24 | }, 25 | unregister: () => { 26 | throw new Error('Stub!'); 27 | } 28 | }; 29 | 30 | // tslint:disable-next-line:variable-name 31 | export const TriggerableContext = React.createContext< 32 | TriggerableContextValueType 33 | >(defaultValue); 34 | // tslint:disable-next-line:variable-name 35 | export const MediaViewContext = React.createContext( 36 | defaultValue 37 | ); 38 | 39 | // tslint:disable-next-line:variable-name 40 | export const AdIconViewContext = React.createContext< 41 | AdIconViewContextValueType 42 | >(defaultValue); 43 | 44 | // tslint:disable-next-line:variable-name 45 | export const AdChoicesViewContext = React.createContext< 46 | AdChoicesViewContextValueType 47 | >(''); 48 | -------------------------------------------------------------------------------- /src/native-ads/nativeAd.ts: -------------------------------------------------------------------------------- 1 | export interface NativeAd { 2 | advertiserName: string; 3 | bodyText: string; 4 | callToActionText: string; 5 | headline: string; 6 | linkDescription: string; 7 | promotedTranslation: string; 8 | sponsoredTranslation: string; 9 | socialContext: string; 10 | translation: string; 11 | } 12 | 13 | export interface HasNativeAd { 14 | nativeAd: NativeAd; 15 | } 16 | -------------------------------------------------------------------------------- /src/native-ads/withNativeAd.tsx: -------------------------------------------------------------------------------- 1 | import { EventSubscription } from 'fbemitter'; 2 | import React, { ReactNode } from 'react'; 3 | import { findNodeHandle, requireNativeComponent } from 'react-native'; 4 | import MediaView from './MediaViewManager'; 5 | import AdIconView from './AdIconViewManager'; 6 | import { 7 | AdChoicesViewContext, 8 | AdIconViewContext, 9 | AdIconViewContextValueType, 10 | ComponentOrClass, 11 | MediaViewContext, 12 | MediaViewContextValueType, 13 | TriggerableContext, 14 | TriggerableContextValueType 15 | } from './contexts'; 16 | import { HasNativeAd, NativeAd } from './nativeAd'; 17 | import AdsManager from './NativeAdsManager'; 18 | import { areSetsEqual } from '../util/areSetsEqual'; 19 | 20 | interface NativeAdViewProps { 21 | adsManager: string; 22 | onAdLoaded: (args: { nativeEvent: NativeAd }) => void; 23 | } 24 | 25 | // tslint:disable-next-line:variable-name 26 | const NativeAdView = requireNativeComponent('CTKNativeAd'); 27 | 28 | interface AdWrapperState { 29 | ad?: NativeAd; 30 | canRequestAds: boolean; 31 | mediaViewNodeHandle: number; 32 | adIconViewNodeHandle: number; 33 | clickableChildren: Set; 34 | } 35 | 36 | interface AdWrapperProps { 37 | adsManager: AdsManager; 38 | onAdLoaded?: (ad: NativeAd) => void; 39 | } 40 | 41 | export default ( 42 | // tslint:disable-next-line:variable-name 43 | Component: React.ComponentType 44 | ) => 45 | class NativeAdWrapper extends React.Component< 46 | AdWrapperProps & T, 47 | AdWrapperState 48 | > { 49 | private subscription?: EventSubscription; 50 | private subscriptionError?: EventSubscription; 51 | private nativeAdViewRef?: React.Component; 52 | private registerFunctionsForTriggerables: TriggerableContextValueType; 53 | private registerFunctionsForMediaView: MediaViewContextValueType; 54 | private registerFunctionsForAdIconView: AdIconViewContextValueType; 55 | private clickableChildrenNodeHandles: Map; 56 | 57 | constructor(props: AdWrapperProps & T) { 58 | super(props); 59 | 60 | this.registerFunctionsForTriggerables = { 61 | register: this.registerClickableChild, 62 | unregister: this.unregisterClickableChild 63 | }; 64 | 65 | this.registerFunctionsForMediaView = { 66 | unregister: this.unregisterMediaView, 67 | register: this.registerMediaView 68 | }; 69 | 70 | this.registerFunctionsForAdIconView = { 71 | unregister: this.unregisterAdIconView, 72 | register: this.registerAdIconView 73 | }; 74 | 75 | this.clickableChildrenNodeHandles = new Map(); 76 | 77 | this.state = { 78 | // iOS requires a non-null value 79 | mediaViewNodeHandle: -1, 80 | adIconViewNodeHandle: -1, 81 | clickableChildren: new Set(), 82 | canRequestAds: false 83 | }; 84 | } 85 | 86 | /** 87 | * On init, register for updates on `adsManager` to know when it becomes available 88 | */ 89 | public componentDidMount() { 90 | this.subscription = this.props.adsManager.onAdsLoaded(() => 91 | this.setState({ canRequestAds: true }) 92 | ); 93 | this.subscriptionError = this.props.adsManager.onAdsError(() => 94 | this.setState({ canRequestAds: false }) 95 | ); 96 | } 97 | 98 | public componentDidUpdate(_: AdWrapperProps, prevState: AdWrapperState) { 99 | if ( 100 | this.state.mediaViewNodeHandle === -1 || 101 | this.state.adIconViewNodeHandle === -1 102 | ) { 103 | // Facebook's SDK requires both MediaView and AdIconView references in order to register 104 | // interactable views. If one of them is missing, we can't proceed with the registration. 105 | return; 106 | } 107 | 108 | const mediaViewNodeHandleChanged = 109 | this.state.mediaViewNodeHandle !== prevState.mediaViewNodeHandle; 110 | const adIconViewNodeHandleChanged = 111 | this.state.adIconViewNodeHandle !== prevState.adIconViewNodeHandle; 112 | const clickableChildrenChanged = areSetsEqual( 113 | prevState.clickableChildren, 114 | this.state.clickableChildren 115 | ); 116 | 117 | if ( 118 | mediaViewNodeHandleChanged || 119 | adIconViewNodeHandleChanged || 120 | clickableChildrenChanged 121 | ) { 122 | const viewHandle = findNodeHandle(this.nativeAdViewRef!); 123 | if (!viewHandle) { 124 | // Skip registration if the view is no longer valid. 125 | return; 126 | } 127 | 128 | AdsManager.registerViewsForInteractionAsync( 129 | viewHandle, 130 | this.state.mediaViewNodeHandle, 131 | this.state.adIconViewNodeHandle, 132 | [...this.state.clickableChildren] 133 | ); 134 | } 135 | } 136 | 137 | /** 138 | * Clear subscription when component goes off screen 139 | */ 140 | public componentWillUnmount() { 141 | if (this.subscription) { 142 | this.subscription.remove(); 143 | } 144 | if (this.subscriptionError) { 145 | this.subscriptionError.remove(); 146 | } 147 | } 148 | 149 | private registerMediaView = (mediaView: ComponentOrClass) => 150 | this.setState({ mediaViewNodeHandle: findNodeHandle(mediaView) || -1 }); 151 | private unregisterMediaView = () => 152 | this.setState({ mediaViewNodeHandle: -1 }); 153 | 154 | private registerAdIconView = (adIconView: ComponentOrClass) => 155 | this.setState({ adIconViewNodeHandle: findNodeHandle(adIconView) || -1 }); 156 | private unregisterAdIconView = () => 157 | this.setState({ adIconViewNodeHandle: -1 }); 158 | 159 | private registerClickableChild = (child: ComponentOrClass) => { 160 | const handle = findNodeHandle(child); 161 | 162 | if (!handle) { 163 | return; 164 | } 165 | 166 | this.clickableChildrenNodeHandles.set(child, handle); 167 | 168 | this.setState({ 169 | clickableChildren: this.state.clickableChildren.add(handle) 170 | }); 171 | }; 172 | 173 | private unregisterClickableChild = (child: ComponentOrClass) => { 174 | this.setState(({ clickableChildren }) => { 175 | const newClickableChildren = new Set(clickableChildren); 176 | newClickableChildren.delete( 177 | this.clickableChildrenNodeHandles.get(child)! 178 | ); 179 | this.clickableChildrenNodeHandles.delete(child); 180 | return { clickableChildren: newClickableChildren }; 181 | }); 182 | }; 183 | 184 | private handleAdUpdated = () => 185 | this.state.ad && 186 | this.props.onAdLoaded && 187 | this.props.onAdLoaded(this.state.ad); 188 | 189 | private handleAdLoaded = ({ nativeEvent }: { nativeEvent: NativeAd }) => { 190 | this.setState({ ad: nativeEvent }, this.handleAdUpdated); 191 | }; 192 | 193 | private handleNativeAdViewMount = (ref: React.Component) => { 194 | this.nativeAdViewRef = ref; 195 | }; 196 | 197 | private renderAdComponent(componentProps: T): ReactNode { 198 | if (!this.state.ad) { 199 | return null; 200 | } 201 | return ( 202 | 203 | 204 | 207 | 210 | {/* Facebook's registerViewForInteraction requires both AdIconView and MediaView 211 | references to be set. We include both as a default */} 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | ); 221 | } 222 | 223 | render() { 224 | // Cast to any until https://github.com/Microsoft/TypeScript/issues/10727 is resolved 225 | const { adsManager, onAdLoaded, ...rest } = this.props as any; 226 | 227 | if (!this.state.canRequestAds) { 228 | return null; 229 | } 230 | 231 | return ( 232 | 237 | {this.renderAdComponent(rest)} 238 | 239 | ); 240 | } 241 | }; 242 | -------------------------------------------------------------------------------- /src/util/areSetsEqual.ts: -------------------------------------------------------------------------------- 1 | export const areSetsEqual = (a: Set, b: Set) => { 2 | if (a.size !== b.size) return false; 3 | for (const aItem of a) { 4 | if (!b.has(aItem)) return false; 5 | } 6 | return true; 7 | }; 8 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "moduleResolution": "node", 4 | "target": "es6", 5 | "module": "es2015", 6 | "strict": true, 7 | "jsx": "react-native", 8 | "sourceMap": false, 9 | "declaration": true, 10 | "allowSyntheticDefaultImports": true, 11 | "declarationDir": "dist/types", 12 | "outDir": "dist/lib", 13 | "typeRoots": ["node_modules/@types"] 14 | }, 15 | "include": ["src"] 16 | } 17 | -------------------------------------------------------------------------------- /tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "defaultSeverity": "error", 3 | "extends": ["tslint-config-airbnb", "tslint-config-prettier"], 4 | "jsRules": {}, 5 | "rules": { 6 | "import-name": false 7 | }, 8 | "rulesDirectory": [] 9 | } 10 | --------------------------------------------------------------------------------