From 46b49ecfb3ee6c3fabfad856e26e8807694918df Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Mon, 20 May 2019 20:23:08 -0500 Subject: [PATCH 01/10] WIP: banners --- .gitignore | 1 + example/ios/Podfile | 2 +- example/ios/Runner.xcodeproj/project.pbxproj | 78 +++++++++- .../contents.xcworkspacedata | 3 + .../xcshareddata/IDEWorkspaceChecks.plist | 8 + .../xcshareddata/WorkspaceSettings.xcsettings | 8 + example/ios/Runner/Info.plist | 6 + ios/Classes/AdmobBanner.swift | 145 +++++++++++++++++- ios/Classes/AdmobBannerFactory.swift | 50 +++++- ios/Classes/AdmobFlutterPlugin.h | 21 +++ ios/Classes/AdmobFlutterPlugin.m | 21 +++ ios/Classes/SwiftAdmobFlutterPlugin.swift | 53 ++++++- ios/admob_flutter.podspec | 13 +- 13 files changed, 383 insertions(+), 26 deletions(-) create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist create mode 100644 example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings diff --git a/.gitignore b/.gitignore index 7ecebb4..d4b65e9 100644 --- a/.gitignore +++ b/.gitignore @@ -6,3 +6,4 @@ pubspec.lock build/ +.idea diff --git a/example/ios/Podfile b/example/ios/Podfile index 64eddc6..6297c51 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -1,5 +1,5 @@ # Uncomment this line to define a global platform for your project -# platform :ios, '9.0' + platform :ios, '8.0' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/example/ios/Runner.xcodeproj/project.pbxproj b/example/ios/Runner.xcodeproj/project.pbxproj index 6761a6a..bda5c16 100644 --- a/example/ios/Runner.xcodeproj/project.pbxproj +++ b/example/ios/Runner.xcodeproj/project.pbxproj @@ -8,7 +8,6 @@ /* Begin PBXBuildFile section */ 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */ = {isa = PBXBuildFile; fileRef = 2D5378251FAA1A9400D5DBA9 /* flutter_assets */; }; 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 3B80C3941E831B6300D905FE /* App.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; }; 3B80C3951E831B6300D905FE /* App.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 3B80C3931E831B6300D905FE /* App.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; }; @@ -19,6 +18,7 @@ 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; + E7A1118201BCA9D718CC3ED4 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 24D7FA5CEB975A68FF05067C /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ /* Begin PBXCopyFilesBuildPhase section */ @@ -39,9 +39,10 @@ /* Begin PBXFileReference section */ 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = ""; }; 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = ""; }; - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */ = {isa = PBXFileReference; lastKnownFileType = folder; name = flutter_assets; path = Flutter/flutter_assets; sourceTree = SOURCE_ROOT; }; + 24D7FA5CEB975A68FF05067C /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = ""; }; 3B80C3931E831B6300D905FE /* App.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = App.framework; path = Flutter/App.framework; sourceTree = ""; }; + 60E4F89FB3CA779FE21111C3 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = ""; }; 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = ""; }; 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; @@ -53,6 +54,8 @@ 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 9F4D6C33C6C390F42E506F21 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = ""; }; + A09C8DAD180952151FDA7CAA /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,16 +65,27 @@ files = ( 9705A1C61CF904A100538489 /* Flutter.framework in Frameworks */, 3B80C3941E831B6300D905FE /* App.framework in Frameworks */, + E7A1118201BCA9D718CC3ED4 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ + 462490AC19C85F5B12038C68 /* Pods */ = { + isa = PBXGroup; + children = ( + 60E4F89FB3CA779FE21111C3 /* Pods-Runner.debug.xcconfig */, + 9F4D6C33C6C390F42E506F21 /* Pods-Runner.release.xcconfig */, + A09C8DAD180952151FDA7CAA /* Pods-Runner.profile.xcconfig */, + ); + name = Pods; + path = Pods; + sourceTree = ""; + }; 9740EEB11CF90186004384FC /* Flutter */ = { isa = PBXGroup; children = ( - 2D5378251FAA1A9400D5DBA9 /* flutter_assets */, 3B80C3931E831B6300D905FE /* App.framework */, 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */, 9740EEBA1CF902C7004384FC /* Flutter.framework */, @@ -88,6 +102,8 @@ 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, + 462490AC19C85F5B12038C68 /* Pods */, + 9AC3B6257E801133BCE0A190 /* Frameworks */, ); sourceTree = ""; }; @@ -122,6 +138,14 @@ name = "Supporting Files"; sourceTree = ""; }; + 9AC3B6257E801133BCE0A190 /* Frameworks */ = { + isa = PBXGroup; + children = ( + 24D7FA5CEB975A68FF05067C /* Pods_Runner.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -129,12 +153,14 @@ isa = PBXNativeTarget; buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( + 0C24A2A7747A18E0525C5E7D /* [CP] Check Pods Manifest.lock */, 9740EEB61CF901F6004384FC /* Run Script */, 97C146EA1CF9000F007C117D /* Sources */, 97C146EB1CF9000F007C117D /* Frameworks */, 97C146EC1CF9000F007C117D /* Resources */, 9705A1C41CF9048500538489 /* Embed Frameworks */, 3B06AD1E1E4923F5004D2608 /* Thin Binary */, + 7FDF1B0DB9C66D50FD638659 /* [CP] Embed Pods Frameworks */, ); buildRules = ( ); @@ -187,7 +213,6 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 9740EEB41CF90195004384FC /* Debug.xcconfig in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, - 2D5378261FAA1A9400D5DBA9 /* flutter_assets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -195,6 +220,28 @@ /* End PBXResourcesBuildPhase section */ /* Begin PBXShellScriptBuildPhase section */ + 0C24A2A7747A18E0525C5E7D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; 3B06AD1E1E4923F5004D2608 /* Thin Binary */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -209,6 +256,28 @@ shellPath = /bin/sh; shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" thin"; }; + 7FDF1B0DB9C66D50FD638659 /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh", + "${PODS_ROOT}/../.symlinks/flutter/ios/Flutter.framework", + "${BUILT_PRODUCTS_DIR}/GoogleUtilities/GoogleUtilities.framework", + "${BUILT_PRODUCTS_DIR}/nanopb/nanopb.framework", + ); + name = "[CP] Embed Pods Frameworks"; + outputPaths = ( + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Flutter.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/GoogleUtilities.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/nanopb.framework", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; 9740EEB61CF901F6004384FC /* Run Script */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -512,7 +581,6 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ - }; rootObject = 97C146E61CF9000F007C117D /* Project object */; } diff --git a/example/ios/Runner.xcworkspace/contents.xcworkspacedata b/example/ios/Runner.xcworkspace/contents.xcworkspacedata index 1d526a1..21a3cc1 100644 --- a/example/ios/Runner.xcworkspace/contents.xcworkspacedata +++ b/example/ios/Runner.xcworkspace/contents.xcworkspacedata @@ -4,4 +4,7 @@ + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100644 index 0000000..949b678 --- /dev/null +++ b/example/ios/Runner.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,8 @@ + + + + + BuildSystemType + Original + + diff --git a/example/ios/Runner/Info.plist b/example/ios/Runner/Info.plist index 37bec47..d541d6d 100644 --- a/example/ios/Runner/Info.plist +++ b/example/ios/Runner/Info.plist @@ -2,6 +2,12 @@ + io.flutter.embedded_views_preview + + LSApplicationCategoryType + + GADApplicationIdentifier + ca-app-pub-3940256099942544~1458002511 CFBundleDevelopmentRegion en CFBundleExecutable diff --git a/ios/Classes/AdmobBanner.swift b/ios/Classes/AdmobBanner.swift index 328bb0d..77a36c2 100644 --- a/ios/Classes/AdmobBanner.swift +++ b/ios/Classes/AdmobBanner.swift @@ -1,8 +1,141 @@ -// -// AdmobBanner.swift -// admob_flutter -// -// Created by Youssef Kababe on 12/9/18. -// +/* + Copyright (c) 2019 Kevin McGill + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ +import Flutter import Foundation +import GoogleMobileAds + +class AdmobBanner : NSObject, FlutterPlatformView { + + private let channel: FlutterMethodChannel + private let messeneger: FlutterBinaryMessenger + private let frame: CGRect + private let viewId: Int64 + private let args: [String: Any] + private var adView: GADBannerView? + + init(frame: CGRect, viewId: Int64, args: [String: Any], messeneger: FlutterBinaryMessenger) { + self.args = args + self.messeneger = messeneger + self.frame = frame + self.viewId = viewId + channel = FlutterMethodChannel(name: "admob_flutter/banner_\(viewId)", binaryMessenger: messeneger) + } + + func view() -> UIView { + return getBannerAdView() ?? UIView() + } + + fileprivate func dispose() { + adView?.removeFromSuperview() + adView = nil + channel.setMethodCallHandler(nil) + } + + fileprivate func getBannerAdView() -> GADBannerView? { + if adView == nil { + adView = GADBannerView() + adView!.rootViewController = UIApplication.shared.keyWindow?.rootViewController + adView!.delegate = self + adView!.frame = self.frame.width == 0 ? CGRect(x: 0, y: 0, width: 1, height: 1) : self.frame + adView!.adUnitID = self.args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/6300978111" + adView!.delegate = self + channel.setMethodCallHandler { [weak self] (flutterMethodCall: FlutterMethodCall, flutterResult: FlutterResult) in + switch flutterMethodCall.method { + case "setListener": +// self?.adView?.delegate = self + break + case "dispose": + self?.dispose() + break + default: + flutterResult(FlutterMethodNotImplemented) + } + } + requestAd() + } + + return adView + } + + fileprivate func requestAd() { + if let ad = getBannerAdView() { + let request = GADRequest() +// if debug { + request.testDevices = [kGADSimulatorID] +// } + ad.load(request) + } + } + + fileprivate func getSize() -> GADAdSize { + let size = args["adSize"] as? [String: Any] + let width = size!["width"] as? Int ?? 0 + let height = size!["height"] as? Int ?? 0 + let name = size!["name"] as! String + + switch name { + case "BANNER": + return kGADAdSizeBanner + case "LARGE_BANNER": + return kGADAdSizeLargeBanner + case "MEDIUM_RECTANGLE": + return kGADAdSizeMediumRectangle + case "FULL_BANNER": + return kGADAdSizeFullBanner + case "LEADERBOARD": + return kGADAdSizeLeaderboard + case "SMART_BANNER": + // TODO: Do we need Landscape too? + return kGADAdSizeSmartBannerPortrait + default: + return GADAdSize.init(size: CGSize(width: width, height: height), flags: 0) + } + } + +} + +extension AdmobBanner : GADBannerViewDelegate { + func adViewDidReceiveAd(_ bannerView: GADBannerView) { + channel.invokeMethod("loaded", arguments: nil) + } + + func adView(_ bannerView: GADBannerView, didFailToReceiveAdWithError error: GADRequestError) { + channel.invokeMethod("failedToLoad", arguments: error) + } + + /// Tells the delegate that a full screen view will be presented in response to the user clicking on + /// an ad. The delegate may want to pause animations and time sensitive interactions. + func adViewWillPresentScreen(_ bannerView: GADBannerView) { + channel.invokeMethod("clicked", arguments: nil) + channel.invokeMethod("opened", arguments: nil) + } + + // channel.invokeMethod("impression", null) + + func adViewWillLeaveApplication(_ bannerView: GADBannerView) { + channel.invokeMethod("leftApplication", arguments: nil) + } + + func adViewDidDismissScreen(_ bannerView: GADBannerView) { + channel.invokeMethod("closed", arguments: nil) + } +} diff --git a/ios/Classes/AdmobBannerFactory.swift b/ios/Classes/AdmobBannerFactory.swift index 932b07b..de0be72 100644 --- a/ios/Classes/AdmobBannerFactory.swift +++ b/ios/Classes/AdmobBannerFactory.swift @@ -1,8 +1,46 @@ -// -// AdmobBannerFactory.swift -// admob_flutter -// -// Created by Youssef Kababe on 12/9/18. -// +/* + Copyright (c) 2019 Kevin McGill + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ +import Flutter import Foundation + +class AdmobBannerFactory : NSObject, FlutterPlatformViewFactory { + let messeneger: FlutterBinaryMessenger + + init(messeneger: FlutterBinaryMessenger) { + self.messeneger = messeneger + } + + func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { + let myargs = args + print(myargs ?? "NO ARGS!") + return AdmobBanner( + frame: frame, + viewId: viewId, + args: args as? [String : Any] ?? [:], + messeneger: messeneger + ) + } + + func createArgsCodec() -> FlutterMessageCodec & NSObjectProtocol { + return FlutterStandardMessageCodec.sharedInstance() + } +} diff --git a/ios/Classes/AdmobFlutterPlugin.h b/ios/Classes/AdmobFlutterPlugin.h index 5d9cf35..4c7433f 100644 --- a/ios/Classes/AdmobFlutterPlugin.h +++ b/ios/Classes/AdmobFlutterPlugin.h @@ -1,3 +1,24 @@ +/* + Copyright (c) 2019 Kevin McGill + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + #import @interface AdmobFlutterPlugin : NSObject diff --git a/ios/Classes/AdmobFlutterPlugin.m b/ios/Classes/AdmobFlutterPlugin.m index d2fe408..b06116c 100644 --- a/ios/Classes/AdmobFlutterPlugin.m +++ b/ios/Classes/AdmobFlutterPlugin.m @@ -1,3 +1,24 @@ +/* + Copyright (c) 2019 Kevin McGill + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + #import "AdmobFlutterPlugin.h" #import diff --git a/ios/Classes/SwiftAdmobFlutterPlugin.swift b/ios/Classes/SwiftAdmobFlutterPlugin.swift index d65bf45..0a0165a 100644 --- a/ios/Classes/SwiftAdmobFlutterPlugin.swift +++ b/ios/Classes/SwiftAdmobFlutterPlugin.swift @@ -1,14 +1,61 @@ +/* + Copyright (c) 2019 Kevin McGill + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + import Flutter import UIKit +import GoogleMobileAds public class SwiftAdmobFlutterPlugin: NSObject, FlutterPlugin { + public static func register(with registrar: FlutterPluginRegistrar) { - let channel = FlutterMethodChannel(name: "admob_flutter", binaryMessenger: registrar.messenger()) let instance = SwiftAdmobFlutterPlugin() - registrar.addMethodCallDelegate(instance, channel: channel) + let defaultChannel = FlutterMethodChannel(name: "admob_flutter", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: defaultChannel) + + let interstitialChannel = FlutterMethodChannel(name: "admob_flutter/interstitial", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: interstitialChannel) + + let rewardChannel = FlutterMethodChannel(name: "admob_flutter/reward", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: rewardChannel) + + registrar.register( + AdmobBannerFactory(messeneger: registrar.messenger()), + withId: "admob_flutter/banner" + ) } public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { - result("iOS " + UIDevice.current.systemVersion) + switch call.method { + case "initialize": + GADMobileAds.sharedInstance().start { (initializationStatus: GADInitializationStatus) in + print("initializationStatus:") + print(initializationStatus) + } + break + case "getPlatformVersion": + result("iOS " + UIDevice.current.systemVersion) + break + default: + result("success") +// result(FlutterMethodNotImplemented) + } } } diff --git a/ios/admob_flutter.podspec b/ios/admob_flutter.podspec index 13c8c82..f6db9e9 100644 --- a/ios/admob_flutter.podspec +++ b/ios/admob_flutter.podspec @@ -3,19 +3,22 @@ # Pod::Spec.new do |s| s.name = 'admob_flutter' - s.version = '0.0.1' - s.summary = 'A new flutter plugin project.' + s.version = '0.2.0' + s.summary = 'Admob plugin that shows banner ads using native platform views.' s.description = <<-DESC -A new flutter plugin project. +Admob plugin that shows banner ads using native platform views. DESC - s.homepage = 'http://example.com' + s.homepage = 'https://github.com/YoussefKababe/admob_flutter' s.license = { :file => '../LICENSE' } - s.author = { 'Your Company' => 'email@example.com' } + s.author = { 'admob_flutter' => 'email@admobflutter.fake' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' s.dependency 'Flutter' + s.dependency 'Firebase/Core' + s.dependency 'Firebase/AdMob' s.ios.deployment_target = '8.0' + s.static_framework = true end From a8677e0eaece71a32192943e381700b0cec77e32 Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Mon, 20 May 2019 21:00:41 -0500 Subject: [PATCH 02/10] Start Interstitial --- ios/Classes/AdmobBanner.swift | 2 +- ios/Classes/AdmobInterstitial.swift | 27 +++++++++++++++++++++++ ios/Classes/SwiftAdmobFlutterPlugin.swift | 1 + 3 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 ios/Classes/AdmobInterstitial.swift diff --git a/ios/Classes/AdmobBanner.swift b/ios/Classes/AdmobBanner.swift index 77a36c2..6778b03 100644 --- a/ios/Classes/AdmobBanner.swift +++ b/ios/Classes/AdmobBanner.swift @@ -129,7 +129,7 @@ extension AdmobBanner : GADBannerViewDelegate { channel.invokeMethod("opened", arguments: nil) } - // channel.invokeMethod("impression", null) + // TODO: not sure this exists on iOS. channel.invokeMethod("impression", null) func adViewWillLeaveApplication(_ bannerView: GADBannerView) { channel.invokeMethod("leftApplication", arguments: nil) diff --git a/ios/Classes/AdmobInterstitial.swift b/ios/Classes/AdmobInterstitial.swift new file mode 100644 index 0000000..788170f --- /dev/null +++ b/ios/Classes/AdmobInterstitial.swift @@ -0,0 +1,27 @@ +/* + Copyright (c) 2019 Kevin McGill + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + +import Foundation + +class AdmobIntersitial: NSObject { + + +} diff --git a/ios/Classes/SwiftAdmobFlutterPlugin.swift b/ios/Classes/SwiftAdmobFlutterPlugin.swift index 0a0165a..7621490 100644 --- a/ios/Classes/SwiftAdmobFlutterPlugin.swift +++ b/ios/Classes/SwiftAdmobFlutterPlugin.swift @@ -31,6 +31,7 @@ public class SwiftAdmobFlutterPlugin: NSObject, FlutterPlugin { registrar.addMethodCallDelegate(instance, channel: defaultChannel) let interstitialChannel = FlutterMethodChannel(name: "admob_flutter/interstitial", binaryMessenger: registrar.messenger()) +// registrar.addMethodCallDelegate(AdmobIntersitial(), channel: interstitialChannel) registrar.addMethodCallDelegate(instance, channel: interstitialChannel) let rewardChannel = FlutterMethodChannel(name: "admob_flutter/reward", binaryMessenger: registrar.messenger()) From 0787463c08a6320892754ee754ee0f44571f0521 Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Mon, 27 May 2019 23:15:30 -0500 Subject: [PATCH 03/10] WIP Interstitial --- ios/Classes/AdmobBanner.swift | 11 +- ios/Classes/AdmobBannerFactory.swift | 2 - ios/Classes/AdmobFlutterPlugin.m | 4 +- ios/Classes/AdmobInterstitial.swift | 27 ----- ios/Classes/AdmobInterstitialPlugin.swift | 136 ++++++++++++++++++++++ ios/Classes/SwiftAdmobFlutterPlugin.swift | 18 ++- 6 files changed, 151 insertions(+), 47 deletions(-) delete mode 100644 ios/Classes/AdmobInterstitial.swift create mode 100644 ios/Classes/AdmobInterstitialPlugin.swift diff --git a/ios/Classes/AdmobBanner.swift b/ios/Classes/AdmobBanner.swift index 6778b03..da29737 100644 --- a/ios/Classes/AdmobBanner.swift +++ b/ios/Classes/AdmobBanner.swift @@ -54,14 +54,12 @@ class AdmobBanner : NSObject, FlutterPlatformView { if adView == nil { adView = GADBannerView() adView!.rootViewController = UIApplication.shared.keyWindow?.rootViewController - adView!.delegate = self adView!.frame = self.frame.width == 0 ? CGRect(x: 0, y: 0, width: 1, height: 1) : self.frame adView!.adUnitID = self.args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/6300978111" - adView!.delegate = self channel.setMethodCallHandler { [weak self] (flutterMethodCall: FlutterMethodCall, flutterResult: FlutterResult) in switch flutterMethodCall.method { case "setListener": -// self?.adView?.delegate = self + self?.adView?.delegate = self break case "dispose": self?.dispose() @@ -79,9 +77,7 @@ class AdmobBanner : NSObject, FlutterPlatformView { fileprivate func requestAd() { if let ad = getBannerAdView() { let request = GADRequest() -// if debug { - request.testDevices = [kGADSimulatorID] -// } + request.testDevices = [kGADSimulatorID] ad.load(request) } } @@ -129,7 +125,8 @@ extension AdmobBanner : GADBannerViewDelegate { channel.invokeMethod("opened", arguments: nil) } - // TODO: not sure this exists on iOS. channel.invokeMethod("impression", null) + // TODO: not sure this exists on iOS. + // channel.invokeMethod("impression", null) func adViewWillLeaveApplication(_ bannerView: GADBannerView) { channel.invokeMethod("leftApplication", arguments: nil) diff --git a/ios/Classes/AdmobBannerFactory.swift b/ios/Classes/AdmobBannerFactory.swift index de0be72..d56510c 100644 --- a/ios/Classes/AdmobBannerFactory.swift +++ b/ios/Classes/AdmobBannerFactory.swift @@ -30,8 +30,6 @@ class AdmobBannerFactory : NSObject, FlutterPlatformViewFactory { } func create(withFrame frame: CGRect, viewIdentifier viewId: Int64, arguments args: Any?) -> FlutterPlatformView { - let myargs = args - print(myargs ?? "NO ARGS!") return AdmobBanner( frame: frame, viewId: viewId, diff --git a/ios/Classes/AdmobFlutterPlugin.m b/ios/Classes/AdmobFlutterPlugin.m index b06116c..b8cc2b5 100644 --- a/ios/Classes/AdmobFlutterPlugin.m +++ b/ios/Classes/AdmobFlutterPlugin.m @@ -22,8 +22,10 @@ of this software and associated documentation files (the "Software"), to deal #import "AdmobFlutterPlugin.h" #import + @implementation AdmobFlutterPlugin + (void)registerWithRegistrar:(NSObject*)registrar { - [SwiftAdmobFlutterPlugin registerWithRegistrar:registrar]; + [SwiftAdmobFlutterPlugin registerWithRegistrar: registrar]; + [AdmobIntersitialPlugin registerWithRegistrar: registrar]; } @end diff --git a/ios/Classes/AdmobInterstitial.swift b/ios/Classes/AdmobInterstitial.swift deleted file mode 100644 index 788170f..0000000 --- a/ios/Classes/AdmobInterstitial.swift +++ /dev/null @@ -1,27 +0,0 @@ -/* - Copyright (c) 2019 Kevin McGill - 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: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - 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. - */ - -import Foundation - -class AdmobIntersitial: NSObject { - - -} diff --git a/ios/Classes/AdmobInterstitialPlugin.swift b/ios/Classes/AdmobInterstitialPlugin.swift new file mode 100644 index 0000000..16e6da6 --- /dev/null +++ b/ios/Classes/AdmobInterstitialPlugin.swift @@ -0,0 +1,136 @@ +/* + Copyright (c) 2019 Kevin McGill + 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: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + 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. + */ + +import Foundation +import GoogleMobileAds + +public class AdmobIntersitialPlugin: NSObject, FlutterPlugin { + + fileprivate var allIds: [Int: GADInterstitial] = [:] + fileprivate var delegates: [Int: GADInterstitialDelegate] = [:] + fileprivate var pluginRegistrar: FlutterPluginRegistrar? + + fileprivate var interstantialAdUnitId: String? + fileprivate var interstitialChannel: FlutterMethodChannel? + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = AdmobIntersitialPlugin() + instance.pluginRegistrar = registrar + let channel = FlutterMethodChannel(name: "admob_flutter/interstitial", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: channel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String : Any] else { + result(FlutterError(code: "Missing args!", message: "Unable to convert args to [String : Any]", details: nil)) + return + } + let id = args["id"] as? Int ?? 0 + let adUnitId = args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/1033173712" + + switch call.method { + case "setListener": + let channel = FlutterMethodChannel(name: "admob_flutter/interstitial_\(id)", binaryMessenger: pluginRegistrar!.messenger()) + delegates[id] = AdmobIntersitialPluginDelegate(channel: channel) + let interstantialAd = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) + interstantialAd.delegate = delegates[id] + break + case "load": + allIds[id] = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) + loadInterstantialAd(id: id, interstantialAdUnitId: adUnitId) + result(nil) + break + case "isLoaded": + result(getInterstitialAd(id: id, interstantialAdUnitId: adUnitId).isReady) + break + case "show": + let interstitial = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) + if interstitial.isReady && !interstitial.hasBeenUsed, let rootViewController = UIApplication.shared.keyWindow?.rootViewController { + interstitial.present(fromRootViewController: rootViewController) + } else { + result(FlutterError(code: "Interstitial Error", message: "Failed to present interstitial", details: nil)) + } + break + case "dispose": + allIds.removeValue(forKey: id) + delegates.removeValue(forKey: id) + break + default: + result(FlutterMethodNotImplemented) + } + } + + private func loadInterstantialAd(id: Int, interstantialAdUnitId: String) { + let interstantial = getInterstitialAd(id: id, interstantialAdUnitId: interstantialAdUnitId) + let request = GADRequest() + request.testDevices = [kGADSimulatorID] + interstantial.load(request) + } + + private func getInterstitialAd(id: Int, interstantialAdUnitId: String) -> GADInterstitial { + if allIds[id] == nil { + let interstantialAd = GADInterstitial(adUnitID: interstantialAdUnitId) + allIds[id] = interstantialAd + } + + return allIds[id]! + } +} + +class AdmobIntersitialPluginDelegate: NSObject, GADInterstitialDelegate { + let channel: FlutterMethodChannel + + init(channel: FlutterMethodChannel) { + self.channel = channel + } + + // TODO: not sure this exists on iOS. + // channel.invokeMethod("impression", null) + + func interstitialWillPresentScreen(_ ad: GADInterstitial) { + channel.invokeMethod("clicked", arguments: nil) + channel.invokeMethod("opened", arguments: nil) + } + + func interstitialWillDismissScreen(_ ad: GADInterstitial) { + // Unused + } + + func interstitialDidDismissScreen(_ ad: GADInterstitial) { + channel.invokeMethod("closed", arguments: nil) + } + + func interstitialWillLeaveApplication(_ ad: GADInterstitial) { + channel.invokeMethod("leftApplication", arguments: nil) + } + + func interstitialDidReceiveAd(_ ad: GADInterstitial) { + channel.invokeMethod("loaded", arguments: nil) + } + + func interstitial(_ ad: GADInterstitial, didFailToReceiveAdWithError error: GADRequestError) { + channel.invokeMethod("failedToLoad", arguments: ["errorCode": error]) + } + + func interstitialDidFail(toPresentScreen ad: GADInterstitial) { + channel.invokeMethod("failedToLoad", arguments: ["errorCode": ad.isReady && ad.hasBeenUsed]) + } +} diff --git a/ios/Classes/SwiftAdmobFlutterPlugin.swift b/ios/Classes/SwiftAdmobFlutterPlugin.swift index 7621490..473d7f1 100644 --- a/ios/Classes/SwiftAdmobFlutterPlugin.swift +++ b/ios/Classes/SwiftAdmobFlutterPlugin.swift @@ -19,7 +19,6 @@ THE SOFTWARE. */ -import Flutter import UIKit import GoogleMobileAds @@ -27,15 +26,16 @@ public class SwiftAdmobFlutterPlugin: NSObject, FlutterPlugin { public static func register(with registrar: FlutterPluginRegistrar) { let instance = SwiftAdmobFlutterPlugin() + let defaultChannel = FlutterMethodChannel(name: "admob_flutter", binaryMessenger: registrar.messenger()) registrar.addMethodCallDelegate(instance, channel: defaultChannel) - let interstitialChannel = FlutterMethodChannel(name: "admob_flutter/interstitial", binaryMessenger: registrar.messenger()) + +// let interstitialChannel = FlutterMethodChannel(name: "admob_flutter/interstitial", binaryMessenger: registrar.messenger()) // registrar.addMethodCallDelegate(AdmobIntersitial(), channel: interstitialChannel) - registrar.addMethodCallDelegate(instance, channel: interstitialChannel) - - let rewardChannel = FlutterMethodChannel(name: "admob_flutter/reward", binaryMessenger: registrar.messenger()) - registrar.addMethodCallDelegate(instance, channel: rewardChannel) + +// let rewardChannel = FlutterMethodChannel(name: "admob_flutter/reward", binaryMessenger: registrar.messenger()) +// registrar.addMethodCallDelegate(instance, channel: rewardChannel) registrar.register( AdmobBannerFactory(messeneger: registrar.messenger()), @@ -47,16 +47,14 @@ public class SwiftAdmobFlutterPlugin: NSObject, FlutterPlugin { switch call.method { case "initialize": GADMobileAds.sharedInstance().start { (initializationStatus: GADInitializationStatus) in - print("initializationStatus:") - print(initializationStatus) + print("initializationStatus: \(initializationStatus)") } break case "getPlatformVersion": result("iOS " + UIDevice.current.systemVersion) break default: - result("success") -// result(FlutterMethodNotImplemented) + result(FlutterMethodNotImplemented) } } } From b96002e4fa1c4fb650eb210d57d486d036c53b49 Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Tue, 28 May 2019 21:07:29 -0500 Subject: [PATCH 04/10] Add Interstitial, update license --- ios/Classes/AdmobBanner.swift | 28 ++-- ios/Classes/AdmobBannerFactory.swift | 27 ++-- ios/Classes/AdmobFlutterPlugin.h | 27 ++-- ios/Classes/AdmobFlutterPlugin.m | 28 ++-- ios/Classes/AdmobInterstitialPlugin.swift | 27 ++-- ios/Classes/AdmobRewardPlugin.swift | 148 ++++++++++++++++++++++ ios/Classes/SwiftAdmobFlutterPlugin.swift | 36 ++---- 7 files changed, 222 insertions(+), 99 deletions(-) create mode 100644 ios/Classes/AdmobRewardPlugin.swift diff --git a/ios/Classes/AdmobBanner.swift b/ios/Classes/AdmobBanner.swift index da29737..dcb2c78 100644 --- a/ios/Classes/AdmobBanner.swift +++ b/ios/Classes/AdmobBanner.swift @@ -1,25 +1,21 @@ /* + ISC License + Copyright (c) 2019 Kevin McGill - 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: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. - 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. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ -import Flutter import Foundation import GoogleMobileAds diff --git a/ios/Classes/AdmobBannerFactory.swift b/ios/Classes/AdmobBannerFactory.swift index d56510c..4f5af53 100644 --- a/ios/Classes/AdmobBannerFactory.swift +++ b/ios/Classes/AdmobBannerFactory.swift @@ -1,22 +1,19 @@ /* + ISC License + Copyright (c) 2019 Kevin McGill - 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: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. - 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. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ import Flutter diff --git a/ios/Classes/AdmobFlutterPlugin.h b/ios/Classes/AdmobFlutterPlugin.h index 4c7433f..11d17e4 100644 --- a/ios/Classes/AdmobFlutterPlugin.h +++ b/ios/Classes/AdmobFlutterPlugin.h @@ -1,22 +1,19 @@ /* + ISC License + Copyright (c) 2019 Kevin McGill - 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: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. - 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. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #import diff --git a/ios/Classes/AdmobFlutterPlugin.m b/ios/Classes/AdmobFlutterPlugin.m index b8cc2b5..53525c2 100644 --- a/ios/Classes/AdmobFlutterPlugin.m +++ b/ios/Classes/AdmobFlutterPlugin.m @@ -1,22 +1,19 @@ /* + ISC License + Copyright (c) 2019 Kevin McGill - 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: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. - 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. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ #import "AdmobFlutterPlugin.h" @@ -27,5 +24,6 @@ @implementation AdmobFlutterPlugin + (void)registerWithRegistrar:(NSObject*)registrar { [SwiftAdmobFlutterPlugin registerWithRegistrar: registrar]; [AdmobIntersitialPlugin registerWithRegistrar: registrar]; + [AdmobRewardPlugin registerWithRegistrar: registrar]; } @end diff --git a/ios/Classes/AdmobInterstitialPlugin.swift b/ios/Classes/AdmobInterstitialPlugin.swift index 16e6da6..d4bcb78 100644 --- a/ios/Classes/AdmobInterstitialPlugin.swift +++ b/ios/Classes/AdmobInterstitialPlugin.swift @@ -1,22 +1,19 @@ /* + ISC License + Copyright (c) 2019 Kevin McGill - 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: - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. - 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. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ import Foundation diff --git a/ios/Classes/AdmobRewardPlugin.swift b/ios/Classes/AdmobRewardPlugin.swift new file mode 100644 index 0000000..292758c --- /dev/null +++ b/ios/Classes/AdmobRewardPlugin.swift @@ -0,0 +1,148 @@ +/* + ISC License + + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. + + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +import Foundation +import GoogleMobileAds + +public class AdmobRewardPlugin: NSObject, FlutterPlugin { + + fileprivate var allIds: [Int: GADRewardBasedVideoAd] = [:] + fileprivate var delegates: [Int: GADRewardBasedVideoAdDelegate] = [:] + fileprivate var pluginRegistrar: FlutterPluginRegistrar? + + public static func register(with registrar: FlutterPluginRegistrar) { + let instance = AdmobRewardPlugin() + instance.pluginRegistrar = registrar + let rewardChannel = FlutterMethodChannel(name: "admob_flutter/reward", binaryMessenger: registrar.messenger()) + registrar.addMethodCallDelegate(instance, channel: rewardChannel) + } + + public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { + guard let args = call.arguments as? [String : Any] else { + result(FlutterError( + code: "Missing args!", + message: "Unable to convert args to [String : Any]", + details: nil) + ) + return + } + let id = args["id"] as? Int ?? 0 + let adUnitId = args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/1712485313" + + switch call.method { + case "setListener": + let channel = FlutterMethodChannel( + name: "admob_flutter/interstitial_\(id)", + binaryMessenger: pluginRegistrar!.messenger() + ) + delegates[id] = AdmobRewardPluginDelegate(channel: channel) + let rewardVideo = getRewardBasedVideoAd(id: id) + rewardVideo.delegate = delegates[id] + break + case "load": + allIds[id] = getRewardBasedVideoAd(id: id) + loadRewardBasedVideoAd(id: id, rewardBasedVideoAdUnitId: adUnitId) + result(nil) + break + case "isLoaded": + result(getRewardBasedVideoAd(id: id).isReady) + break + case "show": + let rewardVideo = getRewardBasedVideoAd(id: id) + if rewardVideo.isReady, let rootViewController = UIApplication.shared.keyWindow?.rootViewController { + rewardVideo.present(fromRootViewController: rootViewController) + } else { + result(FlutterError( + code: "GADRewardBasedVideoAd Error", + message: "Failed to present reward video", + details: nil) + ) + } + break + case "dispose": + allIds.removeValue(forKey: id) + delegates.removeValue(forKey: id) + break + default: + result(FlutterMethodNotImplemented) + } + } + + private func loadRewardBasedVideoAd(id: Int, rewardBasedVideoAdUnitId: String) { + let interstantial = getRewardBasedVideoAd(id: id) + let request = GADRequest() + request.testDevices = [kGADSimulatorID] + interstantial.load(request, withAdUnitID: rewardBasedVideoAdUnitId) + } + + private func getRewardBasedVideoAd(id: Int) -> GADRewardBasedVideoAd { + if allIds[id] == nil { + let rewardBadedVideoAd = GADRewardBasedVideoAd.sharedInstance() + allIds[id] = rewardBadedVideoAd + } + + return allIds[id]! + } +} + +class AdmobRewardPluginDelegate: NSObject, GADRewardBasedVideoAdDelegate { + let channel: FlutterMethodChannel + + init(channel: FlutterMethodChannel) { + self.channel = channel + } + + func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd, didRewardUserWith reward: GADAdReward) { + channel.invokeMethod("rewarded", arguments: [ + "type": reward.type, + "amount": reward.amount + ]) + } + + func rewardBasedVideoAdDidOpen(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("opened", arguments: nil) + } + + func rewardBasedVideoAdDidClose(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("closed", arguments: nil) + } + + func rewardBasedVideoAdDidReceive(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("loaded", arguments: nil) + } + + func rewardBasedVideoAdDidStartPlaying(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("started", arguments: nil) + } + + func rewardBasedVideoAdMetadataDidChange(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + // UNUSED + } + + func rewardBasedVideoAdDidCompletePlaying(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("completed", arguments: nil) + } + + func rewardBasedVideoAdWillLeaveApplication(_ rewardBasedVideoAd: GADRewardBasedVideoAd) { + channel.invokeMethod("leftApplication", arguments: nil) + } + + func rewardBasedVideoAd(_ rewardBasedVideoAd: GADRewardBasedVideoAd, didFailToLoadWithError error: Error) { + channel.invokeMethod("failedToLoad", arguments: ["errorCode": error.localizedDescription]) + } +} diff --git a/ios/Classes/SwiftAdmobFlutterPlugin.swift b/ios/Classes/SwiftAdmobFlutterPlugin.swift index 473d7f1..be06b1f 100644 --- a/ios/Classes/SwiftAdmobFlutterPlugin.swift +++ b/ios/Classes/SwiftAdmobFlutterPlugin.swift @@ -1,22 +1,19 @@ /* - Copyright (c) 2019 Kevin McGill - 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: + ISC License - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. + Copyright (c) 2019 Kevin McGill + + Permission to use, copy, modify, and/or distribute this software for any + purpose with or without fee is hereby granted, provided that the above + copyright notice and this permission notice appear in all copies. - 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. + THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ import UIKit @@ -29,14 +26,7 @@ public class SwiftAdmobFlutterPlugin: NSObject, FlutterPlugin { let defaultChannel = FlutterMethodChannel(name: "admob_flutter", binaryMessenger: registrar.messenger()) registrar.addMethodCallDelegate(instance, channel: defaultChannel) - - -// let interstitialChannel = FlutterMethodChannel(name: "admob_flutter/interstitial", binaryMessenger: registrar.messenger()) -// registrar.addMethodCallDelegate(AdmobIntersitial(), channel: interstitialChannel) -// let rewardChannel = FlutterMethodChannel(name: "admob_flutter/reward", binaryMessenger: registrar.messenger()) -// registrar.addMethodCallDelegate(instance, channel: rewardChannel) - registrar.register( AdmobBannerFactory(messeneger: registrar.messenger()), withId: "admob_flutter/banner" From 7924b58dd838e2aa8932cc5eb98d57844de10880 Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Tue, 28 May 2019 23:32:56 -0500 Subject: [PATCH 05/10] =?UTF-8?q?Add=20iOS=20support!,=20choose=20test=20i?= =?UTF-8?q?d=E2=80=99s=20per=20platform,=20bump=20to=200.3.0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 6 +- LICENSE | 1 + example/lib/main.dart | 69 +++++++++++++++++++++-- ios/Classes/AdmobBanner.swift | 2 +- ios/Classes/AdmobInterstitialPlugin.swift | 11 +++- ios/Classes/AdmobRewardPlugin.swift | 7 ++- pubspec.yaml | 2 +- 7 files changed, 85 insertions(+), 13 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e16ea5f..2b1f2b1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,9 @@ -## [0.2.0] - 2019/04/04 +## [0.3.0] - 2019/05/28 +Add iOS support! +## [0.2.0] - 2019/04/04 Update to AndroidX. +> **Note:** This is a breaking change on Android! ## [0.1.2] - Dec 10 - * Release first version diff --git a/LICENSE b/LICENSE index e2b077f..3976614 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,7 @@ ISC License Copyright (c) 2018, Youssef Kababe +Copyright (c) 2019, Kevin McGill Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above diff --git a/example/lib/main.dart b/example/lib/main.dart index 02343d8..2cacfb1 100644 --- a/example/lib/main.dart +++ b/example/lib/main.dart @@ -1,9 +1,11 @@ +import 'dart:io'; + import 'package:flutter/material.dart'; import 'package:admob_flutter/admob_flutter.dart'; void main() { - Admob.initialize('ca-app-pub-3940256099942544~3347511713'); + Admob.initialize(getAppId()); runApp(MyApp()); } @@ -24,7 +26,7 @@ class _MyAppState extends State { bannerSize = AdmobBannerSize.BANNER; interstitialAd = AdmobInterstitial( - adUnitId: 'ca-app-pub-3940256099942544/1033173712', + adUnitId: getInterstitialAdUnitId(), listener: (AdmobAdEvent event, Map args) { if (event == AdmobAdEvent.closed) interstitialAd.load(); handleEvent(event, args, 'Interstitial'); @@ -32,7 +34,7 @@ class _MyAppState extends State { ); rewardAd = AdmobReward( - adUnitId: 'ca-app-pub-3940256099942544/5224354917', + adUnitId: getRewardBasedVideoAdUnitId(), listener: (AdmobAdEvent event, Map args) { if (event == AdmobAdEvent.closed) rewardAd.load(); handleEvent(event, args, 'Reward'); @@ -199,7 +201,7 @@ class _MyAppState extends State { Container( margin: EdgeInsets.only(bottom: 20.0), child: AdmobBanner( - adUnitId: 'ca-app-pub-3940256099942544/6300978111', + adUnitId: getBannerAdUnitId(), adSize: bannerSize, listener: (AdmobAdEvent event, Map args) { @@ -233,3 +235,62 @@ class _MyAppState extends State { super.dispose(); } } + +/* +Test Id's from: +https://developers.google.com/admob/ios/banner +https://developers.google.com/admob/android/banner + +App Id +Android: ca-app-pub-3940256099942544~3347511713 +iOS: ca-app-pub-3940256099942544~1458002511 + +Banner +Android: ca-app-pub-3940256099942544/6300978111 +iOS: ca-app-pub-3940256099942544/2934735716 + +Interstitial +Android: ca-app-pub-3940256099942544/1033173712 +iOS: ca-app-pub-3940256099942544/4411468910 + +Reward Video +Android: ca-app-pub-3940256099942544/5224354917 +iOS: ca-app-pub-3940256099942544/1712485313 +*/ + +String getAppId() { + if (Platform.isIOS) { + return 'ca-app-pub-3940256099942544~1458002511'; + } else if (Platform.isAndroid) { + return 'ca-app-pub-3940256099942544~3347511713'; + } + return null; +} + +String getBannerAdUnitId() { + if (Platform.isIOS) { + return 'ca-app-pub-3940256099942544/2934735716'; + } else if (Platform.isAndroid) { + return 'ca-app-pub-3940256099942544/6300978111'; + } + return null; +} + +String getInterstitialAdUnitId() { + if (Platform.isIOS) { + return 'ca-app-pub-3940256099942544/4411468910'; + } else if (Platform.isAndroid) { + return 'ca-app-pub-3940256099942544/1033173712'; + } + return null; +} + +String getRewardBasedVideoAdUnitId() { + if (Platform.isIOS) { + return 'ca-app-pub-3940256099942544/1712485313'; + } else if (Platform.isAndroid) { + return 'ca-app-pub-3940256099942544/5224354917'; + } + return null; +} + diff --git a/ios/Classes/AdmobBanner.swift b/ios/Classes/AdmobBanner.swift index dcb2c78..95d7a35 100644 --- a/ios/Classes/AdmobBanner.swift +++ b/ios/Classes/AdmobBanner.swift @@ -51,7 +51,7 @@ class AdmobBanner : NSObject, FlutterPlatformView { adView = GADBannerView() adView!.rootViewController = UIApplication.shared.keyWindow?.rootViewController adView!.frame = self.frame.width == 0 ? CGRect(x: 0, y: 0, width: 1, height: 1) : self.frame - adView!.adUnitID = self.args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/6300978111" + adView!.adUnitID = self.args["adUnitId"] as? String ?? "ca-app-pub-3940256099942544/2934735716" channel.setMethodCallHandler { [weak self] (flutterMethodCall: FlutterMethodCall, flutterResult: FlutterResult) in switch flutterMethodCall.method { case "setListener": diff --git a/ios/Classes/AdmobInterstitialPlugin.swift b/ios/Classes/AdmobInterstitialPlugin.swift index d4bcb78..25dc631 100644 --- a/ios/Classes/AdmobInterstitialPlugin.swift +++ b/ios/Classes/AdmobInterstitialPlugin.swift @@ -16,6 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +import Flutter import Foundation import GoogleMobileAds @@ -56,7 +57,8 @@ public class AdmobIntersitialPlugin: NSObject, FlutterPlugin { result(nil) break case "isLoaded": - result(getInterstitialAd(id: id, interstantialAdUnitId: adUnitId).isReady) + let interstitial = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) + result(interstitial.isReady && !interstitial.hasBeenUsed) break case "show": let interstitial = getInterstitialAd(id: id, interstantialAdUnitId: adUnitId) @@ -83,7 +85,12 @@ public class AdmobIntersitialPlugin: NSObject, FlutterPlugin { } private func getInterstitialAd(id: Int, interstantialAdUnitId: String) -> GADInterstitial { - if allIds[id] == nil { + if let interstantialAd = allIds[id] { + if (interstantialAd.hasBeenUsed) { + let interstantialAd = GADInterstitial(adUnitID: interstantialAdUnitId) + allIds[id] = interstantialAd + } + } else { let interstantialAd = GADInterstitial(adUnitID: interstantialAdUnitId) allIds[id] = interstantialAd } diff --git a/ios/Classes/AdmobRewardPlugin.swift b/ios/Classes/AdmobRewardPlugin.swift index 292758c..ecc50a5 100644 --- a/ios/Classes/AdmobRewardPlugin.swift +++ b/ios/Classes/AdmobRewardPlugin.swift @@ -16,6 +16,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +import Flutter import Foundation import GoogleMobileAds @@ -47,7 +48,7 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin { switch call.method { case "setListener": let channel = FlutterMethodChannel( - name: "admob_flutter/interstitial_\(id)", + name: "admob_flutter/reward_\(id)", binaryMessenger: pluginRegistrar!.messenger() ) delegates[id] = AdmobRewardPluginDelegate(channel: channel) @@ -55,12 +56,12 @@ public class AdmobRewardPlugin: NSObject, FlutterPlugin { rewardVideo.delegate = delegates[id] break case "load": - allIds[id] = getRewardBasedVideoAd(id: id) loadRewardBasedVideoAd(id: id, rewardBasedVideoAdUnitId: adUnitId) result(nil) break case "isLoaded": - result(getRewardBasedVideoAd(id: id).isReady) + let isReady = getRewardBasedVideoAd(id: id).isReady + result(isReady) break case "show": let rewardVideo = getRewardBasedVideoAd(id: id) diff --git a/pubspec.yaml b/pubspec.yaml index 6458d7d..b74f39b 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,6 +1,6 @@ name: admob_flutter description: Admob plugin that shows banner ads using native platform views. -version: 0.2.0 +version: 0.3.0 author: Youssef Kababe homepage: https://github.com/YoussefKababe/admob_flutter From ea22ff5e080e6c517fc1eb5baf4628833bdcec12 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?T=C3=A9ury=20Sim=C3=B5es=20Bazzo?= Date: Thu, 23 May 2019 12:56:57 -0400 Subject: [PATCH 06/10] alter README (#27) --- README.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index d84edc0..bca172a 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,11 @@ This is an early version of the plugin, my primary goal was to overcome the bann Add this to your pubspec.yml dependencies: ```yaml -admob_flutter: "^0.1.2" +admob_flutter: "^0.2.0" ``` - +## Suport +- AndroidX +- FlutterSdk >=2.1.0 < 3.0.0 ## How to use ### Update your AndroidManifest.xml From f0ba4340ec36c0b6a241bea38f6f9bfdbcdb41b0 Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Tue, 28 May 2019 23:47:34 -0500 Subject: [PATCH 07/10] Update README --- README.md | 34 ++++++++++++++++++++-------------- 1 file changed, 20 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index bca172a..2c03751 100644 --- a/README.md +++ b/README.md @@ -16,18 +16,19 @@ This is an early version of the plugin, my primary goal was to overcome the bann **Currently working on Android only, but iOS support will be available so soon.** -## Installation +# Installation Add this to your pubspec.yml dependencies: ```yaml -admob_flutter: "^0.2.0" +admob_flutter: "^0.3.0" ``` -## Suport -- AndroidX +### Supported Platforms +- `0.3.0` >= iOS +- `0.2.0` >= AndroidX - FlutterSdk >=2.1.0 < 3.0.0 -## How to use +## Android ### Update your AndroidManifest.xml Add your AdMob App ID to your app's AndroidManifest.xml file by adding the tag shown below. You can find your App ID in the AdMob UI. For android:value insert your own AdMob App ID in quotes, as shown below. @@ -46,7 +47,14 @@ ca-app-pub-3940256099942544~3347511713 ``` +## iOS +Update your `Info.plist` per [Firebase instructions](https://developers.google.com/admob/ios/quick-start#update_your_infoplist). +```xml +GADApplicationIdentifier +ca-app-pub-3940256099942544~1458002511 +``` +# How to use ### Initialize the plugin First thing to do before attempting to show any ads is to initialize the plugin. You can do this in the earliest starting point of your app, your `main` function: @@ -55,7 +63,7 @@ First thing to do before attempting to show any ads is to initialize the plugin. import 'package:admob_flutter/admob_flutter.dart'; void main() { - Admob.initialize('ca-app-pub-3940256099942544~3347511713'); + Admob.initialize(getAppId()); runApp(MyApp()); } ``` @@ -66,7 +74,7 @@ This plugin uses Native Platform Views to show Admob banner ads thus, same as an ```dart AdmobBanner( - adUnitId: 'ca-app-pub-3940256099942544/6300978111', + adUnitId: getBannerAdUnitId(), adSize: AdmobBannerSize.BANNER, ) ``` @@ -88,7 +96,7 @@ You can attach a listener to your ads in order to customize their behavior like ```dart AdmobBanner( - adUnitId: 'ca-app-pub-3940256099942544/6300978111', + adUnitId: getBannerAdUnitId(), adSize: AdmobBannerSize.BANNER, listener: (AdmobAdEvent event, Map args) { switch (event) { @@ -117,7 +125,7 @@ AdmobBanner( ```dart // First, create an interstitial ad AdmobInterstitial interstitialAd = AdmobInterstitial( - adUnitId: 'ca-app-pub-3940256099942544/1033173712', + adUnitId: getInterstitialAdUnitId(), ); // Interstitials must be loaded before shown. @@ -148,7 +156,7 @@ You can do something like this (Don't do it!): AdmobInterstitial interstitialAd; interstitialAd = AdmobInterstitial( - adUnitId: 'ca-app-pub-3940256099942544/1033173712', + adUnitId: getInterstitialAdUnitId(), listener: (AdmobAdEvent event, Map args) { if (event == AdmobAdEvent.loaded) interstitialAd.show(); if (event == AdmobAdEvent.closed) interstitialAd.load(); @@ -165,7 +173,7 @@ interstitialAd = AdmobInterstitial( ```dart // First, create a reward ad AdmobReward rewardAd = AdmobReward( - adUnitId: 'ca-app-pub-3940256099942544/5224354917', + adUnitId: getRewardBasedVideoAdUnitId(), ); // Reward ads must be loaded before shown. @@ -188,7 +196,7 @@ Listening to reward ads events works the same as with banners and interstitials, ```dart AdmobReward rewardAd = AdmobReward( - adUnitId: 'ca-app-pub-3940256099942544/5224354917', + adUnitId: getRewardBasedVideoAdUnitId(), listener: (AdmobAdEvent event, Map args) { if (event == AdmobAdEvent.rewarded) { print('User was rewarded!'); @@ -200,9 +208,7 @@ AdmobReward rewardAd = AdmobReward( ``` ### Showing Native Ads - **Coming soon!** ### Using targeting info - **Coming soon!** From 609ddee0f56c3cc3a4fc7fe9949637d407edb20f Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Wed, 29 May 2019 17:07:43 -0500 Subject: [PATCH 08/10] Update readme and iOS author --- README.md | 2 -- ios/admob_flutter.podspec | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/README.md b/README.md index 2c03751..d694549 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,6 @@ This plugin also has support for Interstitial and Reward ads. This is an early version of the plugin, my primary goal was to overcome the banner ads positioning limitations by implementing a platform views solution, and keeping things simple and stupid because I'm still learning about many Flutter related stuff. I will actively work on adding missing features, improving, and refactoring the code in my free time. -**Currently working on Android only, but iOS support will be available so soon.** - # Installation Add this to your pubspec.yml dependencies: diff --git a/ios/admob_flutter.podspec b/ios/admob_flutter.podspec index f6db9e9..7970498 100644 --- a/ios/admob_flutter.podspec +++ b/ios/admob_flutter.podspec @@ -3,14 +3,14 @@ # Pod::Spec.new do |s| s.name = 'admob_flutter' - s.version = '0.2.0' + s.version = '0.3.0' s.summary = 'Admob plugin that shows banner ads using native platform views.' s.description = <<-DESC Admob plugin that shows banner ads using native platform views. DESC s.homepage = 'https://github.com/YoussefKababe/admob_flutter' s.license = { :file => '../LICENSE' } - s.author = { 'admob_flutter' => 'email@admobflutter.fake' } + s.author = { 'Kevin McGill' => 'kevin@mcgilldevtech.com' } s.source = { :path => '.' } s.source_files = 'Classes/**/*' s.public_header_files = 'Classes/**/*.h' From 70a8387e9e609c3d4ee3838c8a898c0f9d72c594 Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Wed, 29 May 2019 20:38:53 -0500 Subject: [PATCH 09/10] Remove demo getPlatformVersion --- ios/Classes/SwiftAdmobFlutterPlugin.swift | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/ios/Classes/SwiftAdmobFlutterPlugin.swift b/ios/Classes/SwiftAdmobFlutterPlugin.swift index be06b1f..c2d2ca4 100644 --- a/ios/Classes/SwiftAdmobFlutterPlugin.swift +++ b/ios/Classes/SwiftAdmobFlutterPlugin.swift @@ -36,12 +36,7 @@ public class SwiftAdmobFlutterPlugin: NSObject, FlutterPlugin { public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { switch call.method { case "initialize": - GADMobileAds.sharedInstance().start { (initializationStatus: GADInitializationStatus) in - print("initializationStatus: \(initializationStatus)") - } - break - case "getPlatformVersion": - result("iOS " + UIDevice.current.systemVersion) + GADMobileAds.sharedInstance().start(completionHandler: nil) break default: result(FlutterMethodNotImplemented) From 1cdfe13a4b8201db7a707514a6ef532d081fa8a4 Mon Sep 17 00:00:00 2001 From: Kevin McGill Date: Wed, 29 May 2019 21:41:54 -0500 Subject: [PATCH 10/10] Update readme with io.flutter.embedded_views_preview --- README.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/README.md b/README.md index d694549..1a39d86 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,11 @@ Update your `Info.plist` per [Firebase instructions](https://developers.google.c GADApplicationIdentifier ca-app-pub-3940256099942544~1458002511 ``` +and add +```xml +io.flutter.embedded_views_preview + +``` # How to use ### Initialize the plugin