@ququplay/admob 8.0.8 → 8.1.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/Package.swift +1 -1
- package/QuquplayAdmob.podspec +1 -1
- package/README.md +178 -0
- package/android/build.gradle +1 -1
- package/android/src/main/java/com/getcapacitor/community/admob/AdMob.java +26 -0
- package/android/src/main/java/com/getcapacitor/community/admob/nativead/NativeAdExecutor.java +336 -0
- package/android/src/main/java/com/getcapacitor/community/admob/nativead/NativeAdPluginEvents.kt +9 -0
- package/android/src/main/res/layout/admob_native_ad_strip.xml +112 -0
- package/android/src/main/res/values/strings.xml +4 -0
- package/android/src/main/res/values-pl/strings.xml +4 -0
- package/dist/docs.json +483 -0
- package/dist/esm/definitions.d.ts +2 -1
- package/dist/esm/definitions.js.map +1 -1
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/native-ad/index.d.ts +3 -0
- package/dist/esm/native-ad/index.js +4 -0
- package/dist/esm/native-ad/index.js.map +1 -0
- package/dist/esm/native-ad/native-ad-definitions.interface.d.ts +68 -0
- package/dist/esm/native-ad/native-ad-definitions.interface.js +2 -0
- package/dist/esm/native-ad/native-ad-definitions.interface.js.map +1 -0
- package/dist/esm/native-ad/native-ad-options.interface.d.ts +35 -0
- package/dist/esm/native-ad/native-ad-options.interface.js +2 -0
- package/dist/esm/native-ad/native-ad-options.interface.js.map +1 -0
- package/dist/esm/native-ad/native-ad-plugin-events.enum.d.ts +10 -0
- package/dist/esm/native-ad/native-ad-plugin-events.enum.js +13 -0
- package/dist/esm/native-ad/native-ad-plugin-events.enum.js.map +1 -0
- package/dist/esm/web.d.ts +4 -0
- package/dist/esm/web.js +9 -0
- package/dist/esm/web.js.map +1 -1
- package/dist/plugin.cjs.js +22 -0
- package/dist/plugin.cjs.js.map +1 -1
- package/dist/plugin.js +22 -0
- package/dist/plugin.js.map +1 -1
- package/ios/Sources/AdMobPlugin/AdMobPlugin.swift +30 -0
- package/ios/Sources/AdMobPlugin/NativeAd/NativeAdExecutor.swift +306 -0
- package/ios/Sources/AdMobPlugin/NativeAd/NativeAdPluginEvents.swift +7 -0
- package/package.json +1 -1
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", .upToNextMinor(from: "13.
|
|
14
|
+
.package(url: "https://github.com/googleads/swift-package-manager-google-mobile-ads.git", .upToNextMinor(from: "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/QuquplayAdmob.podspec
CHANGED
|
@@ -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.
|
|
18
|
+
s.dependency 'Google-Mobile-Ads-SDK', '~> 13.11'
|
|
19
19
|
s.dependency 'GoogleUserMessagingPlatform', '~> 3.1'
|
|
20
20
|
end
|
package/README.md
CHANGED
|
@@ -355,6 +355,14 @@ AdMob.addListener(RewardAdPluginEvents.Rewarded, async () => {
|
|
|
355
355
|
* [`addListener(InterstitialAdPluginEvents.Dismissed, ...)`](#addlistenerinterstitialadplugineventsdismissed-)
|
|
356
356
|
* [`addListener(InterstitialAdPluginEvents.FailedToShow, ...)`](#addlistenerinterstitialadplugineventsfailedtoshow-)
|
|
357
357
|
* [`addListener(InterstitialAdPluginEvents.Showed, ...)`](#addlistenerinterstitialadplugineventsshowed-)
|
|
358
|
+
* [`showNativeAd(...)`](#shownativead)
|
|
359
|
+
* [`hideNativeAd()`](#hidenativead)
|
|
360
|
+
* [`removeNativeAd()`](#removenativead)
|
|
361
|
+
* [`addListener(NativeAdPluginEvents.SizeChanged, ...)`](#addlistenernativeadplugineventssizechanged-)
|
|
362
|
+
* [`addListener(NativeAdPluginEvents.Loaded, ...)`](#addlistenernativeadplugineventsloaded-)
|
|
363
|
+
* [`addListener(NativeAdPluginEvents.FailedToLoad, ...)`](#addlistenernativeadplugineventsfailedtoload-)
|
|
364
|
+
* [`addListener(NativeAdPluginEvents.Clicked, ...)`](#addlistenernativeadplugineventsclicked-)
|
|
365
|
+
* [`addListener(NativeAdPluginEvents.AdImpression, ...)`](#addlistenernativeadplugineventsadimpression-)
|
|
358
366
|
* [`prepareRewardVideoAd(...)`](#preparerewardvideoad)
|
|
359
367
|
* [`showRewardVideoAd()`](#showrewardvideoad)
|
|
360
368
|
* [`addListener(RewardAdPluginEvents.FailedToLoad, ...)`](#addlistenerrewardadplugineventsfailedtoload-)
|
|
@@ -807,6 +815,141 @@ addListener(eventName: InterstitialAdPluginEvents.Showed, listenerFunc: () => vo
|
|
|
807
815
|
--------------------
|
|
808
816
|
|
|
809
817
|
|
|
818
|
+
### showNativeAd(...)
|
|
819
|
+
|
|
820
|
+
```typescript
|
|
821
|
+
showNativeAd(options: NativeAdOptions) => Promise<void>
|
|
822
|
+
```
|
|
823
|
+
|
|
824
|
+
Show a native ad as a strip across the top of the screen.
|
|
825
|
+
Shows the ad loaded before when there is one and it has not expired, otherwise loads a new one.
|
|
826
|
+
Android and iOS; resolves without showing anything on the web.
|
|
827
|
+
|
|
828
|
+
| Param | Type | Description |
|
|
829
|
+
| ------------- | ----------------------------------------------------------- | ---------------------------------------------- |
|
|
830
|
+
| **`options`** | <code><a href="#nativeadoptions">NativeAdOptions</a></code> | <a href="#nativeadoptions">NativeAdOptions</a> |
|
|
831
|
+
|
|
832
|
+
**Since:** 8.1.0
|
|
833
|
+
|
|
834
|
+
--------------------
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
### hideNativeAd()
|
|
838
|
+
|
|
839
|
+
```typescript
|
|
840
|
+
hideNativeAd() => Promise<void>
|
|
841
|
+
```
|
|
842
|
+
|
|
843
|
+
Hide the native ad but keep it, so showNativeAd brings back the same ad.
|
|
844
|
+
|
|
845
|
+
**Since:** 8.1.0
|
|
846
|
+
|
|
847
|
+
--------------------
|
|
848
|
+
|
|
849
|
+
|
|
850
|
+
### removeNativeAd()
|
|
851
|
+
|
|
852
|
+
```typescript
|
|
853
|
+
removeNativeAd() => Promise<void>
|
|
854
|
+
```
|
|
855
|
+
|
|
856
|
+
Destroy the native ad, remove it from screen.
|
|
857
|
+
|
|
858
|
+
**Since:** 8.1.0
|
|
859
|
+
|
|
860
|
+
--------------------
|
|
861
|
+
|
|
862
|
+
|
|
863
|
+
### addListener(NativeAdPluginEvents.SizeChanged, ...)
|
|
864
|
+
|
|
865
|
+
```typescript
|
|
866
|
+
addListener(eventName: NativeAdPluginEvents.SizeChanged, listenerFunc: (info: AdMobBannerSize) => void) => Promise<PluginListenerHandle>
|
|
867
|
+
```
|
|
868
|
+
|
|
869
|
+
| Param | Type | Description |
|
|
870
|
+
| ------------------ | --------------------------------------------------------------------------------- | ------------------- |
|
|
871
|
+
| **`eventName`** | <code><a href="#nativeadpluginevents">NativeAdPluginEvents.SizeChanged</a></code> | nativeAdSizeChanged |
|
|
872
|
+
| **`listenerFunc`** | <code>(info: <a href="#admobbannersize">AdMobBannerSize</a>) => void</code> | |
|
|
873
|
+
|
|
874
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
875
|
+
|
|
876
|
+
**Since:** 8.1.0
|
|
877
|
+
|
|
878
|
+
--------------------
|
|
879
|
+
|
|
880
|
+
|
|
881
|
+
### addListener(NativeAdPluginEvents.Loaded, ...)
|
|
882
|
+
|
|
883
|
+
```typescript
|
|
884
|
+
addListener(eventName: NativeAdPluginEvents.Loaded, listenerFunc: () => void) => Promise<PluginListenerHandle>
|
|
885
|
+
```
|
|
886
|
+
|
|
887
|
+
| Param | Type | Description |
|
|
888
|
+
| ------------------ | ---------------------------------------------------------------------------- | -------------- |
|
|
889
|
+
| **`eventName`** | <code><a href="#nativeadpluginevents">NativeAdPluginEvents.Loaded</a></code> | nativeAdLoaded |
|
|
890
|
+
| **`listenerFunc`** | <code>() => void</code> | |
|
|
891
|
+
|
|
892
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
893
|
+
|
|
894
|
+
**Since:** 8.1.0
|
|
895
|
+
|
|
896
|
+
--------------------
|
|
897
|
+
|
|
898
|
+
|
|
899
|
+
### addListener(NativeAdPluginEvents.FailedToLoad, ...)
|
|
900
|
+
|
|
901
|
+
```typescript
|
|
902
|
+
addListener(eventName: NativeAdPluginEvents.FailedToLoad, listenerFunc: (info: AdMobError) => void) => Promise<PluginListenerHandle>
|
|
903
|
+
```
|
|
904
|
+
|
|
905
|
+
| Param | Type | Description |
|
|
906
|
+
| ------------------ | ---------------------------------------------------------------------------------- | -------------------- |
|
|
907
|
+
| **`eventName`** | <code><a href="#nativeadpluginevents">NativeAdPluginEvents.FailedToLoad</a></code> | nativeAdFailedToLoad |
|
|
908
|
+
| **`listenerFunc`** | <code>(info: <a href="#admoberror">AdMobError</a>) => void</code> | |
|
|
909
|
+
|
|
910
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
911
|
+
|
|
912
|
+
**Since:** 8.1.0
|
|
913
|
+
|
|
914
|
+
--------------------
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
### addListener(NativeAdPluginEvents.Clicked, ...)
|
|
918
|
+
|
|
919
|
+
```typescript
|
|
920
|
+
addListener(eventName: NativeAdPluginEvents.Clicked, listenerFunc: () => void) => Promise<PluginListenerHandle>
|
|
921
|
+
```
|
|
922
|
+
|
|
923
|
+
| Param | Type | Description |
|
|
924
|
+
| ------------------ | ----------------------------------------------------------------------------- | --------------- |
|
|
925
|
+
| **`eventName`** | <code><a href="#nativeadpluginevents">NativeAdPluginEvents.Clicked</a></code> | nativeAdClicked |
|
|
926
|
+
| **`listenerFunc`** | <code>() => void</code> | |
|
|
927
|
+
|
|
928
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
929
|
+
|
|
930
|
+
**Since:** 8.1.0
|
|
931
|
+
|
|
932
|
+
--------------------
|
|
933
|
+
|
|
934
|
+
|
|
935
|
+
### addListener(NativeAdPluginEvents.AdImpression, ...)
|
|
936
|
+
|
|
937
|
+
```typescript
|
|
938
|
+
addListener(eventName: NativeAdPluginEvents.AdImpression, listenerFunc: () => void) => Promise<PluginListenerHandle>
|
|
939
|
+
```
|
|
940
|
+
|
|
941
|
+
| Param | Type | Description |
|
|
942
|
+
| ------------------ | ---------------------------------------------------------------------------------- | ------------------ |
|
|
943
|
+
| **`eventName`** | <code><a href="#nativeadpluginevents">NativeAdPluginEvents.AdImpression</a></code> | nativeAdImpression |
|
|
944
|
+
| **`listenerFunc`** | <code>() => void</code> | |
|
|
945
|
+
|
|
946
|
+
**Returns:** <code>Promise<<a href="#pluginlistenerhandle">PluginListenerHandle</a>></code>
|
|
947
|
+
|
|
948
|
+
**Since:** 8.1.0
|
|
949
|
+
|
|
950
|
+
--------------------
|
|
951
|
+
|
|
952
|
+
|
|
810
953
|
### prepareRewardVideoAd(...)
|
|
811
954
|
|
|
812
955
|
```typescript
|
|
@@ -1182,6 +1325,30 @@ https://developers.google.com/android/reference/com/google/android/gms/ads/AdErr
|
|
|
1182
1325
|
| **`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 |
|
|
1183
1326
|
|
|
1184
1327
|
|
|
1328
|
+
#### NativeAdOptions
|
|
1329
|
+
|
|
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
|
+
| **`adId`** | <code>string</code> | The ad unit ID that you want to request | | 1.1.2 |
|
|
1334
|
+
| **`isTesting`** | <code>boolean</code> | You can use test mode of ad. | <code>false</code> | 1.1.2 |
|
|
1335
|
+
| **`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 |
|
|
1336
|
+
| **`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 |
|
|
1337
|
+
| **`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
|
+
|
|
1340
|
+
#### NativeAdStyle
|
|
1341
|
+
|
|
1342
|
+
| Prop | Type | Description |
|
|
1343
|
+
| ------------------------ | ------------------- | ------------------------------------------------------------- |
|
|
1344
|
+
| **`backgroundColor`** | <code>string</code> | Background of the strip. Defaults to white. |
|
|
1345
|
+
| **`textColor`** | <code>string</code> | Headline text. Defaults to black. |
|
|
1346
|
+
| **`secondaryTextColor`** | <code>string</code> | Body text. Defaults to textColor. |
|
|
1347
|
+
| **`buttonColor`** | <code>string</code> | Call-to-action button and "Ad" badge background. |
|
|
1348
|
+
| **`buttonTextColor`** | <code>string</code> | Call-to-action button and "Ad" badge text. Defaults to white. |
|
|
1349
|
+
| **`dividerColor`** | <code>string</code> | Hairline along the bottom of the strip. Defaults to none. |
|
|
1350
|
+
|
|
1351
|
+
|
|
1185
1352
|
#### RewardAdOptions
|
|
1186
1353
|
|
|
1187
1354
|
| Prop | Type | Description | Default | Since |
|
|
@@ -1331,6 +1498,17 @@ From T, pick a set of properties whose keys are in the union K
|
|
|
1331
1498
|
| **`Dismissed`** | <code>'interstitialAdDismissed'</code> | Emits when the Interstitial ad is not visible to the user anymore. |
|
|
1332
1499
|
|
|
1333
1500
|
|
|
1501
|
+
#### NativeAdPluginEvents
|
|
1502
|
+
|
|
1503
|
+
| Members | Value | Description |
|
|
1504
|
+
| ------------------ | ----------------------------------- | ------------------------------------------------------- |
|
|
1505
|
+
| **`SizeChanged`** | <code>'nativeAdSizeChanged'</code> | The strip's size in dp; 0 when it is hidden or removed. |
|
|
1506
|
+
| **`Loaded`** | <code>'nativeAdLoaded'</code> | |
|
|
1507
|
+
| **`FailedToLoad`** | <code>'nativeAdFailedToLoad'</code> | |
|
|
1508
|
+
| **`Clicked`** | <code>'nativeAdClicked'</code> | |
|
|
1509
|
+
| **`AdImpression`** | <code>'nativeAdImpression'</code> | |
|
|
1510
|
+
|
|
1511
|
+
|
|
1334
1512
|
#### RewardAdPluginEvents
|
|
1335
1513
|
|
|
1336
1514
|
| Members | Value | Description |
|
package/android/build.gradle
CHANGED
|
@@ -4,7 +4,7 @@ ext {
|
|
|
4
4
|
androidxCoordinatorLayoutVersion = project.hasProperty('androidxCoordinatorLayoutVersion') ? rootProject.ext.androidxCoordinatorLayoutVersion : '1.3.0'
|
|
5
5
|
androidxJunitVersion = project.hasProperty('androidxJunitVersion') ? rootProject.ext.androidxJunitVersion : '1.3.0'
|
|
6
6
|
androidxEspressoCoreVersion = project.hasProperty('androidxEspressoCoreVersion') ? rootProject.ext.androidxEspressoCoreVersion : '3.7.0'
|
|
7
|
-
playServicesAdsVersion = project.hasProperty('playServicesAdsVersion') ? rootProject.ext.playServicesAdsVersion : '25.
|
|
7
|
+
playServicesAdsVersion = project.hasProperty('playServicesAdsVersion') ? rootProject.ext.playServicesAdsVersion : '25.5.0'
|
|
8
8
|
userMessagingPlatformVersion = project.hasProperty('userMessagingPlatformVersion') ? rootProject.ext.userMessagingPlatformVersion : '4.0.0'
|
|
9
9
|
androidxCoreKTXVersion = project.hasProperty('androidxCoreKTXVersion') ? rootProject.ext.androidxCoreKTXVersion : '1.15.0'
|
|
10
10
|
}
|
|
@@ -13,6 +13,7 @@ import com.getcapacitor.community.admob.consent.AdConsentExecutor;
|
|
|
13
13
|
import com.getcapacitor.community.admob.helpers.AuthorizationStatusEnum;
|
|
14
14
|
import com.getcapacitor.community.admob.interstitial.AdInterstitialExecutor;
|
|
15
15
|
import com.getcapacitor.community.admob.interstitial.InterstitialAdCallbackAndListeners;
|
|
16
|
+
import com.getcapacitor.community.admob.nativead.NativeAdExecutor;
|
|
16
17
|
import com.getcapacitor.community.admob.rewarded.AdRewardExecutor;
|
|
17
18
|
import com.getcapacitor.community.admob.rewardedinterstitial.AdRewardInterstitialExecutor;
|
|
18
19
|
import com.google.android.gms.ads.MobileAds;
|
|
@@ -34,6 +35,12 @@ public class AdMob extends Plugin {
|
|
|
34
35
|
this::notifyListeners,
|
|
35
36
|
getLogTag()
|
|
36
37
|
);
|
|
38
|
+
private final NativeAdExecutor nativeAdExecutor = new NativeAdExecutor(
|
|
39
|
+
this::getContext,
|
|
40
|
+
this::getActivity,
|
|
41
|
+
this::notifyListeners,
|
|
42
|
+
getLogTag()
|
|
43
|
+
);
|
|
37
44
|
private final AdRewardExecutor adRewardExecutor = new AdRewardExecutor(
|
|
38
45
|
this::getContext,
|
|
39
46
|
this::getActivity,
|
|
@@ -75,6 +82,7 @@ public class AdMob extends Plugin {
|
|
|
75
82
|
}
|
|
76
83
|
);
|
|
77
84
|
bannerExecutor.initialize();
|
|
85
|
+
nativeAdExecutor.initialize();
|
|
78
86
|
call.resolve();
|
|
79
87
|
} catch (Exception ex) {
|
|
80
88
|
call.reject(ex.getLocalizedMessage(), ex);
|
|
@@ -160,6 +168,24 @@ public class AdMob extends Plugin {
|
|
|
160
168
|
bannerExecutor.removeBanner(call);
|
|
161
169
|
}
|
|
162
170
|
|
|
171
|
+
// Show a native ad across the top of the screen, loading one when there is none or it expired
|
|
172
|
+
@PluginMethod
|
|
173
|
+
public void showNativeAd(final PluginCall call) {
|
|
174
|
+
nativeAdExecutor.showNativeAd(call);
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Hide the native ad but keep it, so showing it again brings back the same ad
|
|
178
|
+
@PluginMethod
|
|
179
|
+
public void hideNativeAd(final PluginCall call) {
|
|
180
|
+
nativeAdExecutor.hideNativeAd(call);
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Destroy the native ad, remove it from screen.
|
|
184
|
+
@PluginMethod
|
|
185
|
+
public void removeNativeAd(final PluginCall call) {
|
|
186
|
+
nativeAdExecutor.removeNativeAd(call);
|
|
187
|
+
}
|
|
188
|
+
|
|
163
189
|
@PluginMethod
|
|
164
190
|
public void prepareInterstitial(final PluginCall call) {
|
|
165
191
|
adInterstitialExecutor.prepareInterstitial(call, this::notifyListeners);
|
|
@@ -0,0 +1,336 @@
|
|
|
1
|
+
package com.getcapacitor.community.admob.nativead;
|
|
2
|
+
|
|
3
|
+
import android.app.Activity;
|
|
4
|
+
import android.content.Context;
|
|
5
|
+
import android.graphics.Color;
|
|
6
|
+
import android.graphics.drawable.GradientDrawable;
|
|
7
|
+
import android.os.Build;
|
|
8
|
+
import android.os.SystemClock;
|
|
9
|
+
import android.util.Log;
|
|
10
|
+
import android.view.Gravity;
|
|
11
|
+
import android.view.LayoutInflater;
|
|
12
|
+
import android.view.View;
|
|
13
|
+
import android.view.ViewGroup;
|
|
14
|
+
import android.view.ViewTreeObserver;
|
|
15
|
+
import android.widget.Button;
|
|
16
|
+
import android.widget.ImageView;
|
|
17
|
+
import android.widget.TextView;
|
|
18
|
+
import androidx.annotation.NonNull;
|
|
19
|
+
import androidx.coordinatorlayout.widget.CoordinatorLayout;
|
|
20
|
+
import androidx.core.util.Supplier;
|
|
21
|
+
import androidx.core.view.ViewCompat;
|
|
22
|
+
import androidx.core.view.WindowInsetsCompat;
|
|
23
|
+
import com.getcapacitor.JSObject;
|
|
24
|
+
import com.getcapacitor.PluginCall;
|
|
25
|
+
import com.getcapacitor.community.admob.admob.R;
|
|
26
|
+
import com.getcapacitor.community.admob.helpers.AdViewIdHelper;
|
|
27
|
+
import com.getcapacitor.community.admob.helpers.RequestHelper;
|
|
28
|
+
import com.getcapacitor.community.admob.models.AdMobPluginError;
|
|
29
|
+
import com.getcapacitor.community.admob.models.AdOptions;
|
|
30
|
+
import com.getcapacitor.community.admob.models.Executor;
|
|
31
|
+
import com.google.android.gms.ads.AdListener;
|
|
32
|
+
import com.google.android.gms.ads.AdLoader;
|
|
33
|
+
import com.google.android.gms.ads.AdRequest;
|
|
34
|
+
import com.google.android.gms.ads.LoadAdError;
|
|
35
|
+
import com.google.android.gms.ads.nativead.NativeAd;
|
|
36
|
+
import com.google.android.gms.ads.nativead.NativeAdView;
|
|
37
|
+
import com.google.android.gms.common.util.BiConsumer;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* A native ad drawn as a strip across the top of the screen, over the WebView, like a top banner.
|
|
41
|
+
* Hiding keeps the loaded ad, so showing it again brings back the same ad without a new request.
|
|
42
|
+
*/
|
|
43
|
+
public class NativeAdExecutor extends Executor {
|
|
44
|
+
|
|
45
|
+
public static final String NATIVE_TESTER_ID = "ca-app-pub-3940256099942544/2247696110";
|
|
46
|
+
|
|
47
|
+
// A loaded native ad may be shown for an hour; a new one is loaded a little before that.
|
|
48
|
+
private static final long MAX_AD_AGE_MS = 55 * 60 * 1000;
|
|
49
|
+
|
|
50
|
+
private final JSObject emptyObject = new JSObject();
|
|
51
|
+
private ViewGroup mViewGroup;
|
|
52
|
+
private NativeAdView mAdView;
|
|
53
|
+
private NativeAd mNativeAd;
|
|
54
|
+
private String mLoadedAdId;
|
|
55
|
+
private long mLoadedAt;
|
|
56
|
+
private boolean mLoading;
|
|
57
|
+
private boolean mWantsVisible;
|
|
58
|
+
// The status bar can change height while the ad is up (rotation, display size), so its margin follows it.
|
|
59
|
+
private final ViewTreeObserver.OnGlobalLayoutListener insetListener = () -> {
|
|
60
|
+
if (mAdView != null) applyTopInset();
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
public NativeAdExecutor(
|
|
64
|
+
Supplier<Context> contextSupplier,
|
|
65
|
+
Supplier<Activity> activitySupplier,
|
|
66
|
+
BiConsumer<String, JSObject> notifyListenersFunction,
|
|
67
|
+
String pluginLogTag
|
|
68
|
+
) {
|
|
69
|
+
super(contextSupplier, activitySupplier, notifyListenersFunction, pluginLogTag, "NativeAdExecutor");
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public void initialize() {
|
|
73
|
+
mViewGroup = (ViewGroup) ((ViewGroup) activitySupplier.get().findViewById(android.R.id.content)).getChildAt(0);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
public void showNativeAd(final PluginCall call) {
|
|
77
|
+
final AdOptions adOptions = AdOptions.getFactory().createGenericOptions(call, NATIVE_TESTER_ID);
|
|
78
|
+
final JSObject style = call.getObject("style", new JSObject());
|
|
79
|
+
|
|
80
|
+
activitySupplier
|
|
81
|
+
.get()
|
|
82
|
+
.runOnUiThread(() -> {
|
|
83
|
+
try {
|
|
84
|
+
ensureAdView();
|
|
85
|
+
applyStyle(style);
|
|
86
|
+
applyTopInset();
|
|
87
|
+
mWantsVisible = true;
|
|
88
|
+
|
|
89
|
+
final AdRequest adRequest = RequestHelper.createRequest(adOptions);
|
|
90
|
+
final String adId = AdViewIdHelper.getFinalAdId(adOptions, adRequest, logTag, contextSupplier.get());
|
|
91
|
+
|
|
92
|
+
if (mNativeAd != null && adId.equals(mLoadedAdId) && !isExpired()) {
|
|
93
|
+
setVisible(true);
|
|
94
|
+
} else {
|
|
95
|
+
load(adId, adRequest);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
call.resolve();
|
|
99
|
+
} catch (Exception ex) {
|
|
100
|
+
call.reject(ex.getLocalizedMessage(), ex);
|
|
101
|
+
}
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
public void hideNativeAd(final PluginCall call) {
|
|
106
|
+
activitySupplier
|
|
107
|
+
.get()
|
|
108
|
+
.runOnUiThread(() -> {
|
|
109
|
+
mWantsVisible = false;
|
|
110
|
+
if (mAdView != null) {
|
|
111
|
+
setVisible(false);
|
|
112
|
+
}
|
|
113
|
+
call.resolve();
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
public void removeNativeAd(final PluginCall call) {
|
|
118
|
+
activitySupplier
|
|
119
|
+
.get()
|
|
120
|
+
.runOnUiThread(() -> {
|
|
121
|
+
mWantsVisible = false;
|
|
122
|
+
destroyNativeAd();
|
|
123
|
+
|
|
124
|
+
if (mAdView != null) {
|
|
125
|
+
activitySupplier.get().getWindow().getDecorView().getViewTreeObserver().removeOnGlobalLayoutListener(insetListener);
|
|
126
|
+
mAdView.destroy();
|
|
127
|
+
mViewGroup.removeView(mAdView);
|
|
128
|
+
mAdView = null;
|
|
129
|
+
notifySize(0, 0);
|
|
130
|
+
Log.d(logTag, "Native AD Removed");
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
call.resolve();
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
private void load(String adId, AdRequest adRequest) {
|
|
138
|
+
if (mLoading) return;
|
|
139
|
+
|
|
140
|
+
mLoading = true;
|
|
141
|
+
Log.d(logTag, "Native Ad ID: " + adId);
|
|
142
|
+
|
|
143
|
+
final AdLoader adLoader = new AdLoader.Builder(contextSupplier.get(), adId)
|
|
144
|
+
.forNativeAd((nativeAd) -> {
|
|
145
|
+
mLoading = false;
|
|
146
|
+
final Activity activity = activitySupplier.get();
|
|
147
|
+
|
|
148
|
+
// Removed, or the app went away, while it loaded.
|
|
149
|
+
if (mAdView == null || activity == null || activity.isDestroyed()) {
|
|
150
|
+
nativeAd.destroy();
|
|
151
|
+
return;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
destroyNativeAd();
|
|
155
|
+
mNativeAd = nativeAd;
|
|
156
|
+
mLoadedAdId = adId;
|
|
157
|
+
mLoadedAt = SystemClock.elapsedRealtime();
|
|
158
|
+
populate(nativeAd);
|
|
159
|
+
notifyListeners(NativeAdPluginEvents.Loaded.getWebEventName(), emptyObject);
|
|
160
|
+
|
|
161
|
+
if (mWantsVisible) {
|
|
162
|
+
setVisible(true);
|
|
163
|
+
}
|
|
164
|
+
})
|
|
165
|
+
.withAdListener(
|
|
166
|
+
new AdListener() {
|
|
167
|
+
@Override
|
|
168
|
+
public void onAdFailedToLoad(@NonNull LoadAdError adError) {
|
|
169
|
+
mLoading = false;
|
|
170
|
+
|
|
171
|
+
// An expired ad may not be shown again; a fresh one keeps showing.
|
|
172
|
+
if (mNativeAd != null && isExpired()) {
|
|
173
|
+
destroyNativeAd();
|
|
174
|
+
}
|
|
175
|
+
if (mNativeAd == null && mAdView != null) {
|
|
176
|
+
setVisible(false);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
notifyListeners(NativeAdPluginEvents.FailedToLoad.getWebEventName(), new AdMobPluginError(adError));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
@Override
|
|
183
|
+
public void onAdClicked() {
|
|
184
|
+
notifyListeners(NativeAdPluginEvents.Clicked.getWebEventName(), emptyObject);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
@Override
|
|
188
|
+
public void onAdImpression() {
|
|
189
|
+
notifyListeners(NativeAdPluginEvents.AdImpression.getWebEventName(), emptyObject);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
)
|
|
193
|
+
.build();
|
|
194
|
+
|
|
195
|
+
adLoader.loadAd(adRequest);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
private void ensureAdView() {
|
|
199
|
+
if (mAdView != null) return;
|
|
200
|
+
|
|
201
|
+
if (mViewGroup == null) {
|
|
202
|
+
initialize();
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
mAdView = (NativeAdView) LayoutInflater.from(activitySupplier.get()).inflate(R.layout.admob_native_ad_strip, mViewGroup, false);
|
|
206
|
+
|
|
207
|
+
final CoordinatorLayout.LayoutParams params = new CoordinatorLayout.LayoutParams(
|
|
208
|
+
ViewGroup.LayoutParams.MATCH_PARENT,
|
|
209
|
+
ViewGroup.LayoutParams.WRAP_CONTENT
|
|
210
|
+
);
|
|
211
|
+
params.gravity = Gravity.TOP;
|
|
212
|
+
mAdView.setLayoutParams(params);
|
|
213
|
+
mAdView.setVisibility(View.GONE);
|
|
214
|
+
|
|
215
|
+
mAdView.setHeadlineView(mAdView.findViewById(R.id.admob_native_ad_headline));
|
|
216
|
+
mAdView.setBodyView(mAdView.findViewById(R.id.admob_native_ad_body));
|
|
217
|
+
mAdView.setIconView(mAdView.findViewById(R.id.admob_native_ad_icon));
|
|
218
|
+
mAdView.setCallToActionView(mAdView.findViewById(R.id.admob_native_ad_call_to_action));
|
|
219
|
+
mAdView.setAdChoicesView(mAdView.findViewById(R.id.admob_native_ad_choices));
|
|
220
|
+
|
|
221
|
+
mViewGroup.addView(mAdView);
|
|
222
|
+
activitySupplier.get().getWindow().getDecorView().getViewTreeObserver().addOnGlobalLayoutListener(insetListener);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
private void populate(NativeAd nativeAd) {
|
|
226
|
+
((TextView) mAdView.getHeadlineView()).setText(nativeAd.getHeadline());
|
|
227
|
+
|
|
228
|
+
final TextView body = (TextView) mAdView.getBodyView();
|
|
229
|
+
body.setText(nativeAd.getBody());
|
|
230
|
+
body.setVisibility(nativeAd.getBody() == null ? View.GONE : View.VISIBLE);
|
|
231
|
+
|
|
232
|
+
final ImageView icon = (ImageView) mAdView.getIconView();
|
|
233
|
+
if (nativeAd.getIcon() == null) {
|
|
234
|
+
icon.setVisibility(View.GONE);
|
|
235
|
+
} else {
|
|
236
|
+
icon.setImageDrawable(nativeAd.getIcon().getDrawable());
|
|
237
|
+
icon.setVisibility(View.VISIBLE);
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
final Button callToAction = (Button) mAdView.getCallToActionView();
|
|
241
|
+
callToAction.setText(nativeAd.getCallToAction());
|
|
242
|
+
callToAction.setVisibility(nativeAd.getCallToAction() == null ? View.GONE : View.VISIBLE);
|
|
243
|
+
|
|
244
|
+
mAdView.setNativeAd(nativeAd);
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
private void applyStyle(JSObject style) {
|
|
248
|
+
final int backgroundColor = parseColor(style.getString("backgroundColor"), Color.WHITE);
|
|
249
|
+
final int textColor = parseColor(style.getString("textColor"), Color.BLACK);
|
|
250
|
+
final int secondaryTextColor = parseColor(style.getString("secondaryTextColor"), textColor);
|
|
251
|
+
final int buttonColor = parseColor(style.getString("buttonColor"), Color.rgb(26, 115, 232));
|
|
252
|
+
final int buttonTextColor = parseColor(style.getString("buttonTextColor"), Color.WHITE);
|
|
253
|
+
final int dividerColor = parseColor(style.getString("dividerColor"), Color.TRANSPARENT);
|
|
254
|
+
final float density = contextSupplier.get().getResources().getDisplayMetrics().density;
|
|
255
|
+
|
|
256
|
+
mAdView.setBackgroundColor(backgroundColor);
|
|
257
|
+
mAdView.findViewById(R.id.admob_native_ad_divider).setBackgroundColor(dividerColor);
|
|
258
|
+
((TextView) mAdView.getHeadlineView()).setTextColor(textColor);
|
|
259
|
+
((TextView) mAdView.getBodyView()).setTextColor(secondaryTextColor);
|
|
260
|
+
|
|
261
|
+
final TextView badge = mAdView.findViewById(R.id.admob_native_ad_badge);
|
|
262
|
+
badge.setBackground(rounded(buttonColor, 3 * density));
|
|
263
|
+
badge.setTextColor(buttonTextColor);
|
|
264
|
+
|
|
265
|
+
final Button callToAction = (Button) mAdView.getCallToActionView();
|
|
266
|
+
callToAction.setBackground(rounded(buttonColor, 18 * density));
|
|
267
|
+
callToAction.setTextColor(buttonTextColor);
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
// Below the status bar where the app draws under it (Android 15+), like a banner at the top.
|
|
271
|
+
private void applyTopInset() {
|
|
272
|
+
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.VANILLA_ICE_CREAM) return;
|
|
273
|
+
|
|
274
|
+
final WindowInsetsCompat insets = ViewCompat.getRootWindowInsets(activitySupplier.get().getWindow().getDecorView());
|
|
275
|
+
final int topInset = insets == null ? 0 : insets.getInsets(WindowInsetsCompat.Type.systemBars()).top;
|
|
276
|
+
final CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) mAdView.getLayoutParams();
|
|
277
|
+
|
|
278
|
+
if (params.topMargin != topInset) {
|
|
279
|
+
params.topMargin = topInset;
|
|
280
|
+
mAdView.setLayoutParams(params);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
private void setVisible(boolean visible) {
|
|
285
|
+
mAdView.setVisibility(visible ? View.VISIBLE : View.GONE);
|
|
286
|
+
|
|
287
|
+
if (!visible) {
|
|
288
|
+
notifySize(0, 0);
|
|
289
|
+
return;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
// The height is known once it is laid out.
|
|
293
|
+
mAdView.post(() -> {
|
|
294
|
+
if (mAdView == null || mAdView.getVisibility() != View.VISIBLE) return;
|
|
295
|
+
|
|
296
|
+
final float density = contextSupplier.get().getResources().getDisplayMetrics().density;
|
|
297
|
+
notifySize(Math.round(mAdView.getWidth() / density), Math.round(mAdView.getHeight() / density));
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
private void notifySize(int width, int height) {
|
|
302
|
+
final JSObject size = new JSObject();
|
|
303
|
+
size.put("width", width);
|
|
304
|
+
size.put("height", height);
|
|
305
|
+
notifyListeners(NativeAdPluginEvents.SizeChanged.getWebEventName(), size);
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
private void destroyNativeAd() {
|
|
309
|
+
if (mNativeAd == null) return;
|
|
310
|
+
|
|
311
|
+
mNativeAd.destroy();
|
|
312
|
+
mNativeAd = null;
|
|
313
|
+
mLoadedAdId = null;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
private boolean isExpired() {
|
|
317
|
+
return SystemClock.elapsedRealtime() - mLoadedAt > MAX_AD_AGE_MS;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
private static GradientDrawable rounded(int color, float radius) {
|
|
321
|
+
final GradientDrawable drawable = new GradientDrawable();
|
|
322
|
+
drawable.setColor(color);
|
|
323
|
+
drawable.setCornerRadius(radius);
|
|
324
|
+
return drawable;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
private static int parseColor(String color, int fallback) {
|
|
328
|
+
if (color == null) return fallback;
|
|
329
|
+
|
|
330
|
+
try {
|
|
331
|
+
return Color.parseColor(color);
|
|
332
|
+
} catch (IllegalArgumentException ex) {
|
|
333
|
+
return fallback;
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
}
|
package/android/src/main/java/com/getcapacitor/community/admob/nativead/NativeAdPluginEvents.kt
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
package com.getcapacitor.community.admob.nativead
|
|
2
|
+
|
|
3
|
+
enum class NativeAdPluginEvents(val webEventName: String) {
|
|
4
|
+
SizeChanged("nativeAdSizeChanged"),
|
|
5
|
+
Loaded("nativeAdLoaded"),
|
|
6
|
+
FailedToLoad("nativeAdFailedToLoad"),
|
|
7
|
+
Clicked("nativeAdClicked"),
|
|
8
|
+
AdImpression("nativeAdImpression"),
|
|
9
|
+
}
|