@capacitor-community/admob 8.2.0-beta.pr483.shac8cc94ca69ee → 8.2.0-beta.pr484.shaa6be63853bbd

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,6 @@ Pod::Spec.new do |s|
15
15
  s.swift_version = '5.1'
16
16
  s.static_framework = true
17
17
  s.dependency 'Capacitor'
18
- s.dependency 'Google-Mobile-Ads-SDK', '13.6.0'
18
+ s.dependency 'Google-Mobile-Ads-SDK', '13.11.0'
19
19
  s.dependency 'GoogleUserMessagingPlatform', '~> 3.1'
20
20
  end
package/Package.swift CHANGED
@@ -11,7 +11,7 @@ let package = Package(
11
11
  ],
12
12
  dependencies: [
13
13
  .package(url: "https://github.com/ionic-team/capacitor-swift-pm.git", from: "8.0.0"),
14
- .package(url: "https://github.com/googleads/swift-package-manager-google-mobile-ads.git", exact: "13.6.0"),
14
+ .package(url: "https://github.com/googleads/swift-package-manager-google-mobile-ads.git", exact: "13.11.0"),
15
15
  .package(url: "https://github.com/googleads/swift-package-manager-google-user-messaging-platform.git", .upToNextMinor(from: "3.1.0"))
16
16
  ],
17
17
  targets: [
package/README.md CHANGED
@@ -67,7 +67,7 @@ If you still use Capacitor 7, install `@capacitor-community/admob@7`.
67
67
 
68
68
  ### Google Mobile Ads SDK versions
69
69
 
70
- This major version pins Google Mobile Ads SDK **25.4.x** on Android and **13.6.0** on iOS (Swift Package Manager and CocoaPods). Leave those versions unless you have a specific need. Google's [Next-Gen SDK for Android](https://developers.google.com/admob/android/next-gen) waits until the next plugin major. See [Migration](https://docs.rdlabo.dev/projects/capacitor-admob/docs/migration) for the policy behind the pins.
70
+ This major version pins Google Mobile Ads SDK **25.4.x** on Android and **13.11.0** on iOS (Swift Package Manager and CocoaPods). Leave those versions unless you have a specific need. Google's [Next-Gen SDK for Android](https://developers.google.com/admob/android/next-gen) waits until the next plugin major. See [Migration](https://docs.rdlabo.dev/projects/capacitor-admob/docs/migration) for the policy behind the pins.
71
71
 
72
72
  ### Android configuration
73
73
 
package/docs/migration.md CHANGED
@@ -46,7 +46,9 @@ This major version keeps Google Mobile Ads SDK APIs that are deprecated but stil
46
46
 
47
47
  Google's [Next-Gen SDK for Android](https://developers.google.com/admob/android/next-gen) also waits for the next major: it changes SDK initialization, ad requests, and mediation.
48
48
 
49
- Pinned versions: Android 25.4.x, iOS 13.6.0 (Swift Package Manager and CocoaPods). CocoaPods support is planned to be removed in the next major.
49
+ Pinned versions: Android 25.4.x, iOS 13.11.0 (Swift Package Manager and CocoaPods). CocoaPods support is planned to be removed in the next major.
50
+
51
+ Android remains on 25.4.x because [SDK 25.5.0 raises the minimum Android API level](https://developers.google.com/admob/android/rel-notes). The iOS update from 13.6.0 to 13.11.0 keeps the plugin's existing platform and toolchain requirements; no public plugin API changes are required.
50
52
 
51
53
  ## Breaking changes from earlier versions
52
54
 
@@ -69,6 +69,36 @@ public class AdMobPlugin: CAPPlugin, CAPBridgedPlugin {
69
69
  self.consentExecutor.plugin = self
70
70
  }
71
71
 
72
+ /// Full-screen ads that currently have web media suspended.
73
+ private var webMediaSuspenders = Set<ObjectIdentifier>()
74
+
75
+ /// Pauses all web media while a full-screen ad is up, so WebKit releases its audio before the ad plays
76
+ /// and sets it up again afterwards, instead of leaving web audio silent until the app restarts.
77
+ func suspendWebMedia(for owner: AnyObject) {
78
+ Task { @MainActor [weak self] in
79
+ guard let self, self.webMediaSuspenders.insert(ObjectIdentifier(owner)).inserted,
80
+ self.webMediaSuspenders.count == 1
81
+ else { return }
82
+ self.setWebMediaPlaybackSuspended(true)
83
+ }
84
+ }
85
+
86
+ /// Only resumes once every ad that suspended has resumed, so a failed present (which can arrive
87
+ /// without a preceding adWillPresentFullScreenContent) can't release another ad's suspension.
88
+ func resumeWebMedia(for owner: AnyObject) {
89
+ Task { @MainActor [weak self] in
90
+ guard let self, self.webMediaSuspenders.remove(ObjectIdentifier(owner)) != nil,
91
+ self.webMediaSuspenders.isEmpty
92
+ else { return }
93
+ self.setWebMediaPlaybackSuspended(false)
94
+ }
95
+ }
96
+
97
+ @MainActor
98
+ func setWebMediaPlaybackSuspended(_ suspended: Bool) {
99
+ self.bridge?.webView?.setAllMediaPlaybackSuspended(suspended, completionHandler: nil)
100
+ }
101
+
72
102
  /**
73
103
  * Enable SKAdNetwork to track conversions
74
104
  * https://developers.google.com/admob/ios/ios14
@@ -73,6 +73,7 @@ class AdInterstitialExecutor: NSObject, FullScreenContentDelegate {
73
73
  }
74
74
 
75
75
  func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
76
+ self.plugin?.resumeWebMedia(for: ad)
76
77
  removeCurrentlyShowingAd()
77
78
  NSLog("InterstitialFullScreenDelegate Ad failed to present full screen content with error \(error.localizedDescription).")
78
79
  self.plugin?.notifyListeners(InterstitialAdPluginEvents.FailedToShow.rawValue, data: [
@@ -82,11 +83,13 @@ class AdInterstitialExecutor: NSObject, FullScreenContentDelegate {
82
83
  }
83
84
 
84
85
  func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
86
+ self.plugin?.suspendWebMedia(for: ad)
85
87
  NSLog("InterstitialFullScreenDelegate Ad did present full screen content.")
86
88
  self.plugin?.notifyListeners(InterstitialAdPluginEvents.Showed.rawValue, data: [:])
87
89
  }
88
90
 
89
91
  func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
92
+ self.plugin?.resumeWebMedia(for: ad)
90
93
  removeCurrentlyShowingAd()
91
94
  NSLog("InterstitialFullScreenDelegate Ad did dismiss full screen content.")
92
95
  self.plugin?.notifyListeners(InterstitialAdPluginEvents.Dismissed.rawValue, data: [:])
@@ -97,6 +97,7 @@ class AdRewardExecutor: NSObject, FullScreenContentDelegate {
97
97
  }
98
98
 
99
99
  func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
100
+ self.plugin?.resumeWebMedia(for: ad)
100
101
  removeCurrentlyShowingAd()
101
102
  NSLog("RewardFullScreenDelegate Ad failed to present full screen content with error \(error.localizedDescription).")
102
103
  self.plugin?.notifyListeners(RewardAdPluginEvents.FailedToShow.rawValue, data: [
@@ -106,6 +107,7 @@ class AdRewardExecutor: NSObject, FullScreenContentDelegate {
106
107
  }
107
108
 
108
109
  func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
110
+ self.plugin?.suspendWebMedia(for: ad)
109
111
  NSLog("RewardFullScreenDelegate Ad did present full screen content.")
110
112
  self.plugin?.notifyListeners(RewardAdPluginEvents.Showed.rawValue, data: [:])
111
113
  }
@@ -115,6 +117,7 @@ class AdRewardExecutor: NSObject, FullScreenContentDelegate {
115
117
  }
116
118
 
117
119
  func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
120
+ self.plugin?.resumeWebMedia(for: ad)
118
121
  removeCurrentlyShowingAd()
119
122
  NSLog("RewardFullScreenDelegate Ad did dismiss full screen content.")
120
123
  self.plugin?.notifyListeners(RewardAdPluginEvents.Dismissed.rawValue, data: [:])
@@ -97,6 +97,7 @@ class AdRewardInterstitialExecutor: NSObject, FullScreenContentDelegate {
97
97
  }
98
98
 
99
99
  func ad(_ ad: FullScreenPresentingAd, didFailToPresentFullScreenContentWithError error: Error) {
100
+ self.plugin?.resumeWebMedia(for: ad)
100
101
  removeCurrentlyShowingAd()
101
102
  NSLog("RewardFullScreenDelegate Ad failed to present full screen content with error \(error.localizedDescription).")
102
103
  self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.FailedToShow.rawValue, data: [
@@ -106,11 +107,13 @@ class AdRewardInterstitialExecutor: NSObject, FullScreenContentDelegate {
106
107
  }
107
108
 
108
109
  func adWillPresentFullScreenContent(_ ad: FullScreenPresentingAd) {
110
+ self.plugin?.suspendWebMedia(for: ad)
109
111
  NSLog("RewardFullScreenDelegate Ad did present full screen content.")
110
112
  self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.Showed.rawValue, data: [:])
111
113
  }
112
114
 
113
115
  func adDidDismissFullScreenContent(_ ad: FullScreenPresentingAd) {
116
+ self.plugin?.resumeWebMedia(for: ad)
114
117
  removeCurrentlyShowingAd()
115
118
  NSLog("RewardFullScreenDelegate Ad did dismiss full screen content.")
116
119
  self.plugin?.notifyListeners(RewardInterstitialAdPluginEvents.Dismissed.rawValue, data: [:])
@@ -1,15 +1,50 @@
1
1
  import XCTest
2
2
  @testable import AdMobPlugin
3
3
 
4
- class AdMobTests: XCTestCase {
5
- func testEcho() {
6
- // This is an example of a functional test case for a plugin.
7
- // Use XCTAssert and related functions to verify your tests produce the correct results.
8
-
9
- // let implementation = AdMob()
10
- // let value = "Hello, World!"
11
- // let result = implementation.echo(value)
12
- //
13
- // XCTAssertEqual(value, result)
4
+ final class AdMobPluginTests: XCTestCase {
5
+ @MainActor
6
+ func testNormalSuspensionAndResume() async {
7
+ await checkSuspensions([(0, true, true), (0, false, false)])
8
+ }
9
+
10
+ @MainActor
11
+ func testFailedAdCannotReleaseAnotherAdsSuspension() async {
12
+ await checkSuspensions([(0, true, true), (1, false, nil), (0, false, false)])
13
+ }
14
+
15
+ @MainActor
16
+ func testOnlyLastAdResumesMedia() async {
17
+ await checkSuspensions([(0, true, true), (1, true, nil), (0, false, nil), (1, false, false)])
18
+ }
19
+
20
+ @MainActor
21
+ private func checkSuspensions(_ steps: [(Int, Bool, Bool?)]) async {
22
+ let plugin = MediaTestPlugin()
23
+ let owners = [NSObject(), NSObject()]
24
+
25
+ for (owner, suspend, expected) in steps {
26
+ let called = expectation(description: "WebKit suspension: \(String(describing: expected))")
27
+ called.isInverted = expected == nil
28
+ plugin.onSuspension = { actual in
29
+ XCTAssertEqual(actual, expected)
30
+ called.fulfill()
31
+ }
32
+ if suspend {
33
+ plugin.suspendWebMedia(for: owners[owner])
34
+ } else {
35
+ plugin.resumeWebMedia(for: owners[owner])
36
+ }
37
+ await fulfillment(of: [called], timeout: expected == nil ? 0.1 : 2)
38
+ }
39
+ plugin.onSuspension = nil
40
+ }
41
+ }
42
+
43
+ private final class MediaTestPlugin: AdMobPlugin {
44
+ var onSuspension: ((Bool) -> Void)?
45
+
46
+ @MainActor
47
+ override func setWebMediaPlaybackSuspended(_ suspended: Bool) {
48
+ onSuspension?(suspended)
14
49
  }
15
50
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@capacitor-community/admob",
3
3
  "private": false,
4
- "version": "8.2.0-beta.pr483.shac8cc94ca69ee",
4
+ "version": "8.2.0-beta.pr484.shaa6be63853bbd",
5
5
  "description": "A native plugin for AdMob",
6
6
  "main": "dist/plugin.cjs.js",
7
7
  "module": "dist/esm/index.js",
@@ -33,6 +33,7 @@
33
33
  "scripts": {
34
34
  "verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
35
35
  "verify:ios": "xcodebuild -scheme CapacitorCommunityAdmob -destination generic/platform=iOS",
36
+ "test:ios": "xcodebuild test -scheme CapacitorCommunityAdmob",
36
37
  "verify:android": "cd android && ./gradlew clean build test && cd ..",
37
38
  "verify:web": "npm run build",
38
39
  "lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",