fix:1、删除部分不用的资源代码。2、a面修复bug

This commit is contained in:
barry
2026-04-28 18:42:00 +08:00
parent 70d45d4705
commit cc12dffbaa
417 changed files with 36736 additions and 40575 deletions
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin:applovin-sdk:13.3.1" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinSDK" version="13.3.1" />
</iosPods>
</dependencies>
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin:applovin-sdk:13.6.2" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinSDK" version="13.6.2" />
</iosPods>
</dependencies>
@@ -8,7 +8,7 @@
#import "MAUnityAdManager.h"
#define VERSION @"8.3.1"
#define VERSION @"8.6.2"
#define NSSTRING(_X) ( (_X != NULL) ? [NSString stringWithCString: _X encoding: NSStringEncodingConversionAllowLossy].al_stringByTrimmingWhitespace : nil)
@interface NSString (ALUtils)
+1 -1
View File
@@ -1,3 +1,3 @@
# AppLovin MAX Unity Plugin
To get the latest changes, see the [AppLovin MAX Unity Changelog](https://developers.applovin.com/en/max/unity/changelog).
To get the latest changes, see the [AppLovin MAX Unity Changelog](https://support.axon.ai/en/max/unity/changelog).
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: ac25046ac442f4c1895555ada8d01866
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 1235281c630214a8999b2185ceba6388
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/BigoAds/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:bigoads-adapter:4.9.1.0" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationBigoAdsAdapter" version="4.5.1.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: fba397d5cac8648fea9b0fe82e201e63
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 381fc70c9669549e88eb86eb1342ab4c
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: ef8467ffb0e4447b79a8804884a7520a
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/ByteDance/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:bytedance-adapter:6.3.0.3.0">
<repositories>
<repository>https://artifact.bytedance.com/repository/pangle</repository>
</repositories>
</androidPackage>
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationByteDanceAdapter" version="6.3.0.6.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 0828555cb1ce94702a4af6f3dce3d735
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 48b75ae264f3640c99a2815be5a86273
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a90f13141c35746f5a2996c8ad068fe9
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Chartboost/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:chartboost-adapter:9.7.0.4">
<repositories>
<repository>https://cboost.jfrog.io/artifactory/chartboost-ads/</repository>
</repositories>
</androidPackage>
<androidPackage spec="com.google.android.gms:play-services-base:16.1.0" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationChartboostAdapter" version="9.7.0.2" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 93b0a4618bd884871af0981a7867bb2f
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Chartboost/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 1c39dfd07e7ff425795e8eff06ead0b1
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 28880992a399a48b7abe95b66649d711
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Facebook/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<!-- Ensure that Resolver doesn't inadvertently pull Facebook's beta versions of the SDK by forcing a specific version.
Since FAN SDK depends on older versions of a few support and play service versions
`com.applovin.mediation:facebook-adapter:x.y.z.a` resolves to `com.applovin.mediation:facebook-adapter:+` which pulls down the beta versions of FAN SDK.
Note that forcing the adapter is enough to stop Jar Resolver from pulling the latest FAN SDK. -->
<androidPackage spec="com.applovin.mediation:facebook-adapter:[6.19.0.2]" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationFacebookAdapter" version="6.17.0.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: aea9bdf974328420db5ae118ef0d2b87
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Facebook/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 18f372df2f09440cdade47221956470d
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-11
View File
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e076e4ef7e2874ba69b108cc7a346c2a
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Fyber/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:fyber-adapter:8.3.2.0"/>
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationFyberAdapter" version="8.3.2.1"/>
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 5e123cdc08e804dffb2c40c4fbc83caf
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Fyber/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: f4430d8e4f65345669cea12967ee37d8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: e8015bd045cea462c8f39c8a05867d08
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Google/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,10 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<!-- Ensure that Resolver doesn't inadvertently pull the latest Play Services Ads' SDK that we haven't certified against. -->
<androidPackage spec="com.applovin.mediation:google-adapter:[24.6.0.0]" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationGoogleAdapter" version="12.11.0.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 053b810d3594744e38b6fd0fa378fb57
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Google/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: d170f61e92f2d42cab7a56391804f8c9
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: a141703acd55a48c2a3e6e6599f90c64
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/InMobi/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:inmobi-adapter:10.7.8.1" />
<androidPackage spec="com.squareup.picasso:picasso:2.71828" />
<androidPackage spec="com.android.support:recyclerview-v7:28.+" />
<androidPackage spec="com.android.support:customtabs:28.+" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationInMobiAdapter" version="10.7.8.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: bc66a0ef4503843ee9b1bf1b1e867367
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/InMobi/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 397d42e9f2d7345f0a0d7120144610e8
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 531d860cac61f47d19e32f526470ae43
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/IronSource/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:ironsource-adapter:8.4.0.0.1">
<repositories>
<repository>https://android-sdk.is.com/</repository>
</repositories>
</androidPackage>
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationIronSourceAdapter" version="8.4.0.0.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 19262406303f04f05b14b31b3c734d35
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/IronSource/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 58d8d614f6ef2452fba6d494d19e8dbe
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: db1de4066dc4e4290b3879b34fa87de2
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Mintegral/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,14 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:mintegral-adapter:16.8.61.1">
<repositories>
<repository>https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea</repository>
</repositories>
</androidPackage>
<androidPackage spec="androidx.recyclerview:recyclerview:1.2.1" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationMintegralAdapter" version="7.7.3.0.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 221b2a20a58a04f2cb4afb0779587206
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 0284843f4258d4c359b8cc15db28700e
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 9ecadc3649c184389b90252aa2a2b448
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Moloco/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:moloco-adapter:4.1.0.0" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationMolocoAdapter" version="3.13.0.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 0f5b2cb1209274ba18b234b42d2efe96
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Moloco/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
+1 -1
View File
@@ -1,5 +1,5 @@
fileFormatVersion: 2
guid: 802d40ecaa0fb40edb358a66208c04c3
guid: de8dd244651464d549e42e9a133cddd9
folderAsset: yes
DefaultImporter:
externalObjects: {}
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:unityads-adapter:4.12.4.0" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationUnityAdsAdapter" version="4.12.4.0" />
</iosPods>
</dependencies>
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:unityads-adapter:4.17.0.0" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationUnityAdsAdapter" version="4.17.0.0" />
</iosPods>
</dependencies>
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 22f22a7a0c1c74dfbb37786c41d6e0ed
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-11
View File
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 942a73b8f38284e55849bd9986aefee8
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Verve/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,12 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:verve-adapter:3.6.2.0"/>
<repositories>
<repository>https://verve.jfrog.io/artifactory/verve-gradle-release</repository>
</repositories>
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationVerveAdapter" version="3.6.1.0"/>
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: bce11a786f6b54963a8085520a643ce8
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Verve/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: 1db7142735ffa4e9997c7be8c38e96b7
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 77b6ea05736a9458f8ef8762ee9b64bb
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Vungle/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:vungle-adapter:7.4.1.3" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationVungleAdapter" version="7.4.2.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: 0d8ad3a6ddc4f44fab2efe607fe14f26
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Vungle/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
-8
View File
@@ -1,8 +0,0 @@
fileFormatVersion: 2
guid: b1f7767a7bccd4bffa303fb61c32dadb
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,11 +0,0 @@
fileFormatVersion: 2
guid: 277e2383262e844708fc74a6104f4985
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Yandex/Editor
folderAsset: yes
DefaultImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -1,9 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<dependencies>
<androidPackages>
<androidPackage spec="com.applovin.mediation:yandex-adapter:7.15.1.0" />
</androidPackages>
<iosPods>
<iosPod name="AppLovinMediationYandexAdapter" version="7.16.0.0" />
</iosPods>
</dependencies>
@@ -1,10 +0,0 @@
fileFormatVersion: 2
guid: a77aaf886990c4e6ba469a4f083b9284
labels:
- al_max
- al_max_export_path-MaxSdk/Mediation/Yandex/Editor/Dependencies.xml
TextScriptImporter:
externalObjects: {}
userData:
assetBundleName:
assetBundleVariant:
@@ -13,7 +13,7 @@ MonoBehaviour:
m_Name: AppLovinSettings
m_EditorClassIdentifier:
qualityServiceEnabled: 1
sdkKey: oXM0CzVDi7P1HstOpKvFMInPMOzpQ9uA6t3x75q5f5wQvsEy9vuiiiM94ZJCJSV7PcZGroSSInQCTGsu04QEiE
sdkKey: LHx_tPFslZpTTiIPABmS24T7Ev1QEVOUVOzirpkxLUuTwJTVZGCKAk9L3Tm3FwuM5LxHK3q1EIbAemJB5sNpX2
customGradleVersionUrl:
customGradleToolsVersion:
adMobAndroidAppId:

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 572fee4574afa4f6dbf2846e0c152fe8
guid: d59f5579a5b8c4b4c87de418f3edd7fa
labels:
- al_max
- al_max_export_path-MaxSdk/Resources/Images/alert_icon.png
- al_max_export_path-MaxSdk/Resources/Images/error_icon.png
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
Binary file not shown.

After

Width:  |  Height:  |  Size: 576 B

@@ -0,0 +1,95 @@
fileFormatVersion: 2
guid: 036a0d1eebcb7467ba676e6cb67c7872
labels:
- al_max
- al_max_export_path-MaxSdk/Resources/Images/info_icon.png
TextureImporter:
internalIDToNameTable: []
externalObjects: {}
serializedVersion: 11
mipmaps:
mipMapMode: 0
enableMipMap: 1
sRGBTexture: 1
linearTexture: 0
fadeOut: 0
borderMipMap: 0
mipMapsPreserveCoverage: 0
alphaTestReferenceValue: 0.5
mipMapFadeDistanceStart: 1
mipMapFadeDistanceEnd: 3
bumpmap:
convertToNormalMap: 0
externalNormalMap: 0
heightScale: 0.25
normalMapFilter: 0
isReadable: 0
streamingMipmaps: 0
streamingMipmapsPriority: 0
grayScaleToAlpha: 0
generateCubemap: 6
cubemapConvolution: 0
seamlessCubemap: 0
textureFormat: 1
maxTextureSize: 2048
textureSettings:
serializedVersion: 2
filterMode: 1
aniso: 1
mipBias: 0
wrapU: 0
wrapV: 0
wrapW: 0
nPOTScale: 1
lightmap: 0
compressionQuality: 50
spriteMode: 0
spriteExtrude: 1
spriteMeshType: 1
alignment: 0
spritePivot: {x: 0.5, y: 0.5}
spritePixelsToUnits: 100
spriteBorder: {x: 0, y: 0, z: 0, w: 0}
spriteGenerateFallbackPhysicsShape: 1
alphaUsage: 1
alphaIsTransparency: 0
spriteTessellationDetail: -1
textureType: 0
textureShape: 1
singleChannelComponent: 0
maxTextureSizeSet: 0
compressionQualitySet: 0
textureFormatSet: 0
applyGammaDecoding: 0
platformSettings:
- serializedVersion: 3
buildTarget: DefaultTexturePlatform
maxTextureSize: 2048
resizeAlgorithm: 0
textureFormat: -1
textureCompression: 1
compressionQuality: 50
crunchedCompression: 0
allowsAlphaSplitting: 0
overridden: 0
androidETC2FallbackOverride: 0
forceMaximumCompressionQuality_BC6H_BC7: 0
spriteSheet:
serializedVersion: 2
sprites: []
outline: []
physicsShape: []
bones: []
spriteID:
internalID: 0
vertices: []
indices:
edges: []
weights: []
secondaryTextures: []
spritePackingTag:
pSDRemoveMatte: 0
pSDShowRemoveMatteOption: 0
userData:
assetBundleName:
assetBundleVariant:
@@ -97,7 +97,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
if (option == 0) // Download
{
MaxSdkLogger.UserDebug("Downloading plugin...");
AppLovinIntegrationManager.OnDownloadPluginProgressCallback = AppLovinIntegrationManagerWindow.OnDownloadPluginProgress;
AppLovinEditorCoroutine.StartCoroutine(AppLovinIntegrationManager.Instance.DownloadPlugin(data.AppLovinMax));
}
else if (option == 1) // Not Now
@@ -72,7 +72,15 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
"MaxSdk/Version.md",
"MaxSdk/Version.md.meta",
// TODO: Add MaxTargetingData and MaxUserSegment when the plugin has enough traction.
// The alert_icon.png has been renamed to error_icon.png.
"MaxSdk/Resources/Images/alert_icon.png",
"MaxSdk/Resources/Images/alert_icon.png.meta",
// `TargetingData` has been removed and we no longer set `UserSegment` through the Unity Plugin.
"MaxSdk/Scripts/MaxUserSegment.cs",
"MaxSdk/Scripts/MaxUserSegment.cs.meta",
"MaxSdk/Scripts/MaxTargetingData.cs",
"MaxSdk/Scripts/MaxTargetingData.cs.meta"
};
static AppLovinInitialize()
@@ -9,6 +9,7 @@
using System;
using System.Collections;
using System.IO;
using AppLovinMax.Internal;
using UnityEditor;
using UnityEngine;
using UnityEngine.Networking;
@@ -23,6 +24,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public Network[] MediatedNetworks;
public Network[] PartnerMicroSdks;
public DynamicLibraryToEmbed[] ThirdPartyDynamicLibrariesToEmbed;
public Alert[] Alerts;
}
[Serializable]
@@ -58,6 +60,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
[NonSerialized] public Versions CurrentVersions;
[NonSerialized] public MaxSdkUtils.VersionComparisonResult CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Lesser;
[NonSerialized] public bool RequiresUpdate;
[NonSerialized] public bool IsCurrentlyInstalling;
}
[Serializable]
@@ -80,6 +83,55 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
public enum Severity
{
Info,
Warning,
Error
}
[Serializable]
public class Alert
{
public string SeverityType;
public string Title;
public string Message;
public string Url;
public string MinimumPluginVersion;
public string MaximumPluginVersion;
public string MinimumUnityVersion;
public string MaximumUnityVersion;
public Severity Severity;
public void InitializeSeverityEnum()
{
switch (SeverityType)
{
case "INFO":
Severity = Severity.Info;
break;
case "WARNING":
Severity = Severity.Warning;
break;
case "ERROR":
Severity = Severity.Error;
break;
default:
MaxSdkLogger.E(string.Format("Alert <{0}> has unsupported severity type <{1}>.", Title, SeverityType));
Severity = Severity.Info;
break;
}
}
public bool ShouldShowAlert()
{
var pluginVersionValid = MaxSdkUtils.IsVersionInRange(MaxSdk.Version, MinimumPluginVersion, MaximumPluginVersion);
var unityVersionValid = MaxSdkUtils.IsVersionInRange(Application.unityVersion, MinimumUnityVersion, MaximumUnityVersion);
return pluginVersionValid && unityVersionValid;
}
}
/// <summary>
/// A helper data class used to get current versions from Dependency.xml files.
/// </summary>
@@ -128,18 +180,15 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public class AppLovinIntegrationManager
{
/// <summary>
/// Delegate to be called when downloading a plugin with the progress percentage.
/// Delegate to be called when a plugin package's import is started.
/// </summary>
/// <param name="pluginName">The name of the plugin being downloaded.</param>
/// <param name="progress">Percentage downloaded.</param>
/// <param name="done">Whether or not the download is complete.</param>
public delegate void DownloadPluginProgressCallback(string pluginName, float progress, bool done);
internal delegate void ImportPackageStartedCallback(Network network);
/// <summary>
/// Delegate to be called when a plugin package is imported.
/// Delegate to be called when a plugin package is finished importing.
/// </summary>
/// <param name="network">The network data for which the package is imported.</param>
public delegate void ImportPackageCompletedCallback(Network network);
internal delegate void ImportPackageCompletedCallback(Network network);
private static readonly AppLovinIntegrationManager instance = new AppLovinIntegrationManager();
@@ -147,14 +196,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string MaxSdkAssetExportPath = "MaxSdk/Scripts/MaxSdk.cs";
private const string MaxSdkMediationExportPath = "MaxSdk/Mediation";
private static readonly string PluginDataEndpoint = "https://unity.applovin.com/max/1.0/integration_manager_info?plugin_version={0}";
private const string PluginDataEndpoint = "https://unity.applovin.com/max/1.0/integration_manager_info?plugin_version={0}";
private static string _externalDependencyManagerVersion;
private static string externalDependencyManagerVersion;
public static DownloadPluginProgressCallback OnDownloadPluginProgressCallback;
public static ImportPackageCompletedCallback OnImportPackageCompletedCallback;
internal static ImportPackageStartedCallback OnImportPackageStartedCallback;
internal static ImportPackageCompletedCallback OnImportPackageCompletedCallback;
private UnityWebRequest webRequest;
private MaxWebRequest maxWebRequest;
private Network importingNetwork;
/// <summary>
@@ -172,8 +221,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
get
{
// Search for the asset with the default exported path first, In most cases, we should be able to find the asset.
// In some cases where we don't, use the platform specific export path to search for the asset (in case of migrating a project from Windows to Mac or vice versa).
// Search for the asset with the export path label.
// Paths are normalized using AltDirectorySeparatorChar (/) to ensure compatibility across platforms (in case of migrating a project from Windows to Mac or vice versa).
var maxSdkScriptAssetPath = MaxSdkUtils.GetAssetPathForExportPath(MaxSdkAssetExportPath);
// maxSdkScriptAssetPath will always have AltDirectorySeparatorChar (/) as the path separator. Convert to platform specific path.
@@ -230,26 +279,33 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
get
{
if (MaxSdkUtils.IsValidString(_externalDependencyManagerVersion)) return _externalDependencyManagerVersion;
if (MaxSdkUtils.IsValidString(externalDependencyManagerVersion)) return externalDependencyManagerVersion;
try
{
var versionHandlerVersionNumberType = Type.GetType("Google.VersionHandlerVersionNumber, Google.VersionHandlerImpl");
_externalDependencyManagerVersion = versionHandlerVersionNumberType.GetProperty("Value").GetValue(null, null).ToString();
externalDependencyManagerVersion = versionHandlerVersionNumberType.GetProperty("Value").GetValue(null, null).ToString();
}
#pragma warning disable 0168
catch (Exception ignored)
#pragma warning restore 0168
{
_externalDependencyManagerVersion = "Failed to get version.";
externalDependencyManagerVersion = "Failed to get version.";
}
return _externalDependencyManagerVersion;
return externalDependencyManagerVersion;
}
}
private AppLovinIntegrationManager()
{
AssetDatabase.importPackageStarted += packageName =>
{
if (!IsImportingNetwork(packageName)) return;
CallImportPackageStartedCallback(importingNetwork);
};
// Add asset import callbacks.
AssetDatabase.importPackageCompleted += packageName =>
{
@@ -265,7 +321,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
if (!IsImportingNetwork(packageName)) return;
MaxSdkLogger.UserDebug("Package import cancelled.");
importingNetwork = null;
};
@@ -283,15 +338,15 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public static PluginData LoadPluginDataSync()
{
var url = string.Format(PluginDataEndpoint, MaxSdk.Version);
using (var unityWebRequest = UnityWebRequest.Get(url))
var webRequestConfig = new WebRequestConfig()
{
var operation = unityWebRequest.SendWebRequest();
EndPoint = url,
};
// Just wait till www is done
while (!operation.isDone) { }
var maxWebRequest = new MaxWebRequest(webRequestConfig);
var webResponse = maxWebRequest.SendSync();
return CreatePluginDataFromWebResponse(unityWebRequest);
}
return CreatePluginDataFromWebResponse(webResponse);
}
/// <summary>
@@ -301,25 +356,22 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public IEnumerator LoadPluginData(Action<PluginData> callback)
{
var url = string.Format(PluginDataEndpoint, MaxSdk.Version);
using (var unityWebRequest = UnityWebRequest.Get(url))
var webRequestConfig = new WebRequestConfig()
{
var operation = unityWebRequest.SendWebRequest();
while (!operation.isDone) yield return new WaitForSeconds(0.1f); // Just wait till www is done. Our coroutine is pretty rudimentary.
var pluginData = CreatePluginDataFromWebResponse(unityWebRequest);
EndPoint = url,
};
maxWebRequest = new MaxWebRequest(webRequestConfig);
yield return maxWebRequest.Send(webResponse =>
{
var pluginData = CreatePluginDataFromWebResponse(webResponse);
callback(pluginData);
}
});
}
private static PluginData CreatePluginDataFromWebResponse(UnityWebRequest unityWebRequest)
private static PluginData CreatePluginDataFromWebResponse(WebResponse webResponse)
{
#if UNITY_2020_1_OR_NEWER
if (unityWebRequest.result != UnityWebRequest.Result.Success)
#else
if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
#endif
if (!webResponse.IsSuccess)
{
MaxSdkLogger.E("Failed to load plugin data. Please check your internet connection.");
return null;
@@ -328,7 +380,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
PluginData pluginData;
try
{
pluginData = JsonUtility.FromJson<PluginData>(unityWebRequest.downloadHandler.text);
pluginData = JsonUtility.FromJson<PluginData>(webResponse.ResponseMessage);
AppLovinPackageManager.PluginData = pluginData;
}
catch (Exception exception)
@@ -349,9 +401,20 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
AppLovinPackageManager.UpdateCurrentVersions(network);
}
foreach (var partnerMicroSdk in pluginData.PartnerMicroSdks)
if (pluginData.PartnerMicroSdks != null)
{
AppLovinPackageManager.UpdateCurrentVersions(partnerMicroSdk);
foreach (var partnerMicroSdk in pluginData.PartnerMicroSdks)
{
AppLovinPackageManager.UpdateCurrentVersions(partnerMicroSdk);
}
}
if (pluginData.Alerts == null) return pluginData;
// Initiate Severity enums from the raw strings in the response
foreach (var alert in pluginData.Alerts)
{
alert.InitializeSeverityEnum();
}
return pluginData;
@@ -366,36 +429,25 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public IEnumerator DownloadPlugin(Network network, bool showImport = true)
{
var path = Path.Combine(Application.temporaryCachePath, GetPluginFileName(network)); // TODO: Maybe delete plugin file after finishing import.
var downloadHandler = new DownloadHandlerFile(path);
webRequest = new UnityWebRequest(network.DownloadUrl)
var webRequestConfig = new WebRequestConfig()
{
method = UnityWebRequest.kHttpVerbGET,
downloadHandler = downloadHandler
DownloadHandler = new DownloadHandlerFile(path),
EndPoint = network.DownloadUrl
};
var operation = webRequest.SendWebRequest();
while (!operation.isDone)
maxWebRequest = new MaxWebRequest(webRequestConfig);
yield return maxWebRequest.Send(webResponse =>
{
yield return new WaitForSeconds(0.1f); // Just wait till webRequest is completed. Our coroutine is pretty rudimentary.
CallDownloadPluginProgressCallback(network.DisplayName, operation.progress, operation.isDone);
}
#if UNITY_2020_1_OR_NEWER
if (webRequest.result != UnityWebRequest.Result.Success)
#else
if (webRequest.isNetworkError || webRequest.isHttpError)
#endif
{
MaxSdkLogger.UserError(webRequest.error);
}
else
{
importingNetwork = network;
AssetDatabase.ImportPackage(path, showImport);
}
webRequest.Dispose();
webRequest = null;
if (webResponse.IsSuccess)
{
importingNetwork = network;
AssetDatabase.ImportPackage(path, showImport);
}
else
{
MaxSdkLogger.UserError("Failed to download plugin package: " + webResponse.ErrorMessage);
}
});
}
/// <summary>
@@ -403,9 +455,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
public void CancelDownload()
{
if (webRequest == null) return;
if (maxWebRequest == null) return;
webRequest.Abort();
maxWebRequest.Abort();
}
/// <summary>
@@ -436,11 +488,11 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
return importingNetwork != null && GetPluginFileName(importingNetwork).Contains(packageName);
}
private static void CallDownloadPluginProgressCallback(string pluginName, float progress, bool isDone)
private static void CallImportPackageStartedCallback(Network network)
{
if (OnDownloadPluginProgressCallback == null) return;
if (OnImportPackageStartedCallback == null) return;
OnDownloadPluginProgressCallback(pluginName, progress, isDone);
OnImportPackageStartedCallback(network);
}
private static void CallImportPackageCompletedCallback(Network network)
@@ -8,6 +8,7 @@
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using UnityEditor;
@@ -22,12 +23,13 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string AppLovinSdkKeyLink = "https://dash.applovin.com/o/account#keys";
private const string UserTrackingUsageDescriptionDocsLink = "https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription";
private const string DocumentationTermsAndPrivacyPolicyFlow = "https://developers.applovin.com/en/unity/overview/terms-and-privacy-policy-flow";
private const string DocumentationAdaptersLink = "https://developers.applovin.com/en/unity/preparing-mediated-networks";
private const string DocumentationTermsAndPrivacyPolicyFlow = "https://support.axon.ai/en/max/unity/overview/terms-and-privacy-policy-flow";
private const string DocumentationAdaptersLink = "https://support.axon.ai/en/max/unity/preparing-mediated-networks";
private const string DocumentationNote = "Please ensure that integration instructions (e.g. permissions, ATS settings, etc) specific to each network are implemented as well. Click the link below for more info:";
private const string UninstallIconExportPath = "MaxSdk/Resources/Images/uninstall_icon.png";
private const string AlertIconExportPath = "MaxSdk/Resources/Images/alert_icon.png";
private const string InfoIconExportPath = "MaxSdk/Resources/Images/info_icon.png";
private const string WarningIconExportPath = "MaxSdk/Resources/Images/warning_icon.png";
private const string ErrorIconExportPath = "MaxSdk/Resources/Images/error_icon.png";
private const string QualityServiceRequiresGradleBuildErrorMsg = "AppLovin Quality Service integration via AppLovin Integration Manager requires Custom Gradle Template enabled or Unity 2018.2 or higher.\n" +
"If you would like to continue using your existing setup, please add Quality Service Plugin to your build.gradle manually.";
@@ -35,6 +37,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string CustomGradleVersionTooltip = "To set the version to 6.9.3, set the field to: https://services.gradle.org/distributions/gradle-6.9.3-bin.zip";
private const string CustomGradleToolsVersionTooltip = "To set the version to 4.2.0, set the field to: 4.2.0";
private const string KeyShowAlerts = "com.applovin.show_alerts";
private const string KeyShowMicroSdkPartners = "com.applovin.show_micro_sdk_partners";
private const string KeyShowMediatedNetworks = "com.applovin.show_mediated_networks";
private const string KeyShowSdkSettings = "com.applovin.show_sdk_settings";
@@ -50,18 +53,18 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private Vector2 scrollPosition;
private static readonly Vector2 WindowMinSize = new Vector2(750, 750);
private const float ActionFieldWidth = 60f;
private const float ActionFieldWidth = 70f;
private const float UpgradeAllButtonWidth = 80f;
private const float NetworkFieldMinWidth = 100f;
private const float VersionFieldMinWidth = 190f;
private const float PrivacySettingLabelWidth = 250f;
private const float NetworkFieldWidthPercentage = 0.22f;
private const float VersionFieldWidthPercentage = 0.36f; // There are two version fields. Each take 40% of the width, network field takes the remaining 20%.
private static float _previousWindowWidth = WindowMinSize.x;
private static GUILayoutOption _networkWidthOption = GUILayout.Width(NetworkFieldMinWidth);
private static GUILayoutOption _versionWidthOption = GUILayout.Width(VersionFieldMinWidth);
private static float previousWindowWidth = WindowMinSize.x;
private static GUILayoutOption networkWidthOption = GUILayout.Width(NetworkFieldMinWidth);
private static GUILayoutOption versionWidthOption = GUILayout.Width(VersionFieldMinWidth);
private static GUILayoutOption _privacySettingFieldWidthOption = GUILayout.Width(400);
private static GUILayoutOption privacySettingFieldWidthOption = GUILayout.Width(400);
private static readonly GUILayoutOption FieldWidth = GUILayout.Width(ActionFieldWidth);
private static readonly GUILayoutOption UpgradeAllButtonFieldWidth = GUILayout.Width(UpgradeAllButtonWidth);
private static readonly GUILayoutOption CollapseButtonWidthOption = GUILayout.Width(20f);
@@ -82,8 +85,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private AppLovinEditorCoroutine loadDataCoroutine;
private Texture2D uninstallIcon;
private Texture2D alertIcon;
private Texture2D infoIcon;
private Texture2D warningIcon;
private Texture2D errorIcon;
public static void ShowManager()
{
@@ -140,15 +144,20 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
uninstallIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
uninstallIcon.LoadImage(uninstallIconData);
// Load alert icon texture.
var alertIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(AlertIconExportPath));
alertIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
alertIcon.LoadImage(alertIconData);
// Load info icon texture.
var infoIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(InfoIconExportPath));
infoIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
infoIcon.LoadImage(infoIconData);
// Load warning icon texture.
var warningIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(WarningIconExportPath));
warningIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
warningIcon.LoadImage(warningIconData);
// Load error icon texture.
var errorIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(ErrorIconExportPath));
errorIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
errorIcon.LoadImage(errorIconData);
}
private void OnEnable()
@@ -173,9 +182,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private void OnWindowEnabled()
{
AppLovinIntegrationManager.OnDownloadPluginProgressCallback = OnDownloadPluginProgress;
// Plugin downloaded and imported. Update current versions for the imported package.
AppLovinIntegrationManager.OnImportPackageStartedCallback = OnImportPackageStarted;
AppLovinIntegrationManager.OnImportPackageCompletedCallback = OnImportPackageCompleted;
Load();
@@ -199,9 +207,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private void OnGUI()
{
// OnGUI is called on each frame draw, so we don't want to do any unnecessary calculation if we can avoid it. So only calculate it when the width actually changed.
if (Math.Abs(_previousWindowWidth - position.width) > 1)
if (Math.Abs(previousWindowWidth - position.width) > 1)
{
_previousWindowWidth = position.width;
previousWindowWidth = position.width;
CalculateFieldWidth();
}
@@ -213,12 +221,31 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// Draw AppLovin MAX plugin details
EditorGUILayout.LabelField("AppLovin MAX Plugin Details", titleLabelStyle);
DrawPluginDetails();
if (pluginData != null && pluginData.PartnerMicroSdks != null)
// Draw alerts
if (pluginData != null && pluginData.Alerts != null)
{
DrawCollapsableSection(KeyShowMicroSdkPartners, "AppLovin Micro SDK Partners", DrawPartnerMicroSdks);
var alertsToShow = pluginData.Alerts.Where(alert => alert.ShouldShowAlert()).ToList();
if (alertsToShow.Count > 0)
{
EditorGUILayout.BeginHorizontal();
var showAlertDetails = DrawExpandCollapseButton(KeyShowAlerts);
EditorGUILayout.LabelField("Alerts", titleLabelStyle, GUILayout.Width(45));
DrawAlertCount(alertsToShow);
GUILayout.FlexibleSpace();
EditorGUILayout.EndHorizontal();
if (showAlertDetails)
{
DrawAlerts(alertsToShow);
}
}
}
// Draw Micro SDK Partners
if (pluginData != null && !MaxSdkUtils.IsNullOrEmpty(pluginData.PartnerMicroSdks))
{
DrawCollapsibleSection(KeyShowMicroSdkPartners, "AppLovin Micro SDK Partners", DrawPartnerMicroSdks);
}
// Draw mediated networks);
@@ -233,20 +260,18 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
DrawMediatedNetworks();
}
#if UNITY_2019_2_OR_NEWER
if (!AppLovinIntegrationManager.IsPluginInPackageManager)
{
EditorGUILayout.LabelField("Unity Package Manager Migration", titleLabelStyle);
DrawPluginMigrationHelper();
}
#endif
// Draw AppLovin Quality Service settings
DrawCollapsableSection(KeyShowSdkSettings, "SDK Settings", DrawQualityServiceSettings);
DrawCollapsibleSection(KeyShowSdkSettings, "SDK Settings", DrawQualityServiceSettings);
DrawCollapsableSection(KeyShowPrivacySettings, "Privacy Settings", DrawPrivacySettings);
DrawCollapsibleSection(KeyShowPrivacySettings, "Privacy Settings", DrawPrivacySettings);
DrawCollapsableSection(KeyShowOtherSettings, "Other Settings", DrawOtherSettings);
DrawCollapsibleSection(KeyShowOtherSettings, "Other Settings", DrawOtherSettings);
// Draw Unity environment details
EditorGUILayout.LabelField("Unity Environment Details", titleLabelStyle);
@@ -340,9 +365,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.BeginHorizontal();
GUILayout.FlexibleSpace();
GUI.enabled = upgradeButtonEnabled;
if (GUILayout.Button(new GUIContent("Upgrade"), FieldWidth))
var action = appLovinMax.IsCurrentlyInstalling ? "Installing..." : "Upgrade";
GUI.enabled = upgradeButtonEnabled && !appLovinMax.IsCurrentlyInstalling;
if (GUILayout.Button(new GUIContent(action), FieldWidth))
{
// Only show "Installing..." if the plugin is in the Assets folder
// Manifest edits don't trigger import callbacks, and UPM resolution locks the UI anyway.
appLovinMax.IsCurrentlyInstalling = !AppLovinIntegrationManager.IsPluginInPackageManager;
AppLovinEditorCoroutine.StartCoroutine(AppLovinPackageManager.AddNetwork(pluginData.AppLovinMax, true));
}
@@ -358,6 +388,84 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.EndHorizontal();
}
/// <summary>
/// Draw the number of each alert type next to the alert section header.
/// </summary>
private void DrawAlertCount(List<Alert> alerts)
{
if (pluginData == null) return;
var infoAlertsCount = alerts.Count(alert => alert.Severity == Severity.Info);
var warningAlertsCount = alerts.Count(alert => alert.Severity == Severity.Warning);
var errorAlertsCount = alerts.Count(alert => alert.Severity == Severity.Error);
GUILayout.Label(infoIcon, GUILayout.Width(20), GUILayout.Height(20));
EditorGUILayout.LabelField(AlertCountToString(infoAlertsCount), GUILayout.Width(20));
GUILayout.Label(warningIcon, GUILayout.Width(20), GUILayout.Height(20));
EditorGUILayout.LabelField(AlertCountToString(warningAlertsCount), GUILayout.Width(20));
GUILayout.Label(errorIcon, GUILayout.Width(20), GUILayout.Height(20));
EditorGUILayout.LabelField(AlertCountToString(errorAlertsCount), GUILayout.Width(20));
}
/// <summary>
/// Draw the list of alerts grouped by severity.
/// </summary>
private void DrawAlerts(List<Alert> alerts)
{
GUILayout.BeginHorizontal();
GUILayout.Space(10);
using (new EditorGUILayout.VerticalScope("box"))
{
DrawAlertsOfType(alerts, Severity.Error);
DrawAlertsOfType(alerts, Severity.Warning);
DrawAlertsOfType(alerts, Severity.Info);
}
GUILayout.Space(5);
GUILayout.EndHorizontal();
}
private void DrawAlertsOfType(List<Alert> alerts, Severity severity)
{
var alertsOfType = alerts.Where(alert => alert.Severity == severity).ToList();
foreach (var alert in alertsOfType)
{
DrawAlert(alert);
}
}
/// <summary>
/// Draw a single alert.
/// </summary>
private void DrawAlert(Alert alert)
{
using (new EditorGUILayout.HorizontalScope())
{
using (new EditorGUILayout.VerticalScope(GUILayout.Width(20)))
{
GUILayout.Space(2);
GUILayout.Label(GetSeverityIcon(alert.Severity), GUILayout.Width(20), GUILayout.Height(20));
}
using (new EditorGUILayout.VerticalScope())
{
GUILayout.Label(alert.Title, headerLabelStyle);
EditorGUILayout.LabelField(alert.Message, wrapTextLabelStyle);
if (MaxSdkUtils.IsValidString(alert.Url))
{
if (GUILayout.Button(new GUIContent(alert.Url), linkLabelStyle))
{
Application.OpenURL(alert.Url);
}
}
GUILayout.Space(2);
}
}
GUILayout.Space(10);
}
/// <summary>
/// Draws the headers for a table.
/// </summary>
@@ -366,10 +474,10 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.Space(5);
EditorGUILayout.LabelField(firstColumnTitle, headerLabelStyle, _networkWidthOption);
EditorGUILayout.LabelField("Current Version", headerLabelStyle, _versionWidthOption);
EditorGUILayout.LabelField(firstColumnTitle, headerLabelStyle, networkWidthOption);
EditorGUILayout.LabelField("Current Version", headerLabelStyle, versionWidthOption);
GUILayout.Space(3);
EditorGUILayout.LabelField("Latest Version", headerLabelStyle, _versionWidthOption);
EditorGUILayout.LabelField("Latest Version", headerLabelStyle, versionWidthOption);
GUILayout.Space(3);
if (drawAction)
{
@@ -390,10 +498,10 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
using (new EditorGUILayout.HorizontalScope())
{
GUILayout.Space(5);
EditorGUILayout.LabelField(new GUIContent(platform), _networkWidthOption);
EditorGUILayout.LabelField(new GUIContent(currentVersion), _versionWidthOption);
EditorGUILayout.LabelField(new GUIContent(platform), networkWidthOption);
EditorGUILayout.LabelField(new GUIContent(currentVersion), versionWidthOption);
GUILayout.Space(3);
EditorGUILayout.LabelField(new GUIContent(latestVersion), _versionWidthOption);
EditorGUILayout.LabelField(new GUIContent(latestVersion), versionWidthOption);
GUILayout.Space(3);
}
@@ -501,25 +609,33 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandHeight(false)))
{
GUILayout.Space(5);
EditorGUILayout.LabelField(new GUIContent(network.DisplayName), _networkWidthOption);
EditorGUILayout.LabelField(new GUIContent(currentVersion), _versionWidthOption);
EditorGUILayout.LabelField(new GUIContent(network.DisplayName), networkWidthOption);
EditorGUILayout.LabelField(new GUIContent(currentVersion), versionWidthOption);
GUILayout.Space(3);
EditorGUILayout.LabelField(new GUIContent(latestVersion), _versionWidthOption);
EditorGUILayout.LabelField(new GUIContent(latestVersion), versionWidthOption);
GUILayout.Space(3);
GUILayout.FlexibleSpace();
if (network.RequiresUpdate)
{
GUILayout.Label(new GUIContent {image = alertIcon, tooltip = "Adapter not compatible, please update to the latest version."}, iconStyle);
GUILayout.Label(new GUIContent {image = errorIcon, tooltip = "Adapter not compatible, please update to the latest version."}, iconStyle);
}
else if ((network.Name.Equals("ADMOB_NETWORK") || network.Name.Equals("GOOGLE_AD_MANAGER_NETWORK")) && shouldShowGoogleWarning)
{
GUILayout.Label(new GUIContent {image = warningIcon, tooltip = "You may see unexpected errors if you use different versions of the AdMob and Google Ad Manager adapter SDKs."}, iconStyle);
}
GUI.enabled = networkButtonsEnabled && isActionEnabled;
if (network.IsCurrentlyInstalling)
{
action = "Installing...";
}
GUI.enabled = networkButtonsEnabled && isActionEnabled && !network.IsCurrentlyInstalling;
if (GUILayout.Button(new GUIContent(action), FieldWidth))
{
// Only show "Installing..." if the plugin is in the Assets folder
// Manifest edits don't trigger import callbacks, and UPM resolution locks the UI anyway.
network.IsCurrentlyInstalling = !AppLovinIntegrationManager.IsPluginInPackageManager;
AppLovinEditorCoroutine.StartCoroutine(AppLovinPackageManager.AddNetwork(network, true));
}
@@ -529,9 +645,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUI.enabled = networkButtonsEnabled && isInstalled;
if (GUILayout.Button(new GUIContent {image = uninstallIcon, tooltip = "Uninstall"}, iconStyle))
{
EditorUtility.DisplayProgressBar("Integration Manager", "Deleting " + network.Name + "...", 0.5f);
AppLovinPackageManager.RemoveNetwork(network);
EditorUtility.ClearProgressBar();
AppLovinPackageManager.UpdateCurrentVersions(network);
UpdateShouldShowGoogleWarningIfNeeded();
}
GUI.enabled = true;
@@ -571,8 +687,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
using (new EditorGUILayout.VerticalScope("box"))
{
GUILayout.Space(2);
AppLovinSettings.Instance.AdMobAndroidAppId = DrawTextField("App ID (Android)", AppLovinSettings.Instance.AdMobAndroidAppId, _networkWidthOption);
AppLovinSettings.Instance.AdMobIosAppId = DrawTextField("App ID (iOS)", AppLovinSettings.Instance.AdMobIosAppId, _networkWidthOption);
AppLovinSettings.Instance.AdMobAndroidAppId = DrawTextField("App ID (Android)", AppLovinSettings.Instance.AdMobAndroidAppId, networkWidthOption);
AppLovinSettings.Instance.AdMobIosAppId = DrawTextField("App ID (iOS)", AppLovinSettings.Instance.AdMobIosAppId, networkWidthOption);
}
GUILayout.EndHorizontal();
@@ -593,7 +709,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(10);
}
#if UNITY_2019_2_OR_NEWER
private void DrawPluginMigrationHelper()
{
GUILayout.BeginHorizontal();
@@ -640,7 +755,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(5);
GUILayout.EndHorizontal();
}
#endif
private void DrawQualityServiceSettings()
{
@@ -661,7 +775,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(4);
}
AppLovinSettings.Instance.SdkKey = DrawTextField("AppLovin SDK Key", AppLovinSettings.Instance.SdkKey, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption);
AppLovinSettings.Instance.SdkKey = DrawTextField("AppLovin SDK Key", AppLovinSettings.Instance.SdkKey, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption);
GUILayout.BeginHorizontal();
GUILayout.Space(4);
GUILayout.Button("You can find your SDK key here: ", wrapTextLabelStyle, GUILayout.Width(185)); // Setting a fixed width since Unity adds arbitrary padding at the end leaving a space between link and text.
@@ -762,8 +876,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(8);
AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl = DrawTextField("Privacy Policy URL", AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption);
AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl = DrawTextField("Terms of Service URL (optional)", AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption);
AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl = DrawTextField("Privacy Policy URL", AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption);
AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl = DrawTextField("Terms of Service URL (optional)", AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption);
GUILayout.Space(4);
GUILayout.BeginHorizontal();
@@ -786,7 +900,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.Space(4);
GUILayout.Space(4);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn = DrawTextField("User Tracking Usage Description", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn = DrawTextField("User Tracking Usage Description", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
GUILayout.BeginHorizontal();
GUILayout.Space(4);
@@ -796,13 +910,13 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
if (AppLovinInternalSettings.Instance.UserTrackingUsageLocalizationEnabled)
{
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans = DrawTextField("Chinese, Simplified (zh-Hans)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant = DrawTextField("Chinese, Traditional (zh-Hant)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr = DrawTextField("French (fr)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe = DrawTextField("German (de)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa = DrawTextField("Japanese (ja)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo = DrawTextField("Korean (ko)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs = DrawTextField("Spanish (es)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans = DrawTextField("Chinese, Simplified (zh-Hans)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant = DrawTextField("Chinese, Traditional (zh-Hant)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr = DrawTextField("French (fr)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe = DrawTextField("German (de)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa = DrawTextField("Japanese (ja)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo = DrawTextField("Korean (ko)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs = DrawTextField("Spanish (es)", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, isEditableTextField);
GUILayout.Space(4);
GUILayout.BeginHorizontal();
@@ -837,7 +951,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
GUILayout.BeginHorizontal();
GUILayout.Space(4);
EditorGUILayout.LabelField("Debug User Geography");
AppLovinInternalSettings.Instance.DebugUserGeography = (MaxSdkBase.ConsentFlowUserGeography) EditorGUILayout.Popup((int) AppLovinInternalSettings.Instance.DebugUserGeography, debugUserGeographies, _privacySettingFieldWidthOption);
AppLovinInternalSettings.Instance.DebugUserGeography = (MaxSdkBase.ConsentFlowUserGeography) EditorGUILayout.Popup((int) AppLovinInternalSettings.Instance.DebugUserGeography, debugUserGeographies, privacySettingFieldWidthOption);
GUILayout.Space(4);
GUILayout.EndHorizontal();
@@ -857,8 +971,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var verboseLoggingEnabled = DrawOtherSettingsToggle(EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled, false), " Enable Verbose Logging");
EditorPrefs.SetBool(MaxSdkLogger.KeyVerboseLoggingEnabled, verboseLoggingEnabled);
GUILayout.Space(5);
AppLovinSettings.Instance.CustomGradleVersionUrl = DrawTextField("Custom Gradle Version URL", AppLovinSettings.Instance.CustomGradleVersionUrl, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, tooltip: CustomGradleVersionTooltip);
AppLovinSettings.Instance.CustomGradleToolsVersion = DrawTextField("Custom Gradle Tools Version", AppLovinSettings.Instance.CustomGradleToolsVersion, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, tooltip: CustomGradleToolsVersionTooltip);
AppLovinSettings.Instance.CustomGradleVersionUrl = DrawTextField("Custom Gradle Version URL", AppLovinSettings.Instance.CustomGradleVersionUrl, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, tooltip: CustomGradleVersionTooltip);
AppLovinSettings.Instance.CustomGradleToolsVersion = DrawTextField("Custom Gradle Tools Version", AppLovinSettings.Instance.CustomGradleToolsVersion, GUILayout.Width(PrivacySettingLabelWidth), privacySettingFieldWidthOption, tooltip: CustomGradleToolsVersionTooltip);
EditorGUILayout.HelpBox("This will overwrite the gradle build tools version in your base gradle template.", MessageType.Info);
}
@@ -910,7 +1024,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
private void DrawCollapsableSection(string keyShowDetails, string label, Action drawContent)
private void DrawCollapsibleSection(string keyShowDetails, string label, Action drawContent)
{
EditorGUILayout.BeginHorizontal();
var showDetails = DrawExpandCollapseButton(keyShowDetails);
@@ -944,14 +1058,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var currentWidth = position.width;
var availableWidth = currentWidth - ActionFieldWidth - 80; // NOTE: Magic number alert. This is the sum of all the spacing the fields and other UI elements.
var networkLabelWidth = Math.Max(NetworkFieldMinWidth, availableWidth * NetworkFieldWidthPercentage);
_networkWidthOption = GUILayout.Width(networkLabelWidth);
networkWidthOption = GUILayout.Width(networkLabelWidth);
var versionLabelWidth = Math.Max(VersionFieldMinWidth, availableWidth * VersionFieldWidthPercentage);
_versionWidthOption = GUILayout.Width(versionLabelWidth);
versionWidthOption = GUILayout.Width(versionLabelWidth);
const int textFieldOtherUiElementsWidth = 55; // NOTE: Magic number alert. This is the sum of all the spacing the fields and other UI elements.
var availableUserDescriptionTextFieldWidth = currentWidth - PrivacySettingLabelWidth - textFieldOtherUiElementsWidth;
_privacySettingFieldWidthOption = GUILayout.Width(availableUserDescriptionTextFieldWidth);
privacySettingFieldWidthOption = GUILayout.Width(availableUserDescriptionTextFieldWidth);
}
#endregion
@@ -982,25 +1096,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}));
}
/// <summary>
/// Callback method that will be called with progress updates when the plugin is being downloaded.
/// </summary>
public static void OnDownloadPluginProgress(string pluginName, float progress, bool done)
private void OnImportPackageStarted(Network network)
{
// Download is complete. Clear progress bar.
if (done)
{
EditorUtility.ClearProgressBar();
}
// Download is in progress, update progress bar.
else
{
if (EditorUtility.DisplayCancelableProgressBar(WindowTitle, string.Format("Downloading {0} plugin...", pluginName), progress))
{
AppLovinIntegrationManager.Instance.CancelDownload();
EditorUtility.ClearProgressBar();
}
}
network.IsCurrentlyInstalling = false;
}
private void OnImportPackageCompleted(Network network)
@@ -1070,6 +1168,32 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
return networks.Any(network => MaxSdkUtils.IsValidString(network.CurrentVersions.Unity) && network.CurrentToLatestVersionComparisonResult == MaxSdkUtils.VersionComparisonResult.Lesser);
}
/// <summary>
/// Takes in an int representing the count of an alert and returns it as a string or "9+" if greater than 9.
/// </summary>
private string AlertCountToString(int count)
{
return count > 9 ? "9+" : count.ToString();
}
/// <summary>
/// Returns the icon for the given severity type.
/// </summary>
private Texture2D GetSeverityIcon(Severity severity)
{
switch (severity)
{
case Severity.Info:
return infoIcon;
case Severity.Warning:
return warningIcon;
case Severity.Error:
return errorIcon;
default:
return infoIcon;
}
}
#endregion
}
}
@@ -1,5 +1,5 @@
//
// AppLovinInternalSettigns.cs
// AppLovinInternalSettings.cs
// AppLovin User Engagement Unity Plugin
//
// Created by Santosh Bagadi on 9/15/22.
@@ -20,7 +20,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
public class AppLovinInternalSettings : ScriptableObject
{
private static AppLovinInternalSettings _instance;
private static AppLovinInternalSettings instance;
private const string DefaultUserTrackingDescriptionEn = "This uses device info for more personalized ads and content";
private const string DefaultUserTrackingDescriptionDe = "Dies benutzt Gerätinformationen für relevantere Werbeinhalte";
@@ -53,33 +53,33 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
get
{
if (_instance != null) return _instance;
if (instance != null) return instance;
_instance = CreateInstance<AppLovinInternalSettings>();
instance = CreateInstance<AppLovinInternalSettings>();
var projectRootPath = Path.GetDirectoryName(Application.dataPath);
var settingsFilePath = Path.Combine(projectRootPath, SettingsFilePath);
if (!File.Exists(settingsFilePath))
{
_instance.Save();
return _instance;
instance.Save();
return instance;
}
var settingsJson = File.ReadAllText(settingsFilePath);
if (string.IsNullOrEmpty(settingsJson))
{
_instance.Save();
return _instance;
instance.Save();
return instance;
}
JsonUtility.FromJsonOverwrite(settingsJson, _instance);
return _instance;
JsonUtility.FromJsonOverwrite(settingsJson, instance);
return instance;
}
}
public void Save()
{
var settingsJson = JsonUtility.ToJson(_instance);
var settingsJson = JsonUtility.ToJson(instance);
try
{
var projectRootPath = Path.GetDirectoryName(Application.dataPath);
@@ -31,7 +31,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
[MenuItem("AppLovin/Documentation")]
private static void Documentation()
{
Application.OpenURL("https://developers.applovin.com/en/unity/overview/integration");
Application.OpenURL("https://support.axon.ai/en/max/unity/overview/integration");
}
[MenuItem("AppLovin/Contact Us")]
@@ -31,20 +31,14 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
private const string AppLovinMediationAmazonAdapterDependenciesPath = "Amazon/Scripts/Mediations/AppLovinMediation/Editor/Dependencies.xml";
#if UNITY_2019_2_OR_NEWER
private static readonly IPackageManagerClient _upmPackageManager = new AppLovinUpmPackageManager();
#endif
private static readonly IPackageManagerClient _assetsPackageManager = new AppLovinAssetsPackageManager();
private static IPackageManagerClient PackageManagerClient
{
get
{
#if UNITY_2019_2_OR_NEWER
return AppLovinIntegrationManager.IsPluginInPackageManager ? _upmPackageManager : _assetsPackageManager;
#else
return _assetsPackageManager;
#endif
}
}
@@ -110,10 +104,8 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var installedNetworksInAssets = AppLovinAssetsPackageManager.GetInstalledMediationNetworks();
installedNetworks.AddRange(installedNetworksInAssets);
#if UNITY_2019_2_OR_NEWER
var installedNetworksInPackages = AppLovinUpmPackageManager.GetInstalledMediationNetworks();
installedNetworks.AddRange(installedNetworksInPackages);
#endif
if (IsAmazonAppLovinAdapterInstalled())
{
@@ -155,7 +147,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// <returns>The exported path of the MAX plugin asset or an empty list if the asset is not found.</returns>
private static List<string> GetAssetPathListForExportPath(string exportPath)
{
var assetLabelToFind = "l:al_max_export_path-" + exportPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var assetLabelToFind = "l:al_max_export_path-" + MaxSdkUtils.NormalizeToUnityPath(exportPath);
var assetGuids = AssetDatabase.FindAssets(assetLabelToFind);
var assetPaths = new List<string>();
@@ -174,12 +166,10 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
internal static void UpdateCurrentVersions(Network network)
{
var assetPaths = GetAssetPathListForExportPath(network.DependenciesFilePath);
#if UNITY_2019_2_OR_NEWER
if (HasDuplicateAdapters(assetPaths))
{
ShowDeleteDuplicateAdapterPrompt(network);
}
#endif
var currentVersions = GetCurrentVersions(assetPaths);
network.CurrentVersions = currentVersions;
@@ -237,7 +227,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
#if UNITY_2019_2_OR_NEWER
/// <summary>
/// Checks whether a network has duplicate adapters installed in both the Assets folder and via UPM.
/// </summary>
@@ -274,6 +263,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// otherwise, deletes the adapter from the Assets folder.</param>
internal static void DeleteDuplicateAdapter(Network network, bool keepAssetsAdapter)
{
// Skip duplicate removal logic for our plugin.
if (network.Name.Equals("APPLOVIN_NETWORK")) return;
if (keepAssetsAdapter)
{
var appLovinManifest = AppLovinUpmManifest.Load();
@@ -292,7 +284,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
AppLovinUpmPackageManager.ResolvePackageManager();
}
#endif
/// <summary>
/// Gets the current versions for a given network's dependency file paths. UPM will have multiple paths
@@ -426,7 +417,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
#endregion
}
#if UNITY_2019_2_OR_NEWER
public class AppLovinUpmPackageManager : IPackageManagerClient
{
public const string PackageNamePrefixAppLovin = "com.applovin.mediation.ads";
@@ -595,8 +585,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
}
}
#endif
public class AppLovinAssetsPackageManager : IPackageManagerClient
{
public static List<string> GetInstalledMediationNetworks()
@@ -4,7 +4,6 @@ using System.Linq;
using UnityEditor;
using UnityEngine;
#if UNITY_2019_2_OR_NEWER
namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
/// <summary>
@@ -165,4 +164,3 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
#endregion
}
}
#endif
@@ -24,11 +24,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
public class AppLovinPostProcessAndroid : IPostGenerateGradleAndroidProject
{
#if UNITY_2019_3_OR_NEWER
private const string PropertyAndroidX = "android.useAndroidX";
private const string PropertyJetifier = "android.enableJetifier";
private const string EnableProperty = "=true";
#endif
private const string PropertyDexingArtifactTransform = "android.enableDexingArtifactTransform";
private const string DisableProperty = "=false";
@@ -70,15 +68,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
public void OnPostGenerateGradleAndroidProject(string path)
{
#if UNITY_2019_3_OR_NEWER
var rootGradleBuildFilePath = Path.Combine(path, "../build.gradle");
var gradlePropertiesPath = Path.Combine(path, "../gradle.properties");
var gradleWrapperPropertiesPath = Path.Combine(path, "../gradle/wrapper/gradle-wrapper.properties");
#else
var rootGradleBuildFilePath = Path.Combine(path, "build.gradle");
var gradlePropertiesPath = Path.Combine(path, "gradle.properties");
var gradleWrapperPropertiesPath = Path.Combine(path, "gradle/wrapper/gradle-wrapper.properties");
#endif
UpdateGradleVersionsIfNeeded(gradleWrapperPropertiesPath, rootGradleBuildFilePath);
@@ -89,20 +81,13 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
var lines = File.ReadAllLines(gradlePropertiesPath);
#if UNITY_2019_3_OR_NEWER
// Add all properties except AndroidX, Jetifier, and DexingArtifactTransform since they may already exist. We will re-add them below.
gradlePropertiesUpdated.AddRange(lines.Where(line => !line.Contains(PropertyAndroidX) && !line.Contains(PropertyJetifier) && !line.Contains(PropertyDexingArtifactTransform)));
#else
// Add all properties except DexingArtifactTransform since it may already exist. We will re-add it below.
gradlePropertiesUpdated.AddRange(lines.Where(line => !line.Contains(PropertyDexingArtifactTransform)));
#endif
}
#if UNITY_2019_3_OR_NEWER
// Enable AndroidX and Jetifier properties
gradlePropertiesUpdated.Add(PropertyAndroidX + EnableProperty);
gradlePropertiesUpdated.Add(PropertyJetifier + EnableProperty);
#endif
// `DexingArtifactTransform` has been removed in Gradle 8+ which is the default Gradle version for Unity 6.
#if !UNITY_6000_0_OR_NEWER
@@ -22,28 +22,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
#if UNITY_2019_3_OR_NEWER
// On Unity 2019.3+, the path returned is the path to the unityLibrary's module.
// The AppLovin Quality Service buildscript closure related lines need to be added to the root build.gradle file.
var rootGradleBuildFilePath = Path.Combine(path, "../build.gradle");
var shouldAddQualityServiceToDependencies = ShouldAddQualityServiceToDependencies(rootGradleBuildFilePath);
var failedToAddPlugin = false;
if (shouldAddQualityServiceToDependencies)
{
// Add the Quality Service Plugin to the dependencies block in the root build.gradle file
var buildScriptChangesAdded = AddQualityServiceBuildScriptLines(rootGradleBuildFilePath);
failedToAddPlugin = !buildScriptChangesAdded;
}
else
{
// Add the Quality Service Plugin to the plugin block in the root build.gradle file
var rootSettingsGradleFilePath = Path.Combine(path, "../settings.gradle");
var qualityServiceAdded = AddPluginToRootGradleBuildFile(rootGradleBuildFilePath);
var appLovinRepositoryAdded = AddAppLovinRepository(rootSettingsGradleFilePath);
failedToAddPlugin = !(qualityServiceAdded && appLovinRepositoryAdded);
}
var failedToAddPlugin = !AddQualityServiceToRootGradleFile(path);
if (failedToAddPlugin)
{
MaxSdkLogger.UserWarning("Failed to add AppLovin Quality Service plugin to the gradle project.");
@@ -52,12 +31,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// The plugin needs to be added to the application module (named launcher)
var applicationGradleBuildFilePath = Path.Combine(path, "../launcher/build.gradle");
#else
// If Gradle template is enabled, we would have already updated the plugin.
if (AppLovinIntegrationManager.GradleTemplateEnabled) return;
var applicationGradleBuildFilePath = Path.Combine(path, "build.gradle");
#endif
if (!File.Exists(applicationGradleBuildFilePath))
{
@@ -9,18 +9,14 @@
#if UNITY_IOS || UNITY_IPHONE
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using AppLovinMax.Internal;
using UnityEditor;
using UnityEditor.Callbacks;
#if UNITY_2019_3_OR_NEWER
using UnityEditor.iOS.Xcode.Extensions;
#endif
using UnityEditor.iOS.Xcode;
using UnityEditor.PackageManager;
using UnityEngine;
using UnityEngine.Networking;
@@ -36,9 +32,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
private const string OutputFileName = "AppLovinQualityServiceSetup.rb";
#if !UNITY_2019_3_OR_NEWER
private const string UnityMainTargetName = "Unity-iPhone";
#endif
// Use a priority of 90 to have AppLovin embed frameworks after Pods are installed (EDM finishes installing Pods at priority 60) and before Firebase Crashlytics runs their scripts (at priority 100).
private const int AppLovinEmbedFrameworksPriority = 90;
@@ -95,49 +88,40 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
return;
}
// Download the ruby script needed to install Quality Service
var downloadHandler = new DownloadHandlerFile(outputFilePath);
var postJson = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey);
var bodyRaw = Encoding.UTF8.GetBytes(postJson);
var uploadHandler = new UploadHandlerRaw(bodyRaw);
uploadHandler.contentType = "application/json";
using (var unityWebRequest = new UnityWebRequest("https://api2.safedk.com/v1/build/ios_setup2"))
var webRequestConfig = new WebRequestConfig()
{
unityWebRequest.method = UnityWebRequest.kHttpVerbPOST;
unityWebRequest.downloadHandler = downloadHandler;
unityWebRequest.uploadHandler = uploadHandler;
var operation = unityWebRequest.SendWebRequest();
DownloadHandler = new DownloadHandlerFile(outputFilePath),
JsonString = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey),
EndPoint = "https://api2.safedk.com/v1/build/ios_setup2",
RequestType = WebRequestType.Post,
};
// Wait for the download to complete or the request to timeout.
while (!operation.isDone) { }
webRequestConfig.Headers.Add("Content-Type", "application/json");
#if UNITY_2020_1_OR_NEWER
if (unityWebRequest.result != UnityWebRequest.Result.Success)
#else
if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
#endif
{
MaxSdkLogger.UserError("AppLovin Quality Service installation failed. Failed to download script with error: " + unityWebRequest.error);
return;
}
var maxWebRequest = new MaxWebRequest(webRequestConfig);
// Check if Ruby is installed
var rubyVersion = AppLovinCommandLine.Run("ruby", "--version", buildPath);
if (rubyVersion.ExitCode != 0)
{
MaxSdkLogger.UserError("AppLovin Quality Service installation requires Ruby. Please install Ruby, export it to your system PATH and re-export the project.");
return;
}
// Ruby is installed, run `ruby AppLovinQualityServiceSetup.rb`
var result = AppLovinCommandLine.Run("ruby", OutputFileName, buildPath);
// Check if we have an error.
if (result.ExitCode != 0) MaxSdkLogger.UserError("Failed to set up AppLovin Quality Service");
MaxSdkLogger.UserDebug(result.Message);
var webResponse = maxWebRequest.SendSync();
if (!webResponse.IsSuccess)
{
MaxSdkLogger.UserError("AppLovin Quality Service installation failed. Failed to download script with error: " + webResponse.ErrorMessage);
return;
}
// Check if Ruby is installed
var rubyVersion = AppLovinCommandLine.Run("ruby", "--version", buildPath);
if (rubyVersion.ExitCode != 0)
{
MaxSdkLogger.UserError("AppLovin Quality Service installation requires Ruby. Please install Ruby, export it to your system PATH and re-export the project.");
return;
}
// Ruby is installed, run `ruby AppLovinQualityServiceSetup.rb`
var result = AppLovinCommandLine.Run("ruby", OutputFileName, buildPath);
// Check if we have an error.
if (result.ExitCode != 0) MaxSdkLogger.UserError("Failed to set up AppLovin Quality Service");
MaxSdkLogger.UserDebug(result.Message);
}
[PostProcessBuild(AppLovinEmbedFrameworksPriority)]
@@ -147,13 +131,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var project = new PBXProject();
project.ReadFromFile(projectPath);
#if UNITY_2019_3_OR_NEWER
var unityMainTargetGuid = project.GetUnityMainTargetGuid();
var unityFrameworkTargetGuid = project.GetUnityFrameworkTargetGuid();
#else
var unityMainTargetGuid = project.TargetGuidByName(UnityMainTargetName);
var unityFrameworkTargetGuid = project.TargetGuidByName(UnityMainTargetName);
#endif
EmbedDynamicLibrariesIfNeeded(buildPath, project, unityMainTargetGuid);
LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionDe, "de", buildPath, project, unityMainTargetGuid);
@@ -180,23 +160,11 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var dynamicLibraryPathsToEmbed = GetDynamicLibraryPathsToEmbed(podsDirectory, buildPath);
if (dynamicLibraryPathsToEmbed == null || dynamicLibraryPathsToEmbed.Count == 0) return;
#if UNITY_2019_3_OR_NEWER
foreach (var dynamicLibraryPath in dynamicLibraryPathsToEmbed)
{
var fileGuid = project.AddFile(dynamicLibraryPath, dynamicLibraryPath);
project.AddFileToEmbedFrameworks(targetGuid, fileGuid);
}
#else
string runpathSearchPaths;
runpathSearchPaths = project.GetBuildPropertyForAnyConfig(targetGuid, "LD_RUNPATH_SEARCH_PATHS");
runpathSearchPaths += string.IsNullOrEmpty(runpathSearchPaths) ? "" : " ";
// Check if runtime search paths already contains the required search paths for dynamic libraries.
if (runpathSearchPaths.Contains("@executable_path/Frameworks")) return;
runpathSearchPaths += "@executable_path/Frameworks";
project.SetBuildProperty(targetGuid, "LD_RUNPATH_SEARCH_PATHS", runpathSearchPaths);
#endif
}
/// <summary>
@@ -316,13 +284,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var currentIosVersion = network.CurrentVersions.Ios;
if (string.IsNullOrEmpty(currentIosVersion)) return false;
var minIosVersion = libraryToEmbed.MinVersion;
var maxIosVersion = libraryToEmbed.MaxVersion;
var greaterThanOrEqualToMinVersion = string.IsNullOrEmpty(minIosVersion) || MaxSdkUtils.CompareVersions(currentIosVersion, minIosVersion) != MaxSdkUtils.VersionComparisonResult.Lesser;
var lessThanOrEqualToMaxVersion = string.IsNullOrEmpty(maxIosVersion) || MaxSdkUtils.CompareVersions(currentIosVersion, maxIosVersion) != MaxSdkUtils.VersionComparisonResult.Greater;
return greaterThanOrEqualToMinVersion && lessThanOrEqualToMaxVersion;
return MaxSdkUtils.IsVersionInRange(currentIosVersion, libraryToEmbed.MinVersion, libraryToEmbed.MaxVersion);
}
private static List<string> GetDynamicLibraryPathsInProjectToEmbed(string podsDirectory, List<string> dynamicLibrariesToEmbed)
@@ -573,11 +535,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var project = new PBXProject();
project.ReadFromFile(projectPath);
#if UNITY_2019_3_OR_NEWER
var unityMainTargetGuid = project.GetUnityMainTargetGuid();
#else
var unityMainTargetGuid = project.TargetGuidByName(UnityMainTargetName);
#endif
var guid = project.AddFile(AppLovinSettingsPlistFileName, AppLovinSettingsPlistFileName);
project.AddFileToBuild(unityMainTargetGuid, guid);
@@ -691,31 +649,28 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
uriBuilder.Query += string.Format("ad_networks={0}", adNetworks);
}
using (var unityWebRequest = UnityWebRequest.Get(uriBuilder.ToString()))
var webRequestConfig = new WebRequestConfig()
{
var operation = unityWebRequest.SendWebRequest();
// Wait for the download to complete or the request to timeout.
while (!operation.isDone) { }
EndPoint = uriBuilder.ToString()
};
#if UNITY_2020_1_OR_NEWER
if (unityWebRequest.result != UnityWebRequest.Result.Success)
#else
if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
#endif
{
MaxSdkLogger.UserError("Failed to retrieve SKAdNetwork IDs with error: " + unityWebRequest.error);
return new SkAdNetworkData();
}
var maxWebRequest = new MaxWebRequest(webRequestConfig);
var webResponse = maxWebRequest.SendSync();
try
{
return JsonUtility.FromJson<SkAdNetworkData>(unityWebRequest.downloadHandler.text);
}
catch (Exception exception)
{
MaxSdkLogger.UserError("Failed to parse data '" + unityWebRequest.downloadHandler.text + "' with exception: " + exception);
return new SkAdNetworkData();
}
if (!webResponse.IsSuccess)
{
MaxSdkLogger.UserError("Failed to retrieve SKAdNetwork IDs with error: " + webResponse.ErrorMessage);
return new SkAdNetworkData();
}
try
{
return JsonUtility.FromJson<SkAdNetworkData>(webResponse.ResponseMessage);
}
catch (Exception exception)
{
MaxSdkLogger.UserError("Failed to parse data '" + webResponse.ResponseMessage + "' with exception: " + exception);
return new SkAdNetworkData();
}
}
@@ -23,7 +23,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string ElementNameAndroidPackage = "androidPackage";
private const string AttributeNameSpec = "spec";
private const string UmpDependencyPackage = "com.google.android.ump:user-messaging-platform:";
private const string UmpDependencyVersion = "2.1.0";
private const string UmpDependencyVersion = "4.0.0";
public void OnPreprocessBuild(BuildReport report)
{
@@ -36,13 +36,9 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// We can only process gradle template file here. If it is not available, we will try again in post build on Unity IDEs newer than 2018_2 (see AppLovinPostProcessGradleProject).
if (!AppLovinIntegrationManager.GradleTemplateEnabled) return;
#if UNITY_2019_3_OR_NEWER
// The publisher could be migrating from older Unity versions to 2019_3 or newer.
// If so, we should delete the plugin from the template. The plugin will be added to the project's application module in the post processing script (AppLovinPostProcessGradleProject).
RemoveAppLovinQualityServiceOrSafeDkPlugin(AppLovinIntegrationManager.GradleTemplatePath);
#else
AddAppLovinQualityServicePlugin(AppLovinIntegrationManager.GradleTemplatePath);
#endif
}
private static void AddGoogleCmpDependencyIfNeeded()
@@ -26,7 +26,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string AttributeNameName = "name";
private const string AttributeNameVersion = "version";
private const string UmpDependencyPod = "GoogleUserMessagingPlatform";
private const string UmpDependencyVersion = "~> 2.1";
private const string UmpDependencyVersion = "~> 3.1";
private static void AddGoogleCmpDependencyIfNeeded()
{
@@ -6,16 +6,14 @@
#if UNITY_ANDROID
using System.Text;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using UnityEditorInternal;
using AppLovinMax.Internal;
using UnityEngine;
using UnityEngine.Networking;
using Debug = UnityEngine.Debug;
using UnityEngine.PlayerLoop;
namespace AppLovinMax.Scripts.IntegrationManager.Editor
{
@@ -58,6 +56,29 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private const string SafeDkLegacyMavenRepo = "http://download.safedk.com";
private const string SafeDkLegacyDependencyClassPath = "com.safedk:SafeDKGradlePlugin:";
/// <summary>
/// Adds the Quality Service plugin to the root gradle file.
/// </summary>
/// <param name="path">The path to the unityLibrary's module.</param>
/// <returns>True if the plugin was added successfully, otherwise return false</returns>
protected static bool AddQualityServiceToRootGradleFile(string path)
{
var rootGradleBuildFilePath = Path.Combine(path, "../build.gradle");
var shouldAddQualityServiceToDependencies = ShouldAddQualityServiceToDependencies(rootGradleBuildFilePath);
if (shouldAddQualityServiceToDependencies)
{
// Add the Quality Service Plugin to the dependencies block in the root build.gradle file
return AddQualityServiceBuildScriptLines(rootGradleBuildFilePath);
}
// Add the Quality Service Plugin to the plugin block in the root build.gradle file
var rootSettingsGradleFilePath = Path.Combine(path, "../settings.gradle");
var qualityServiceAdded = AddPluginToRootGradleBuildFile(rootGradleBuildFilePath);
var appLovinRepositoryAdded = AddAppLovinRepository(rootSettingsGradleFilePath);
return qualityServiceAdded && appLovinRepositoryAdded;
}
/// <summary>
/// Determines whether the AppLovin Quality Service plugin should be added to the
/// dependencies block in the root build.gradle file or to the plugins block.
@@ -83,7 +104,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
/// <param name="rootGradleBuildFile">The path to project's root build.gradle file.</param>
/// <returns><c>true</c> if the file contains a `dependencies` block, indicating an older Gradle version</returns>
protected static bool ShouldAddQualityServiceToDependencies(string rootGradleBuildFile)
private static bool ShouldAddQualityServiceToDependencies(string rootGradleBuildFile)
{
var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
return lines.Any(line => TokenBuildScriptDependencies.IsMatch(line));
@@ -119,11 +140,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
var outputLines = GenerateUpdatedBuildFileLines(
sanitizedLines,
apiKey,
#if UNITY_2019_3_OR_NEWER
false // On Unity 2019.3+, the buildscript closure related lines will to be added to the root build.gradle file.
#else
true
#endif
false // The buildscript closure related lines will to be added to the root build.gradle file.
);
// outputLines can be null if we couldn't add the plugin.
if (outputLines == null) return;
@@ -154,7 +171,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
/// <param name="rootGradleBuildFile">The path to project's root build.gradle file.</param>
/// <returns><c>true</c> when the plugin was added successfully.</returns>
protected bool AddPluginToRootGradleBuildFile(string rootGradleBuildFile)
private static bool AddPluginToRootGradleBuildFile(string rootGradleBuildFile)
{
var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
@@ -214,7 +231,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
/// <param name="settingsGradleFile">The path to the project's settings.gradle file.</param>
/// <returns><c>true</c> if the repository was added successfully.</returns>
protected bool AddAppLovinRepository(string settingsGradleFile)
private static bool AddAppLovinRepository(string settingsGradleFile)
{
var lines = File.ReadLines(settingsGradleFile).ToList();
var outputLines = new List<string>();
@@ -276,7 +293,6 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
return true;
}
#if UNITY_2019_3_OR_NEWER
/// <summary>
/// Adds the necessary AppLovin Quality Service dependency and maven repo lines to the provided root build.gradle file.
/// Sample build.gradle file after adding quality service:
@@ -297,7 +313,7 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
/// </summary>
/// <param name="rootGradleBuildFile">The root build.gradle file path</param>
/// <returns><c>true</c> if the build script lines were applied correctly.</returns>
protected bool AddQualityServiceBuildScriptLines(string rootGradleBuildFile)
private static bool AddQualityServiceBuildScriptLines(string rootGradleBuildFile)
{
var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
var outputLines = GenerateUpdatedBuildFileLines(lines, null, true);
@@ -339,46 +355,35 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
Console.WriteLine(exception);
}
}
#endif
private static AppLovinQualityServiceData RetrieveQualityServiceData(string sdkKey)
{
var postJson = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey);
var bodyRaw = Encoding.UTF8.GetBytes(postJson);
// Upload handler is automatically disposed when UnityWebRequest is disposed
var uploadHandler = new UploadHandlerRaw(bodyRaw);
uploadHandler.contentType = "application/json";
using (var unityWebRequest = new UnityWebRequest("https://api2.safedk.com/v1/build/cred"))
var webRequestConfig = new WebRequestConfig()
{
unityWebRequest.method = UnityWebRequest.kHttpVerbPOST;
unityWebRequest.uploadHandler = uploadHandler;
unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
JsonString = string.Format("{{\"sdk_key\" : \"{0}\"}}", sdkKey),
EndPoint = "https://api2.safedk.com/v1/build/cred",
RequestType = WebRequestType.Post,
};
var operation = unityWebRequest.SendWebRequest();
webRequestConfig.Headers.Add("Content-Type", "application/json");
// Wait for the download to complete or the request to timeout.
while (!operation.isDone) { }
var maxWebRequest = new MaxWebRequest(webRequestConfig);
var webResponse = maxWebRequest.SendSync();
#if UNITY_2020_1_OR_NEWER
if (unityWebRequest.result != UnityWebRequest.Result.Success)
#else
if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
#endif
{
MaxSdkLogger.UserError("Failed to retrieve API Key for SDK Key: " + sdkKey + "with error: " + unityWebRequest.error);
return new AppLovinQualityServiceData();
}
if (!webResponse.IsSuccess)
{
MaxSdkLogger.UserError("Failed to retrieve API Key for SDK Key: " + sdkKey + "with error: " + webResponse.ErrorMessage);
return new AppLovinQualityServiceData();
}
try
{
return JsonUtility.FromJson<AppLovinQualityServiceData>(unityWebRequest.downloadHandler.text);
}
catch (Exception exception)
{
MaxSdkLogger.UserError("Failed to parse API Key." + exception);
return new AppLovinQualityServiceData();
}
try
{
return JsonUtility.FromJson<AppLovinQualityServiceData>(webResponse.ResponseMessage);
}
catch (Exception exception)
{
MaxSdkLogger.UserError("Failed to parse API Key." + exception);
return new AppLovinQualityServiceData();
}
}
@@ -445,7 +450,13 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
private static List<string> GenerateUpdatedBuildFileLines(List<string> lines, string apiKey, bool addBuildScriptLines)
{
var addPlugin = MaxSdkUtils.IsValidString(apiKey);
// Check if the plugin exists, if so, update the SDK Key.
var pluginExists = lines.Any(line => TokenAppLovinPlugin.IsMatch(line));
return pluginExists ? UpdateExistingPlugin(lines, apiKey) : AddPluginAndBuildScript(lines, apiKey, addBuildScriptLines);
}
private static List<string> UpdateExistingPlugin(List<string> lines, string apiKey)
{
// A sample of the template file.
// ...
// allprojects {
@@ -466,149 +477,162 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
// **DEPS**}
// ...
var outputLines = new List<string>();
// Check if the plugin exists, if so, update the SDK Key.
var pluginExists = lines.Any(line => TokenAppLovinPlugin.IsMatch(line));
if (pluginExists)
var pluginMatched = false;
var insideAppLovinClosure = false;
var updatedApiKey = false;
var mavenRepoUpdated = false;
var dependencyClassPathUpdated = false;
foreach (var line in lines)
{
var pluginMatched = false;
var insideAppLovinClosure = false;
var updatedApiKey = false;
var mavenRepoUpdated = false;
var dependencyClassPathUpdated = false;
foreach (var line in lines)
// Bintray maven repo is no longer being used. Update to s3 maven repo with regex check
if (!mavenRepoUpdated && (line.Contains(QualityServiceBintrayMavenRepo) || line.Contains(QualityServiceNoRegexMavenRepo)))
{
// Bintray maven repo is no longer being used. Update to s3 maven repo with regex check
if (!mavenRepoUpdated && (line.Contains(QualityServiceBintrayMavenRepo) || line.Contains(QualityServiceNoRegexMavenRepo)))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
mavenRepoUpdated = true;
continue;
}
// We no longer use version specific dependency class path. Just use + for version to always pull the latest.
if (!dependencyClassPathUpdated && line.Contains(QualityServiceDependencyClassPathV3))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceDependencyClassPath));
dependencyClassPathUpdated = true;
continue;
}
if (!pluginMatched && line.Contains(QualityServicePlugin))
{
insideAppLovinClosure = true;
pluginMatched = true;
}
if (insideAppLovinClosure && line.Contains("}"))
{
insideAppLovinClosure = false;
}
// Update the API key.
if (insideAppLovinClosure && !updatedApiKey && TokenApiKey.IsMatch(line))
{
outputLines.Add(string.Format(QualityServiceApiKey, apiKey));
updatedApiKey = true;
}
// Keep adding the line until we find and update the plugin.
else
{
outputLines.Add(line);
}
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
mavenRepoUpdated = true;
continue;
}
}
// Plugin hasn't been added yet, add it.
else
{
var buildScriptClosureDepth = 0;
var insideBuildScriptClosure = false;
var buildScriptMatched = false;
var qualityServiceRepositoryAdded = false;
var qualityServiceDependencyClassPathAdded = false;
var qualityServicePluginAdded = false;
foreach (var line in lines)
// We no longer use version specific dependency class path. Just use + for version to always pull the latest.
if (!dependencyClassPathUpdated && line.Contains(QualityServiceDependencyClassPathV3))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceDependencyClassPath));
dependencyClassPathUpdated = true;
continue;
}
if (!pluginMatched && line.Contains(QualityServicePlugin))
{
insideAppLovinClosure = true;
pluginMatched = true;
}
if (insideAppLovinClosure && line.Contains("}"))
{
insideAppLovinClosure = false;
}
// Update the API key.
if (insideAppLovinClosure && !updatedApiKey && TokenApiKey.IsMatch(line))
{
outputLines.Add(string.Format(QualityServiceApiKey, apiKey));
updatedApiKey = true;
}
// Keep adding the line until we find and update the plugin.
else
{
// Add the line to the output lines.
outputLines.Add(line);
// Check if we need to add the build script lines and add them.
if (addBuildScriptLines)
{
if (!buildScriptMatched && line.Contains(BuildScriptMatcher))
{
buildScriptMatched = true;
insideBuildScriptClosure = true;
}
// Match the parenthesis to track if we are still inside the buildscript closure.
if (insideBuildScriptClosure)
{
if (line.Contains("{"))
{
buildScriptClosureDepth++;
}
if (line.Contains("}"))
{
buildScriptClosureDepth--;
}
if (buildScriptClosureDepth == 0)
{
insideBuildScriptClosure = false;
// There may be multiple buildscript closures and we need to keep looking until we added both the repository and classpath.
buildScriptMatched = qualityServiceRepositoryAdded && qualityServiceDependencyClassPathAdded;
}
}
if (insideBuildScriptClosure)
{
// Add the build script dependency repositories.
if (!qualityServiceRepositoryAdded && TokenBuildScriptRepositories.IsMatch(line))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
qualityServiceRepositoryAdded = true;
}
// Add the build script dependencies.
else if (!qualityServiceDependencyClassPathAdded && TokenBuildScriptDependencies.IsMatch(line))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceDependencyClassPath));
qualityServiceDependencyClassPathAdded = true;
}
}
}
// Check if we need to add the plugin and add it.
if (addPlugin)
{
// Add the plugin.
if (!qualityServicePluginAdded && TokenApplicationPlugin.IsMatch(line))
{
outputLines.Add(QualityServiceApplyPlugin);
outputLines.AddRange(GenerateAppLovinPluginClosure(apiKey));
qualityServicePluginAdded = true;
}
}
}
if ((addBuildScriptLines && (!qualityServiceRepositoryAdded || !qualityServiceDependencyClassPathAdded)) || (addPlugin && !qualityServicePluginAdded))
{
return null;
}
}
return outputLines;
}
private static List<string> AddPluginAndBuildScript(List<string> lines, string apiKey, bool addBuildScriptLines)
{
var shouldAddPlugin = MaxSdkUtils.IsValidString(apiKey);
if (shouldAddPlugin)
{
lines = AddPlugin(lines, apiKey);
if (lines == null) return null;
}
if (!addBuildScriptLines) return lines;
lines = AddBuildScript(lines);
return lines;
}
private static List<string> AddBuildScript(List<string> lines)
{
var outputLines = new List<string>();
var buildScriptClosureDepth = 0;
var insideBuildScriptClosure = false;
var buildScriptMatched = false;
var qualityServiceRepositoryAdded = false;
var qualityServiceDependencyClassPathAdded = false;
foreach (var line in lines)
{
// Add the line to the output lines.
outputLines.Add(line);
if (!buildScriptMatched && line.Contains(BuildScriptMatcher))
{
buildScriptMatched = true;
insideBuildScriptClosure = true;
}
// Match the parenthesis to track if we are still inside the buildscript closure.
if (insideBuildScriptClosure)
{
if (line.Contains("{"))
{
buildScriptClosureDepth++;
}
if (line.Contains("}"))
{
buildScriptClosureDepth--;
}
if (buildScriptClosureDepth == 0)
{
insideBuildScriptClosure = false;
// There may be multiple buildscript closures and we need to keep looking until we added both the repository and classpath.
buildScriptMatched = qualityServiceRepositoryAdded && qualityServiceDependencyClassPathAdded;
}
}
if (insideBuildScriptClosure)
{
// Add the build script dependency repositories.
if (!qualityServiceRepositoryAdded && TokenBuildScriptRepositories.IsMatch(line))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
qualityServiceRepositoryAdded = true;
}
// Add the build script dependencies.
else if (!qualityServiceDependencyClassPathAdded && TokenBuildScriptDependencies.IsMatch(line))
{
outputLines.Add(GetFormattedBuildScriptLine(QualityServiceDependencyClassPath));
qualityServiceDependencyClassPathAdded = true;
}
}
}
if (!qualityServiceRepositoryAdded || !qualityServiceDependencyClassPathAdded)
{
return null;
}
return outputLines;
}
private static List<string> AddPlugin(List<string> lines, string apiKey)
{
var outputLines = new List<string>();
var qualityServicePluginAdded = false;
foreach (var line in lines)
{
outputLines.Add(line);
// Add the plugin.
if (qualityServicePluginAdded || !TokenApplicationPlugin.IsMatch(line)) continue;
outputLines.Add(QualityServiceApplyPlugin);
outputLines.AddRange(GenerateAppLovinPluginClosure(apiKey));
qualityServicePluginAdded = true;
}
return qualityServicePluginAdded ? outputLines : null;
}
public static string GetFormattedBuildScriptLine(string buildScriptLine)
{
#if UNITY_2022_2_OR_NEWER
return " "
#elif UNITY_2019_3_OR_NEWER
return " "
#else
return " "
return " "
#endif
+ buildScriptLine;
}
@@ -23,7 +23,7 @@ public class AppLovinSettings : ScriptableObject
{
private const string SettingsExportPath = "MaxSdk/Resources/AppLovinSettings.asset";
private static AppLovinSettings _instance;
private static AppLovinSettings instance;
[SerializeField] private bool qualityServiceEnabled = true;
[SerializeField] private string sdkKey;
@@ -41,7 +41,7 @@ public class AppLovinSettings : ScriptableObject
{
get
{
if (_instance == null)
if (instance == null)
{
// Check for an existing AppLovinSettings somewhere in the project
var guids = AssetDatabase.FindAssets("AppLovinSettings t:ScriptableObject");
@@ -53,20 +53,19 @@ public class AppLovinSettings : ScriptableObject
if (guids.Length != 0)
{
var path = AssetDatabase.GUIDToAssetPath(guids[0]);
_instance = AssetDatabase.LoadAssetAtPath<AppLovinSettings>(path);
return _instance;
instance = AssetDatabase.LoadAssetAtPath<AppLovinSettings>(path);
return instance;
}
// If there is no existing AppLovinSettings asset, create one in the default location
string settingsFilePath;
// The settings file should be under the Assets/ folder so that it can be version controlled and cannot be overriden when updating.
// If the plugin is outside the Assets folder, create the settings asset at the default location.
// If the plugin is outside the Assets folder or if there is no existing AppLovinSettings asset, create the settings asset at the default location.
if (AppLovinIntegrationManager.IsPluginInPackageManager)
{
// Note: Can't use absolute path when calling `CreateAsset`. Should use relative path to Assets/ directory.
settingsFilePath = Path.Combine("Assets", SettingsExportPath);
// Note: Can't use absolute path when calling `CreateAsset`. Should use path relative to Assets/ directory.
settingsFilePath = MaxSdkUtils.NormalizeToUnityPath(Path.Combine("Assets", SettingsExportPath));
var maxSdkDir = Path.Combine(Application.dataPath, "MaxSdk");
var maxSdkDir = MaxSdkUtils.NormalizeToUnityPath(Path.Combine(Application.dataPath, "MaxSdk"));
if (!Directory.Exists(maxSdkDir))
{
Directory.CreateDirectory(maxSdkDir);
@@ -74,7 +73,7 @@ public class AppLovinSettings : ScriptableObject
}
else
{
settingsFilePath = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, SettingsExportPath);
settingsFilePath = MaxSdkUtils.NormalizeToUnityPath(Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, SettingsExportPath));
}
var settingsDir = Path.GetDirectoryName(settingsFilePath);
@@ -86,13 +85,13 @@ public class AppLovinSettings : ScriptableObject
// On script reload AssetDatabase.FindAssets() can fail and will overwrite AppLovinSettings without this check
if (!File.Exists(settingsFilePath))
{
_instance = CreateInstance<AppLovinSettings>();
AssetDatabase.CreateAsset(_instance, settingsFilePath);
instance = CreateInstance<AppLovinSettings>();
AssetDatabase.CreateAsset(instance, settingsFilePath);
MaxSdkLogger.D("Creating new AppLovinSettings asset at path: " + settingsFilePath);
}
}
return _instance;
return instance;
}
}
@@ -155,6 +154,6 @@ public class AppLovinSettings : ScriptableObject
/// </summary>
public void SaveAsync()
{
EditorUtility.SetDirty(_instance);
EditorUtility.SetDirty(instance);
}
}
@@ -1,4 +1,3 @@
#if UNITY_2019_2_OR_NEWER
using System;
using System.Collections.Generic;
using System.IO;
@@ -189,4 +188,3 @@ namespace AppLovinMax.Scripts.IntegrationManager.Editor
#endregion
}
}
#endif
+1 -1
View File
@@ -14,7 +14,7 @@ public class MaxSdk :
MaxSdkUnityEditor
#endif
{
private const string _version = "8.3.1";
private const string _version = "8.6.2";
/// <summary>
/// Returns the current plugin version.
+3 -1
View File
@@ -812,7 +812,9 @@ public class MaxSdkAndroid : MaxSdkBase
/// <param name="parameters">A dictionary containing key-value pairs further describing this event.</param>
public static void TrackEvent(string name, IDictionary<string, string> parameters = null)
{
MaxUnityPluginClass.CallStatic("trackEvent", name, Json.Serialize(parameters));
// Convert null to "{}" to avoid Unity sending the literal "null" to Android.
var jsonString = ( parameters == null ) ? EmptyJson : Json.Serialize(parameters);
MaxUnityPluginClass.CallStatic("trackEvent", name, jsonString);
}
#endregion
+2
View File
@@ -14,6 +14,8 @@ using System.Runtime.InteropServices;
public abstract class MaxSdkBase
{
protected const string EmptyJson = "{}";
/// <summary>
/// This enum represents the user's geography used to determine the type of consent flow shown to the user.
/// </summary>
+1 -1
View File
@@ -1082,7 +1082,7 @@ public static class MaxSdkCallbacks
MaxSdkLogger.D("Listener has been removed from callback: " + eventName);
}
#if UNITY_EDITOR && UNITY_2019_2_OR_NEWER
#if UNITY_EDITOR
/// <summary>
/// Resets static event handlers so they still get reset even if Domain Reloading is disabled
/// </summary>
+3 -3
View File
@@ -23,7 +23,7 @@ public class MaxSdkLogger
/// </summary>
public static void D(string message)
{
if (MaxSdk.DisableAllLogs && !MaxSdk.IsVerboseLoggingEnabled()) return;
if (MaxSdk.DisableAllLogs || !MaxSdk.IsVerboseLoggingEnabled()) return;
Debug.Log("Debug [" + SdkTag + "] " + message);
}
@@ -45,7 +45,7 @@ public class MaxSdkLogger
/// </summary>
public static void W(string message)
{
if (MaxSdk.DisableAllLogs && !MaxSdk.IsVerboseLoggingEnabled()) return;
if (MaxSdk.DisableAllLogs || !MaxSdk.IsVerboseLoggingEnabled()) return;
Debug.LogWarning("Warning [" + SdkTag + "] " + message);
}
@@ -67,7 +67,7 @@ public class MaxSdkLogger
/// </summary>
public static void E(string message)
{
if (MaxSdk.DisableAllLogs && !MaxSdk.IsVerboseLoggingEnabled()) return;
if (MaxSdk.DisableAllLogs || !MaxSdk.IsVerboseLoggingEnabled()) return;
Debug.LogError("Error [" + SdkTag + "] " + message);
}
+30 -2
View File
@@ -12,7 +12,7 @@ using UnityEditor;
#endif
public class MaxSdkUtils
public static class MaxSdkUtils
{
/// <summary>
/// An Enum to be used when comparing two versions.
@@ -488,6 +488,13 @@ public class MaxSdkUtils
}
}
public static bool IsVersionInRange(string currentVersion, string minVersion, string maxVersion)
{
var greaterThanOrEqualToMin = string.IsNullOrEmpty(minVersion) || MaxSdkUtils.CompareVersions(currentVersion, minVersion) != MaxSdkUtils.VersionComparisonResult.Lesser;
var lessThanOrEqualToMax = string.IsNullOrEmpty(maxVersion) || MaxSdkUtils.CompareVersions(currentVersion, maxVersion) != MaxSdkUtils.VersionComparisonResult.Greater;
return greaterThanOrEqualToMin && lessThanOrEqualToMax;
}
/// <summary>
/// Compares its two arguments for order. Returns <see cref="VersionComparisonResult.Lesser"/>, <see cref="VersionComparisonResult.Equal"/>,
/// or <see cref="VersionComparisonResult.Greater"/> as the first version is less than, equal to, or greater than the second.
@@ -573,6 +580,16 @@ public class MaxSdkUtils
return !string.IsNullOrEmpty(toCheck);
}
/// <summary>
/// Check if the given array is null or empty.
/// </summary>
/// <param name="array">The array to be checked.</param>
/// <returns><c>true</c> if the given array is <c>null</c> or has zero length.</returns>
public static bool IsNullOrEmpty<T>(T[] array)
{
return array == null || array.Length == 0;
}
#if UNITY_EDITOR
/// <summary>
/// Gets the path of the asset in the project for a given MAX plugin export path.
@@ -581,7 +598,7 @@ public class MaxSdkUtils
/// <returns>The exported path of the MAX plugin asset or the default export path if the asset is not found.</returns>
public static string GetAssetPathForExportPath(string exportPath)
{
var assetLabelToFind = "al_max_export_path-" + exportPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
var assetLabelToFind = "al_max_export_path-" + NormalizeToUnityPath(exportPath);
var labelSearchQuery = "l:" + assetLabelToFind;
var assetGuids = AssetDatabase.FindAssets(labelSearchQuery);
@@ -601,5 +618,16 @@ public class MaxSdkUtils
// Fall back to the default path if no exact label match is found
return Path.Combine("Assets", exportPath);
}
/// <summary>
/// Turns a path into a Unity compatible path by replacing backslashes with forward slashes.
/// This is important when dealing with Unity's AssetDatabase, which expects paths to use forward slashes.
/// </summary>
/// <param name="path">The path to normalize</param>
/// <returns>A Unity compatible normalized path with only forward slashes.</returns>
public static string NormalizeToUnityPath(string path)
{
return path.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
}
#endif
}
+3 -1
View File
@@ -1027,7 +1027,9 @@ public class MaxSdkiOS : MaxSdkBase
/// <param name="parameters">A dictionary containing key-value pairs further describing this event.</param>
public static void TrackEvent(string name, IDictionary<string, string> parameters = null)
{
_MaxTrackEvent(name, Json.Serialize(parameters));
// Convert null to "{}" to avoid Unity sending the literal "null" to iOS.
var jsonString = ( parameters == null ) ? EmptyJson : Json.Serialize(parameters);
_MaxTrackEvent(name, jsonString);
}
#endregion
@@ -1 +0,0 @@
// This file has been deprecated and will be removed in a future plugin release.
@@ -1,14 +0,0 @@
fileFormatVersion: 2
guid: 409fe14211f31433da09f5b4bd5467b9
labels:
- al_max
- al_max_export_path-MaxSdk/Scripts/MaxTargetingData.cs
MonoImporter:
externalObjects: {}
serializedVersion: 2
defaultReferences: []
executionOrder: 0
icon: {instanceID: 0}
userData:
assetBundleName:
assetBundleVariant:
-1
View File
@@ -1 +0,0 @@
// This file has been deprecated and will be removed in a future plugin release.
+319
View File
@@ -0,0 +1,319 @@
//
// MaxWebRequest.cs
// AppLovin MAX Unity Plugin
//
// Created by Jonathan Liu on 6/10/2025.
// Copyright © 2025 AppLovin. All rights reserved.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.Networking;
using AppLovinMax.ThirdParty.MiniJson;
namespace AppLovinMax.Internal
{
public enum WebRequestType
{
Get,
Post
}
public class WebRequestConfig
{
/// <summary>
/// Request endpoint. Task will not execute if one is not set.
/// </summary>
public string EndPoint { get; set; }
/// <summary>
/// Request method. GET is used by default.
/// </summary>
public WebRequestType RequestType { get; set; } = WebRequestType.Get;
/// <summary>
/// The download handler for the web request.
/// </summary>
public DownloadHandler DownloadHandler { get; set; } = new DownloadHandlerBuffer();
/// <summary>
/// Parameters that will be attached to the request.
/// </summary>
public Dictionary<string, string> QueryParams { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Headers that will be added to the request.
/// </summary>
public Dictionary<string, string> Headers { get; set; } = new Dictionary<string, string>();
/// <summary>
/// Request message data that will be sent with the request.
/// If both <see cref="Data"/> and <see cref="JsonString"/> are set, <see cref="Data"/> takes precedence and will be serialized to JSON.
/// </summary>
public object Data { get; set; } = null;
/// <summary>
/// Request message data in JSON format that will be sent with the request.
/// If both <see cref="Data"/> and <see cref="JsonString"/> are set, <see cref="Data"/> takes precedence and will be serialized to JSON.
/// </summary>
public string JsonString { get; set; } = "";
/// <summary>
/// The max number of attempts to make the web request before stopping.
/// </summary>
public int MaxRequestAttempts { get; set; } = 3;
/// <summary>
/// Timeout in seconds
/// </summary>
public int TimeoutSeconds { get; set; } = 60;
}
public class WebResponse
{
/// <summary>
/// Whether the request succeeded.
/// </summary>
public bool IsSuccess { get; } = false;
/// <summary>
/// The completed UnityWebRequest.
/// </summary>
public string ResponseMessage { get; } = "";
/// <summary>
/// The error message if the request failed.
/// </summary>
public string ErrorMessage { get; } = "";
public WebResponse(UnityWebRequest request)
{
if (request == null) return;
#if UNITY_2020_1_OR_NEWER
IsSuccess = request.result == UnityWebRequest.Result.Success;
#else
IsSuccess = !(request.isNetworkError || request.isHttpError);
#endif
// Only DownloadHandlerBuffer should try to access the text
if (request.downloadHandler is DownloadHandlerBuffer)
{
ResponseMessage = request.downloadHandler.text;
}
ErrorMessage = request.error;
}
}
public class MaxWebRequest
{
private const int WaitBetweenRetriesSeconds = 1;
private readonly WebRequestConfig webRequestConfig;
private UnityWebRequest webRequest;
private bool isSending;
public MaxWebRequest(WebRequestConfig config)
{
if (config == null)
{
MaxSdkLogger.E("WebRequestConfig cannot be null. Please provide a valid configuration.");
return;
}
webRequestConfig = config;
}
/// <summary>
/// Sends a web request using coroutines.
/// </summary>
/// <param name="callback">
/// A callback invoked with the resulting <see cref="WebResponse"/> object.
/// </param>
public IEnumerator Send(Action<WebResponse> callback)
{
yield return SendInternal(
request => request.SendAndWait(),
response => { callback?.Invoke(response); });
}
/// <summary>
/// Sends a web request synchronously and returns the response.
/// </summary>
/// <returns>Returns a <see cref="WebResponse"/> object.</returns>
public WebResponse SendSync()
{
var finalResponse = new WebResponse(null);
SendInternal(
waitFunc: request =>
{
request.SendWebRequest();
while (!request.isDone) { } // Block until the request is done
return null; // We don't use IEnumerator for sync version
},
onComplete: response => finalResponse = response
).MoveNext(); // Needed to start the loop (since it's still an IEnumerator)
return finalResponse;
}
public void Abort()
{
if (webRequest != null && !webRequest.isDone)
{
webRequest.Abort();
}
}
/// <summary>
/// Sends a web request using the current WebRequestConfig with automatic retries on failure.
/// </summary>
/// <param name="waitFunc">
/// The function to use for waiting on the web request to complete.
/// </param>
/// /// <param name="onComplete">
/// The callback invoked when the web request completes, with a <see cref="WebResponse"/> object containing the result.
/// </param>
private IEnumerator SendInternal(Func<UnityWebRequest, IEnumerator> waitFunc, Action<WebResponse> onComplete)
{
if (isSending || string.IsNullOrEmpty(webRequestConfig.EndPoint))
{
var errorString = isSending ? "Web Request currently being sent. Please send another request after the current one has finished." : "Web request endpoint is null or empty.";
MaxSdkLogger.E(errorString);
onComplete(new WebResponse(null));
}
isSending = true;
try
{
for (var attempt = 1; attempt <= webRequestConfig.MaxRequestAttempts; attempt++)
{
using (var request = CreateWebRequest())
{
// Hold a reference to the request so we can Abort the request if needed.
webRequest = request;
var wait = waitFunc(request);
if (wait != null)
yield return wait;
var webResponse = new WebResponse(request);
if (webResponse.IsSuccess)
{
onComplete(webResponse);
yield break;
}
if (attempt < webRequestConfig.MaxRequestAttempts)
{
MaxSdkLogger.UserWarning($"Error: {request.error}, Attempt {attempt} failed... Retrying request");
}
else
{
// All attempts have failed. Send error callback.
MaxSdkLogger.UserError($"Failed to make web request after {webRequestConfig.MaxRequestAttempts} attempts.");
onComplete(webResponse);
}
}
yield return new WaitForSeconds(WaitBetweenRetriesSeconds);
}
}
finally
{
webRequest = null;
isSending = false;
}
}
/// <summary>
/// Creates and returns a web request using the given configuration.
/// </summary>
/// <returns>Returns the web request that was created using the instance's WebRequestConfiguration.</returns>
private UnityWebRequest CreateWebRequest()
{
var url = BuildURL();
var request = new UnityWebRequest(url, webRequestConfig.RequestType.ToHttpMethodString())
{
downloadHandler = webRequestConfig.DownloadHandler,
timeout = webRequestConfig.TimeoutSeconds
};
// Set request upload data if needed
if (webRequestConfig.Data != null || MaxSdkUtils.IsValidString(webRequestConfig.JsonString))
{
var jsonString = webRequestConfig.Data != null ? Json.Serialize(webRequestConfig.Data) : webRequestConfig.JsonString;
var rawData = Encoding.UTF8.GetBytes(jsonString);
request.uploadHandler = new UploadHandlerRaw(rawData);
}
// Set request headers
foreach (var header in webRequestConfig.Headers)
{
request.SetRequestHeader(header.Key, header.Value);
}
return request;
}
/// <summary>
/// Builds a URL with the endpoint and query parameters from the instance's WebRequestConfiguration.
/// </summary>
/// <returns>Returns a formatted URL built using the endpoint and query parameters.</returns>
private string BuildURL()
{
if (webRequestConfig.QueryParams.Count == 0) return webRequestConfig.EndPoint;
var uriBuilder = new UriBuilder(webRequestConfig.EndPoint);
uriBuilder.Query = webRequestConfig.QueryParams.ToQueryString();
return uriBuilder.ToString();
}
}
public static class MaxWebRequestExtension
{
internal static IEnumerator SendAndWait(this UnityWebRequest request)
{
#if UNITY_EDITOR
var operation = request.SendWebRequest();
// In the Unity Editor, `yield return request.SendWebRequest()` fails, so we manually poll `isDone` in a loop.
while (!operation.isDone) yield return new WaitForSeconds(0.1f);
#else
yield return request.SendWebRequest();
#endif
}
internal static string ToHttpMethodString(this WebRequestType type)
{
switch (type)
{
case WebRequestType.Get:
return "GET";
case WebRequestType.Post:
return "POST";
default:
return "GET";
}
}
internal static string ToQueryString(this Dictionary<string, string> queries)
{
var queryBuilder = new StringBuilder();
foreach (var query in queries)
{
if (query.Key == null || query.Value == null) continue;
queryBuilder.Append(queryBuilder.Length == 0 ? "?" : "&");
queryBuilder.AppendFormat("{0}={1}", Uri.EscapeDataString(query.Key), Uri.EscapeDataString(query.Value));
}
return queryBuilder.ToString();
}
}
}
@@ -1,8 +1,8 @@
fileFormatVersion: 2
guid: 8fb1f6f31b19c407b9d3fa62557d373a
guid: 9f1b03fdc97184fb98b605fe27346916
labels:
- al_max
- al_max_export_path-MaxSdk/Scripts/MaxUserSegment.cs
- al_max_export_path-MaxSdk/Scripts/MaxWebRequest.cs
MonoImporter:
externalObjects: {}
serializedVersion: 2