@ququplay/admob 8.1.0 → 8.2.0
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.
- package/README.md +9 -8
- package/android/src/main/java/com/getcapacitor/community/admob/AdMob.java +12 -0
- package/android/src/main/java/com/getcapacitor/community/admob/nativead/NativeAdExecutor.java +56 -1
- package/dist/docs.json +12 -0
- package/dist/esm/native-ad/native-ad-options.interface.d.ts +8 -0
- package/ios/Sources/AdMobPlugin/NativeAd/NativeAdExecutor.swift +66 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1327,14 +1327,15 @@ https://developers.google.com/android/reference/com/google/android/gms/ads/AdErr
|
|
|
1327
1327
|
|
|
1328
1328
|
#### NativeAdOptions
|
|
1329
1329
|
|
|
1330
|
-
| Prop
|
|
1331
|
-
|
|
|
1332
|
-
| **`style`**
|
|
1333
|
-
| **`
|
|
1334
|
-
| **`
|
|
1335
|
-
| **`
|
|
1336
|
-
| **`
|
|
1337
|
-
| **`
|
|
1330
|
+
| Prop | Type | Description | Default | Since |
|
|
1331
|
+
| --------------------- | ------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------ | ----- |
|
|
1332
|
+
| **`style`** | <code><a href="#nativeadstyle">NativeAdStyle</a></code> | Colours of the native ad strip, as `#RRGGBB` (or `#AARRGGBB`). | | 8.1.0 |
|
|
1333
|
+
| **`refreshInterval`** | <code>number</code> | Seconds the ad stays on screen before a new one replaces it. Only the time it is shown counts: hidden, or with the app in the background, it waits. The old ad stays up until the new one loads. 0, the default, keeps it until it expires after an hour. | | 8.2.0 |
|
|
1334
|
+
| **`adId`** | <code>string</code> | The ad unit ID that you want to request | | 1.1.2 |
|
|
1335
|
+
| **`isTesting`** | <code>boolean</code> | You can use test mode of ad. | <code>false</code> | 1.1.2 |
|
|
1336
|
+
| **`margin`** | <code>number</code> | Margin Banner. Default is 0px; If position is BOTTOM_CENTER, margin is be margin-bottom. If position is TOP_CENTER, margin is be margin-top. | <code>0</code> | 1.1.2 |
|
|
1337
|
+
| **`npa`** | <code>boolean</code> | The default behavior of the Google Mobile Ads SDK is to serve personalized ads. Set this to true to request Non-Personalized Ads | <code>false</code> | 1.2.0 |
|
|
1338
|
+
| **`immersiveMode`** | <code>boolean</code> | Sets a flag that controls if this interstitial or reward object will be displayed in immersive mode. Call this method before show. During show, if this flag is on and immersive mode is supported, SYSTEM_UI_FLAG_IMMERSIVE_STICKY &SYSTEM_UI_FLAG_HIDE_NAVIGATION will be turned on for interstitial or reward ad. | | 7.0.3 |
|
|
1338
1339
|
|
|
1339
1340
|
|
|
1340
1341
|
#### NativeAdStyle
|
|
@@ -168,6 +168,18 @@ public class AdMob extends Plugin {
|
|
|
168
168
|
bannerExecutor.removeBanner(call);
|
|
169
169
|
}
|
|
170
170
|
|
|
171
|
+
@Override
|
|
172
|
+
protected void handleOnPause() {
|
|
173
|
+
super.handleOnPause();
|
|
174
|
+
nativeAdExecutor.onPause();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
@Override
|
|
178
|
+
protected void handleOnResume() {
|
|
179
|
+
super.handleOnResume();
|
|
180
|
+
nativeAdExecutor.onResume();
|
|
181
|
+
}
|
|
182
|
+
|
|
171
183
|
// Show a native ad across the top of the screen, loading one when there is none or it expired
|
|
172
184
|
@PluginMethod
|
|
173
185
|
public void showNativeAd(final PluginCall call) {
|
package/android/src/main/java/com/getcapacitor/community/admob/nativead/NativeAdExecutor.java
CHANGED
|
@@ -39,6 +39,8 @@ import com.google.android.gms.common.util.BiConsumer;
|
|
|
39
39
|
/**
|
|
40
40
|
* A native ad drawn as a strip across the top of the screen, over the WebView, like a top banner.
|
|
41
41
|
* Hiding keeps the loaded ad, so showing it again brings back the same ad without a new request.
|
|
42
|
+
* With a refresh interval, a new ad replaces it once it has been on screen that long in all, hidden time and time in
|
|
43
|
+
* the background not counted; the old one stays up until the new one comes.
|
|
42
44
|
*/
|
|
43
45
|
public class NativeAdExecutor extends Executor {
|
|
44
46
|
|
|
@@ -55,6 +57,17 @@ public class NativeAdExecutor extends Executor {
|
|
|
55
57
|
private long mLoadedAt;
|
|
56
58
|
private boolean mLoading;
|
|
57
59
|
private boolean mWantsVisible;
|
|
60
|
+
private boolean mPaused;
|
|
61
|
+
// What the next refresh asks for: the last show's ad unit and request.
|
|
62
|
+
private String mAdId;
|
|
63
|
+
private AdRequest mAdRequest;
|
|
64
|
+
private long mRefreshMs;
|
|
65
|
+
// How long the current ad has been on screen: the time before it was last hidden, and since when it shows.
|
|
66
|
+
private long mShownMs;
|
|
67
|
+
private long mShownSince = -1;
|
|
68
|
+
private final Runnable mRefresh = () -> {
|
|
69
|
+
if (mAdId != null) load(mAdId, mAdRequest);
|
|
70
|
+
};
|
|
58
71
|
// The status bar can change height while the ad is up (rotation, display size), so its margin follows it.
|
|
59
72
|
private final ViewTreeObserver.OnGlobalLayoutListener insetListener = () -> {
|
|
60
73
|
if (mAdView != null) applyTopInset();
|
|
@@ -76,6 +89,7 @@ public class NativeAdExecutor extends Executor {
|
|
|
76
89
|
public void showNativeAd(final PluginCall call) {
|
|
77
90
|
final AdOptions adOptions = AdOptions.getFactory().createGenericOptions(call, NATIVE_TESTER_ID);
|
|
78
91
|
final JSObject style = call.getObject("style", new JSObject());
|
|
92
|
+
final long refreshMs = Math.max(0, call.getInt("refreshInterval", 0)) * 1000L;
|
|
79
93
|
|
|
80
94
|
activitySupplier
|
|
81
95
|
.get()
|
|
@@ -89,8 +103,13 @@ public class NativeAdExecutor extends Executor {
|
|
|
89
103
|
final AdRequest adRequest = RequestHelper.createRequest(adOptions);
|
|
90
104
|
final String adId = AdViewIdHelper.getFinalAdId(adOptions, adRequest, logTag, contextSupplier.get());
|
|
91
105
|
|
|
106
|
+
mAdId = adId;
|
|
107
|
+
mAdRequest = adRequest;
|
|
108
|
+
mRefreshMs = refreshMs;
|
|
109
|
+
|
|
92
110
|
if (mNativeAd != null && adId.equals(mLoadedAdId) && !isExpired()) {
|
|
93
111
|
setVisible(true);
|
|
112
|
+
startCounting();
|
|
94
113
|
} else {
|
|
95
114
|
load(adId, adRequest);
|
|
96
115
|
}
|
|
@@ -107,6 +126,7 @@ public class NativeAdExecutor extends Executor {
|
|
|
107
126
|
.get()
|
|
108
127
|
.runOnUiThread(() -> {
|
|
109
128
|
mWantsVisible = false;
|
|
129
|
+
stopCounting();
|
|
110
130
|
if (mAdView != null) {
|
|
111
131
|
setVisible(false);
|
|
112
132
|
}
|
|
@@ -119,6 +139,7 @@ public class NativeAdExecutor extends Executor {
|
|
|
119
139
|
.get()
|
|
120
140
|
.runOnUiThread(() -> {
|
|
121
141
|
mWantsVisible = false;
|
|
142
|
+
stopCounting();
|
|
122
143
|
destroyNativeAd();
|
|
123
144
|
|
|
124
145
|
if (mAdView != null) {
|
|
@@ -151,15 +172,18 @@ public class NativeAdExecutor extends Executor {
|
|
|
151
172
|
return;
|
|
152
173
|
}
|
|
153
174
|
|
|
175
|
+
stopCounting();
|
|
154
176
|
destroyNativeAd();
|
|
155
177
|
mNativeAd = nativeAd;
|
|
156
178
|
mLoadedAdId = adId;
|
|
157
179
|
mLoadedAt = SystemClock.elapsedRealtime();
|
|
180
|
+
mShownMs = 0;
|
|
158
181
|
populate(nativeAd);
|
|
159
182
|
notifyListeners(NativeAdPluginEvents.Loaded.getWebEventName(), emptyObject);
|
|
160
183
|
|
|
161
184
|
if (mWantsVisible) {
|
|
162
185
|
setVisible(true);
|
|
186
|
+
startCounting();
|
|
163
187
|
}
|
|
164
188
|
})
|
|
165
189
|
.withAdListener(
|
|
@@ -167,14 +191,18 @@ public class NativeAdExecutor extends Executor {
|
|
|
167
191
|
@Override
|
|
168
192
|
public void onAdFailedToLoad(@NonNull LoadAdError adError) {
|
|
169
193
|
mLoading = false;
|
|
194
|
+
stopCounting();
|
|
170
195
|
|
|
171
|
-
// An expired ad may not be shown again; a fresh one keeps showing
|
|
196
|
+
// An expired ad may not be shown again; a fresh one keeps showing, and the next refresh is
|
|
197
|
+
// tried after another interval on screen.
|
|
172
198
|
if (mNativeAd != null && isExpired()) {
|
|
173
199
|
destroyNativeAd();
|
|
174
200
|
}
|
|
175
201
|
if (mNativeAd == null && mAdView != null) {
|
|
176
202
|
setVisible(false);
|
|
177
203
|
}
|
|
204
|
+
mShownMs = 0;
|
|
205
|
+
startCounting();
|
|
178
206
|
|
|
179
207
|
notifyListeners(NativeAdPluginEvents.FailedToLoad.getWebEventName(), new AdMobPluginError(adError));
|
|
180
208
|
}
|
|
@@ -195,6 +223,33 @@ public class NativeAdExecutor extends Executor {
|
|
|
195
223
|
adLoader.loadAd(adRequest);
|
|
196
224
|
}
|
|
197
225
|
|
|
226
|
+
// The app went to the background, or came back: the ad is not on screen meanwhile.
|
|
227
|
+
public void onPause() {
|
|
228
|
+
mPaused = true;
|
|
229
|
+
stopCounting();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
public void onResume() {
|
|
233
|
+
mPaused = false;
|
|
234
|
+
startCounting();
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Counts the time the ad is on screen and asks for a new one when it has been there for the refresh interval.
|
|
238
|
+
private void startCounting() {
|
|
239
|
+
if (mShownSince >= 0 || mRefreshMs <= 0 || mNativeAd == null || mAdView == null || !mWantsVisible || mPaused) return;
|
|
240
|
+
|
|
241
|
+
mShownSince = SystemClock.elapsedRealtime();
|
|
242
|
+
mAdView.postDelayed(mRefresh, Math.max(0, mRefreshMs - mShownMs));
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
private void stopCounting() {
|
|
246
|
+
if (mAdView != null) mAdView.removeCallbacks(mRefresh);
|
|
247
|
+
if (mShownSince < 0) return;
|
|
248
|
+
|
|
249
|
+
mShownMs += SystemClock.elapsedRealtime() - mShownSince;
|
|
250
|
+
mShownSince = -1;
|
|
251
|
+
}
|
|
252
|
+
|
|
198
253
|
private void ensureAdView() {
|
|
199
254
|
if (mAdView != null) return;
|
|
200
255
|
|
package/dist/docs.json
CHANGED
|
@@ -2053,6 +2053,18 @@
|
|
|
2053
2053
|
],
|
|
2054
2054
|
"type": "NativeAdStyle"
|
|
2055
2055
|
},
|
|
2056
|
+
{
|
|
2057
|
+
"name": "refreshInterval",
|
|
2058
|
+
"tags": [
|
|
2059
|
+
{
|
|
2060
|
+
"text": "8.2.0",
|
|
2061
|
+
"name": "since"
|
|
2062
|
+
}
|
|
2063
|
+
],
|
|
2064
|
+
"docs": "Seconds the ad stays on screen before a new one replaces it. Only the time it is shown counts: hidden, or with\nthe app in the background, it waits. The old ad stays up until the new one loads. 0, the default, keeps it until\nit expires after an hour.",
|
|
2065
|
+
"complexTypes": [],
|
|
2066
|
+
"type": "number | undefined"
|
|
2067
|
+
},
|
|
2056
2068
|
{
|
|
2057
2069
|
"name": "adId",
|
|
2058
2070
|
"tags": [
|
|
@@ -6,6 +6,14 @@ export interface NativeAdOptions extends AdOptions {
|
|
|
6
6
|
* @since 8.1.0
|
|
7
7
|
*/
|
|
8
8
|
style?: NativeAdStyle;
|
|
9
|
+
/**
|
|
10
|
+
* Seconds the ad stays on screen before a new one replaces it. Only the time it is shown counts: hidden, or with
|
|
11
|
+
* the app in the background, it waits. The old ad stays up until the new one loads. 0, the default, keeps it until
|
|
12
|
+
* it expires after an hour.
|
|
13
|
+
*
|
|
14
|
+
* @since 8.2.0
|
|
15
|
+
*/
|
|
16
|
+
refreshInterval?: number;
|
|
9
17
|
}
|
|
10
18
|
export interface NativeAdStyle {
|
|
11
19
|
/**
|
|
@@ -5,6 +5,8 @@ import GoogleMobileAds
|
|
|
5
5
|
/**
|
|
6
6
|
* A native ad drawn as a strip across the top of the screen, below the status bar, over the WebView like a top banner.
|
|
7
7
|
* Hiding keeps the loaded ad, so showing it again brings back the same ad without a new request.
|
|
8
|
+
* With a refresh interval, a new ad replaces it once it has been on screen that long in all, hidden time and time in
|
|
9
|
+
* the background not counted; the old one stays up until the new one comes.
|
|
8
10
|
*/
|
|
9
11
|
class NativeAdExecutor: NSObject, NativeAdLoaderDelegate, NativeAdDelegate {
|
|
10
12
|
weak var plugin: AdMobPlugin?
|
|
@@ -18,6 +20,16 @@ class NativeAdExecutor: NSObject, NativeAdLoaderDelegate, NativeAdDelegate {
|
|
|
18
20
|
private var loadedAdUnitID: String?
|
|
19
21
|
private var loadedAt = Date.distantPast
|
|
20
22
|
private var wantsVisible = false
|
|
23
|
+
private var inBackground = false
|
|
24
|
+
// What the next refresh asks for: the last show's ad unit and request.
|
|
25
|
+
private var adUnitID: String?
|
|
26
|
+
private var request: Request?
|
|
27
|
+
private var refreshInterval: TimeInterval = 0
|
|
28
|
+
// How long the current ad has been on screen: the time before it was last hidden, and since when it shows.
|
|
29
|
+
private var shownTime: TimeInterval = 0
|
|
30
|
+
private var shownSince: Date?
|
|
31
|
+
private var refreshTimer: Timer?
|
|
32
|
+
private var observers: [NSObjectProtocol] = []
|
|
21
33
|
|
|
22
34
|
private let iconView = UIImageView()
|
|
23
35
|
private let badgeLabel = PaddedLabel()
|
|
@@ -35,10 +47,15 @@ class NativeAdExecutor: NSObject, NativeAdLoaderDelegate, NativeAdDelegate {
|
|
|
35
47
|
|
|
36
48
|
ensureAdView(in: rootViewController.view)
|
|
37
49
|
applyStyle(call.getObject("style") ?? [:])
|
|
50
|
+
observeApp()
|
|
38
51
|
wantsVisible = true
|
|
52
|
+
self.adUnitID = adUnitID
|
|
53
|
+
self.request = request
|
|
54
|
+
refreshInterval = TimeInterval(max(0, call.getInt("refreshInterval") ?? 0))
|
|
39
55
|
|
|
40
56
|
if nativeAd != nil && loadedAdUnitID == adUnitID && !isExpired() {
|
|
41
57
|
setVisible(true)
|
|
58
|
+
startCounting()
|
|
42
59
|
} else {
|
|
43
60
|
load(adUnitID, request, rootViewController)
|
|
44
61
|
}
|
|
@@ -48,6 +65,7 @@ class NativeAdExecutor: NSObject, NativeAdLoaderDelegate, NativeAdDelegate {
|
|
|
48
65
|
|
|
49
66
|
func hideNativeAd(_ call: CAPPluginCall) {
|
|
50
67
|
wantsVisible = false
|
|
68
|
+
stopCounting()
|
|
51
69
|
if adView != nil {
|
|
52
70
|
setVisible(false)
|
|
53
71
|
}
|
|
@@ -56,6 +74,7 @@ class NativeAdExecutor: NSObject, NativeAdLoaderDelegate, NativeAdDelegate {
|
|
|
56
74
|
|
|
57
75
|
func removeNativeAd(_ call: CAPPluginCall) {
|
|
58
76
|
wantsVisible = false
|
|
77
|
+
stopCounting()
|
|
59
78
|
nativeAd = nil
|
|
60
79
|
loadedAdUnitID = nil
|
|
61
80
|
|
|
@@ -87,23 +106,28 @@ class NativeAdExecutor: NSObject, NativeAdLoaderDelegate, NativeAdDelegate {
|
|
|
87
106
|
// Removed while it loaded.
|
|
88
107
|
guard let adView = adView else { return }
|
|
89
108
|
|
|
109
|
+
stopCounting()
|
|
90
110
|
nativeAd.delegate = self
|
|
91
111
|
self.nativeAd = nativeAd
|
|
92
112
|
loadedAdUnitID = adLoader.adUnitID
|
|
93
113
|
loadedAt = Date()
|
|
114
|
+
shownTime = 0
|
|
94
115
|
populate(adView, nativeAd)
|
|
95
116
|
plugin?.notifyListeners(NativeAdPluginEvents.Loaded.rawValue, data: [:])
|
|
96
117
|
|
|
97
118
|
if wantsVisible {
|
|
98
119
|
setVisible(true)
|
|
120
|
+
startCounting()
|
|
99
121
|
}
|
|
100
122
|
}
|
|
101
123
|
|
|
102
124
|
func adLoader(_ adLoader: AdLoader, didFailToReceiveAdWithError error: Error) {
|
|
103
125
|
self.adLoader = nil
|
|
104
126
|
NSLog("AdMob: native ad failed to load: \(error.localizedDescription)")
|
|
127
|
+
stopCounting()
|
|
105
128
|
|
|
106
|
-
// An expired ad may not be shown again; a fresh one keeps showing
|
|
129
|
+
// An expired ad may not be shown again; a fresh one keeps showing, and the next refresh is tried after another
|
|
130
|
+
// interval on screen.
|
|
107
131
|
if nativeAd != nil && isExpired() {
|
|
108
132
|
nativeAd = nil
|
|
109
133
|
loadedAdUnitID = nil
|
|
@@ -111,6 +135,8 @@ class NativeAdExecutor: NSObject, NativeAdLoaderDelegate, NativeAdDelegate {
|
|
|
111
135
|
if nativeAd == nil && adView != nil {
|
|
112
136
|
setVisible(false)
|
|
113
137
|
}
|
|
138
|
+
shownTime = 0
|
|
139
|
+
startCounting()
|
|
114
140
|
|
|
115
141
|
plugin?.notifyListeners(NativeAdPluginEvents.FailedToLoad.rawValue, data: [
|
|
116
142
|
"code": (error as NSError).code,
|
|
@@ -126,6 +152,45 @@ class NativeAdExecutor: NSObject, NativeAdLoaderDelegate, NativeAdDelegate {
|
|
|
126
152
|
plugin?.notifyListeners(NativeAdPluginEvents.AdImpression.rawValue, data: [:])
|
|
127
153
|
}
|
|
128
154
|
|
|
155
|
+
// Counts the time the ad is on screen and asks for a new one when it has been there for the refresh interval.
|
|
156
|
+
private func startCounting() {
|
|
157
|
+
if shownSince != nil || refreshInterval <= 0 || nativeAd == nil || !wantsVisible || inBackground { return }
|
|
158
|
+
|
|
159
|
+
shownSince = Date()
|
|
160
|
+
refreshTimer = Timer.scheduledTimer(withTimeInterval: max(0, refreshInterval - shownTime), repeats: false) { [weak self] _ in
|
|
161
|
+
guard let self = self, let adUnitID = self.adUnitID, let request = self.request,
|
|
162
|
+
let rootViewController = self.plugin?.getRootVC() else { return }
|
|
163
|
+
|
|
164
|
+
self.load(adUnitID, request, rootViewController)
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
private func stopCounting() {
|
|
169
|
+
refreshTimer?.invalidate()
|
|
170
|
+
refreshTimer = nil
|
|
171
|
+
guard let shownSince = shownSince else { return }
|
|
172
|
+
|
|
173
|
+
shownTime += Date().timeIntervalSince(shownSince)
|
|
174
|
+
self.shownSince = nil
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// The app went to the background, or came back: the ad is not on screen meanwhile.
|
|
178
|
+
private func observeApp() {
|
|
179
|
+
if !observers.isEmpty { return }
|
|
180
|
+
|
|
181
|
+
let center = NotificationCenter.default
|
|
182
|
+
observers = [
|
|
183
|
+
center.addObserver(forName: UIApplication.didEnterBackgroundNotification, object: nil, queue: .main) { [weak self] _ in
|
|
184
|
+
self?.inBackground = true
|
|
185
|
+
self?.stopCounting()
|
|
186
|
+
},
|
|
187
|
+
center.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [weak self] _ in
|
|
188
|
+
self?.inBackground = false
|
|
189
|
+
self?.startCounting()
|
|
190
|
+
}
|
|
191
|
+
]
|
|
192
|
+
}
|
|
193
|
+
|
|
129
194
|
// Icon, "Ad" badge, headline and body, the call to action and AdChoices in the top right corner; a hairline along
|
|
130
195
|
// the bottom separates it from the screen below. The wider trailing padding keeps the button clear of AdChoices.
|
|
131
196
|
private func ensureAdView(in parent: UIView) {
|