diff --git a/Assets/ExternalDependencyManager.meta b/Assets/ExternalDependencyManager.meta
new file mode 100644
index 0000000..767f704
--- /dev/null
+++ b/Assets/ExternalDependencyManager.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: e757d2dc930dc4d66890aa6fa69bcbe0
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor.meta b/Assets/ExternalDependencyManager/Editor.meta
new file mode 100644
index 0000000..9f56dbb
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8f38e3ab3aa7d44f7922af4e7191babd
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182.meta b/Assets/ExternalDependencyManager/Editor/1.2.182.meta
new file mode 100644
index 0000000..1f95e45
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8d52f69a8c2a84a318ba721236909840
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.dll b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.dll
new file mode 100755
index 0000000..0b15851
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.dll differ
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.dll.meta b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.dll.meta
new file mode 100644
index 0000000..39bf8db
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.dll.meta
@@ -0,0 +1,38 @@
+fileFormatVersion: 2
+guid: 3429646f5d4949efa347230fa86778b7
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.dll
+- gvhp_targets-editor
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 1
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Windows Store Apps: WindowsStoreApps
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.pdb b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.pdb
new file mode 100755
index 0000000..ce3b2b6
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.pdb differ
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.pdb.meta b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.pdb.meta
new file mode 100644
index 0000000..8aabdd7
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.pdb.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 51b6f287d0d540049f299c19a2f62321
+labels:
+- gvh
+- gvh_rename_to_disable
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.pdb
+- gvhp_targets-editor
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.dll b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.dll
new file mode 100755
index 0000000..d9a77eb
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.dll differ
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.dll.meta b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.dll.meta
new file mode 100644
index 0000000..68071ee
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.dll.meta
@@ -0,0 +1,38 @@
+fileFormatVersion: 2
+guid: 5b1a2b9a8d0748609fa3195265844d21
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.dll
+- gvhp_targets-editor
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 1
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Windows Store Apps: WindowsStoreApps
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.pdb b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.pdb
new file mode 100755
index 0000000..6af81c7
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.pdb differ
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.pdb.meta b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.pdb.meta
new file mode 100644
index 0000000..cd9a498
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.pdb.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 50924eb6369047da88d4803f207075b7
+labels:
+- gvh
+- gvh_rename_to_disable
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.pdb
+- gvhp_targets-editor
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.dll b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.dll
new file mode 100755
index 0000000..3c92ed5
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.dll differ
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.dll.meta b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.dll.meta
new file mode 100644
index 0000000..b531847
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.dll.meta
@@ -0,0 +1,38 @@
+fileFormatVersion: 2
+guid: 4026c19ba8ec495f93f5919b5f2934ee
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.dll
+- gvhp_targets-editor
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 1
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Windows Store Apps: WindowsStoreApps
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.pdb b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.pdb
new file mode 100755
index 0000000..0870f75
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.pdb differ
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.pdb.meta b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.pdb.meta
new file mode 100644
index 0000000..47ebe5e
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.pdb.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: fda6a96d9a064226b6cea47e6eef3d16
+labels:
+- gvh
+- gvh_rename_to_disable
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.pdb
+- gvhp_targets-editor
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.dll b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.dll
new file mode 100755
index 0000000..53c60b2
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.dll differ
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.dll.meta b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.dll.meta
new file mode 100644
index 0000000..8001971
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.dll.meta
@@ -0,0 +1,38 @@
+fileFormatVersion: 2
+guid: 025280fbe75c450fbdf7c5fc7ecc8860
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.dll
+- gvhp_targets-editor
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 1
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Windows Store Apps: WindowsStoreApps
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.pdb b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.pdb
new file mode 100755
index 0000000..00ed433
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.pdb differ
diff --git a/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.pdb.meta b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.pdb.meta
new file mode 100644
index 0000000..b176c8c
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.pdb.meta
@@ -0,0 +1,13 @@
+fileFormatVersion: 2
+guid: 5f7e0d6d612a4e18be5161653a63d2f0
+labels:
+- gvh
+- gvh_rename_to_disable
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.pdb
+- gvhp_targets-editor
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/CHANGELOG.md b/Assets/ExternalDependencyManager/Editor/CHANGELOG.md
new file mode 100755
index 0000000..eef4da0
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/CHANGELOG.md
@@ -0,0 +1,1411 @@
+# Version 1.2.182 - Aug 2, 2024
+* General - Check for gradle version instead of Unity version when determining
+ the template files to modify.
+
+# Version 1.2.181 - Jun 26, 2024
+* General - Disable `EditorMeasurement` reporting that relied on the
+ Measurement Protocol for Universal Analytics.
+
+# Version 1.2.180 - Jun 4, 2024
+* General - Fix project settings resetting on domain reload.
+ Fixes #524
+
+# Version 1.2.179 - Feb 12, 2024
+* Android Resolver - Added logic to automatically turn on `mainTemplate.gradle`
+ for new projects, and prompt users to enable it on projects that have previously
+ had the resolver run.
+
+# Version 1.2.178 - Dec 20, 2023
+* Added [OpenUPM support](https://openupm.com/packages/com.google.external-dependency-manager/).
+
+# Version 1.2.177 - Aug 14, 2023
+* iOS Resolver - Added `/opt/homebrew/bin` to Cocoapod executable search path.
+ Fixes #627
+
+# Version 1.2.176 - Apr 27, 2023
+* Android Resolver - Added two Android Resolver settings to determine whether
+ EDM4U injects custom local Maven repo path as a relative path or full path.
+ Fixes #537
+* Android Resolver - Inject Maven Repo to `settingTemplate.gradle` from
+ Unity 2022.2+
+ Fixes #594
+* Android Resolver - Jetifier option is enabled by default now.
+* Android Resolver - `Explode Aar` option applies to all cases, whether the
+ project will be exported or not.
+ Fixes #584
+ Fixes #287
+
+# Version 1.2.175 - Nov 16, 2022
+* General - Added tvOS podfile support to the iOS resolver.
+
+# Version 1.2.174 - Oct 06, 2022
+* General - Added tvOS support to the iOS resolver.
+* General - Fixed #484 - Changed `EditorMeasurement` to use secure connection.
+* Android Resolver - Fixed Android Resolver unable to resolve
+ `mainTemplate.gradle` in Unity `2022.2+` or `2023.1+`.
+
+# Version 1.2.173 - Sep 28, 2022
+* General - Added tvOS library support to the export unity package scripts.
+
+# Version 1.2.172 - Jun 23, 2022
+* iOS Resolver - Stop forcing `ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES` to `YES`,
+ which seems to cause problem for some when submitting apps. See #526 for more
+ information.
+
+# Version 1.2.171 - May 11, 2022
+* iOS Resolver - Change `Enable Swift Framework Support Workaround` setting to
+ be `ON` by default since more pods are using Swift Framework now.
+
+# Version 1.2.170 - Apr 4, 2022
+* Android Resolver - Fixes #498 - Fix the path separator of the Maven repo
+ injected to `mainTemplate.gradle`.
+* iOS Resolver - Fixes #470 - Switch default Cocoapods master repo from Github
+ to CDN.
+* iOS Resolver - `Link Framework Statically` setting is now default to `true`.
+ That is, `use_frameworks! :linkage => static` will be added to `Podfile` by
+ default instead of `use_frameworks!`. This can be changed in iOS Resolver
+ settings. This fixes odd behaviors when pods include static libraries, ex.
+ Firebase Analytics.
+* iOS Resolver - Added a workaround when app crashes on launch due to
+ `Library not loaded: @rpath/libswiftCore.dylib` when some pods includes Swift
+ framework. This is turned `OFF` by default and can be changed in iOS Resolver
+ settings.
+
+# Version 1.2.169 - Jan 20, 2022
+* General - Fixes #425 - Change to save `GvhProjectSettings.xml` without
+ Unicode byte order mark (BoM).
+* Android Resolver - Remove reference to `jcenter()`
+* iOS Resolver - Force setting `LANG` when executing Cocoapods in shell mode on
+ Mac.
+
+# Version 1.2.168 - Dec 9, 2021
+* All - Fixes #472 by removing the use of `System.Diagnostics.Debug.Assert`
+* All - Fixed #477 by properly enabling EDM4U libraries for Unity 2021.2+ when
+ the package is installed through `.tgz`
+
+# Version 1.2.167 - Oct 6, 2021
+* All - Moved versioned `.dll` in EDM4U to a versioned folder and remove their
+ version postfix in their filename. For instance, `IOSResolver.dll` will be
+ placed at `ExternalDependencyManager/Editor/1.2.167/Google.IOSResolver.dll`.
+* Android Resolver - Fixed #243 by only using the highest version in
+ `mainTemplate.gradle` when duplicated dependencies are presented.
+* Android Resolver - Added supports to x86_64 to ABI list for Android apps on
+ Chrome OS.
+
+# Version 1.2.166 - Jun 30, 2021
+* All - Fixed #440 and fixed #447 by specifying the parameter type while calling
+ `GetApplicationIdentifier()` Unity API using reflection, due to a new
+ overloaded method introduced in Unity 2021.2.
+* Android Resolver - Fixed #442 by patching `Dependency.IsGreater()` when the
+ version strings end '+'.
+
+# Version 1.2.165 - Apr 28, 2021
+## Bug Fixes
+* Version Handler - Fixed #431 by replacing the use of `HttpUtility.UrlEncode()`
+ which causes NullReferenceException in certain version of Unity.
+* Android Resolver - Check that androidSdkRootPath directory exists before using
+ as sdkPath.
+* Android Resolver - Fixed Android Resolver integration tests with Unity
+ 2019.3+.
+
+# Version 1.2.164 - Feb 4, 2021
+## New Features
+* Android Resolver - Added support for Android packages with classifier in their
+ namespaces.
+* iOS Resolver - Added new settings in iOS Resolver to configure generated
+ Podfile.
+* iOS Resolver - Added a new attribute `addToAllTargets` in Dependencies.xml.
+
+## Bug Fixes
+* iOS Resolver - Fixed XML parsing for `bitcodeEnabled` attribute in
+ Dependencies.xml.
+
+# Version 1.2.163 - Dec 15, 2020
+## Bug Fixes
+* Version Handler - Fixed measurement reporting
+
+# Version 1.2.162 - Nov 19, 2020
+## Bug Fixes
+* Version Handler - Improved #413 by preventing Version Handler from running
+ from static constructor when it is disabled.
+* Package Manager Resolver - Remove GPR
+
+# Version 1.2.161 - Oct 12, 2020
+## Bug Fixes
+* Android Resolver - Fixed the issue that Android Resolver does not resolve
+ again before build in Unity 2020 if it failed to resolve previously.
+
+# Version 1.2.160 - Sep 30, 2020
+## Bug Fixes
+* Android Resolver - Fixed a regression that gradleResolver can be null until
+ Initialize() is called.
+* Android Resolver - Fixed a regression that Android Resolver failed in Unity
+ 2019.3+ due to `gradleTemplate.properties` not enabled when
+ `mainTemplate.gradle` is not enabled at all.
+
+# Version 1.2.159 - Sep 11, 2020
+## Bug Fixes
+* Android Resolver - Fixed #322 where the Unity editor will lose its target SDK
+ setting between Unity restarts if `>28` is selected in 2019. This is due to
+ Unity AndroidSdkVersions enum does not contain values above 28.
+* Android Resolver - Fixed #360 where building Android app with Untiy 2019.3+
+ may fail due to Jetifier and AndroidX not enabled properly in generated
+ Gradle project. This fix requires the user to enable
+ `Custom Gradle Properties Template` found under
+ `Player Settings > Settings for Android > Publishing Settings`.
+
+# Version 1.2.158 - Sep 3, 2020
+## Bug Fixes
+* Version Handler: Fixed editor freeze when `-executeMethod` is used in
+ non-batch mode.
+* Android Resolver: Normalized file paths when generating local Maven repo
+ since the path may contains a mix of forward and backward slash on Windows.
+* Export Unity Package: Fixed generation of .unitypackage with tarfile on
+ Windows.
+
+# Version 1.2.157 - Aug 6, 2020
+## Bug Fixes
+* Android Resolver: Delay initialization until active build target is Android
+ and the editor is not in play mode.
+* iOS Resolver: Delay initialization until active build target is iOS
+ and the editor is not in play mode.
+* Export Unity Package: Workaround directory creation racy if multiple export
+ operations are spawned at the same time.
+
+# Version 1.2.156 - June 10, 2020
+## Bug Fixes
+* Android Resolver: Fixed that the generated local repo assets contains
+ redundent labels which are causing Version Handler to failed while
+ uninstalling packages.
+* Android Resolver: Fixed that the repo url injected into mainTemplate.gradle
+ is incorrect when Unity is configured to export gradle project.
+* Android Resolver: Limited to only create local Maven repo when the source
+ repo contains ".srcaar" file.
+
+## Changes
+* All: Described EDM4U analytics data usage in readme.
+
+# Version 1.2.155 - May 14, 2020
+## Bug Fixes
+* All: Fixed compiler error when build with Unity 5.4 or below due to the
+ usage of Rect.zero.
+* All: Ignore cases when checking command line arguments.
+
+# Version 1.2.154 - May 14, 2020
+## Bug Fixes
+* All: Make each MultiSelectWindow for different purposes to have its own
+ unique window.
+
+## Changes
+* All: Replace all dialog with DialogWindow which is implemented from
+ EditorWindow.
+* Package Manager Resolver: Clarify how manifest.json will be changed in Package
+ Manager Resolver window.
+
+# Version 1.2.153 - Apr 24, 2020
+## Bug Fixes
+* Android Resolver: Fixed an exception when repainting the Android resolution
+ window in Unity 2019.3.x.
+
+# Version 1.2.152 - Apr 17, 2020
+## Bug Fixes
+* Version Handler: Fixed exception when waiting for enabled editor DLLs to
+ load.
+* Android Resolver: Fixed regression when using a Custom Gradle Template
+ on Windows.
+
+# Version 1.2.151 - Apr 16, 2020
+## Bug Fixes
+* Version Handler: When waiting for newly enabled editor DLLs to load, ignore
+ all DLLs that do not have a file-system location.
+* Android Resolver: Fixed resolution when using a Custom Gradle Template with
+ libraries stored in a local maven repository distributed with a plugin
+ installed with the Unity Package Manager.
+
+# Version 1.2.150 - Apr 9, 2020
+## Bug Fixes
+* All: The new packaging script when run on MacOS was generating a
+ .unitypackage archive that could not be read by Unity on Windows.
+ This release simply repackages the plugin with tar/gzip to fix the problem.
+
+# Version 1.2.149 - Apr 8, 2020
+## Bug Fixes
+* Package Manager Resolver: Fixed spurious error message when resuming
+ migration after installing a UPM package.
+
+# Version 1.2.148 - Apr 8, 2020
+## Bug Fixes
+* Package Manager Resolver: Fixed an exception when resuming migration
+ after installing a UPM package.
+
+# Version 1.2.147 - Apr 8, 2020
+## Bug Fixes
+* Version Handler: Fixed alias traversal bug which caused problems when
+ migrating from installed .unitypackage files to UPM packages.
+
+# Version 1.2.146 - Apr 8, 2020
+## Bug Fixes
+* Version Handler: Fixed exception in manifest parsing when a manifest is
+ detected with no aliases.
+
+# Version 1.2.145 - Apr 2, 2020
+## New Features
+* Package Manager Resolver: Added a method to migrate Version Handler
+ managed packages installed via `.unitypackage` to Unity Package Manager
+ packages. This is initially used to migrate the External Dependency Manager
+ to UPM.
+
+## Changes
+* All: Verbose logging is now no longer automatically enabled in batch mode
+ across all components. Instead logging can be configured using each
+ component's verbose logging setting or by using the `-gvh_log_debug` command
+ line flag when starting Unity.
+* Version Handler: Sped up version handler updates when the app domain isn't
+ reloaded.
+
+## Bug Fixes
+* Version Handler: Fixed the display of the obsolete files clean up dialog
+ when the asset database refreshes.
+* Version Handler: Improved reliability of callback from
+ the VersionHandler.UpdateCompleteMethods event when an asset database
+ refresh occurs.
+* Version Handler: Fixed duplicate exportPath labels when 'Assets/' is the
+ root of paths assigned to files.
+* Version Handler: Handle empty lines in manifest files.
+
+# Version 1.2.144 - Mar 23, 2020
+## Changed
+* iOS Resolver: Removed the ability to configure the Xcode target a Cocoapod
+ is added to.
+
+## Bug Fixes
+* iOS Resolver: Reverted support for adding Cocoapods to multiple targets as
+ it caused a regression (exception thrown during post-build step) in some
+ versions of Unity.
+
+# Version 1.2.143 - Mar 20, 2020
+## Bug Fixes
+* Android Resolver: Fixed caching of resolution state which was causing
+ the resolver to always run when no dependencies had changed.
+
+# Version 1.2.142 - Mar 19, 2020
+## Changes
+* Package Manager Resolver: Enabled auto-add by default.
+
+# Version 1.2.141 - Mar 19, 2020
+## Bug Fixes
+* Fixed a bug when retrieving project settings. If a plugin was configured
+ to fetch project settings, if a setting was fetched (e.g "foo") and this
+ setting existed in the system settings but not the project settings the
+ system value would override the default value leading to unexpected
+ behavior.
+* Fixed a warning when caching web request classes in Unity 5.6.
+
+# Version 1.2.140 - Mar 19, 2020
+## Bug Fixes
+* Fixed measurement reporting in Unity 5.x.
+* Version Handler: Fixed NullReferenceException when an asset doesn't have
+ an AssetImporter.
+
+# Version 1.2.139 - Mar 18, 2020
+## Changed
+* Added documentation to the built plugin.
+
+# Version 1.2.138 - Mar 17, 2020
+## New Features
+* Package Manager Resolver: Added the Package Manager Resolver
+ component that allows developers to easily boostrap Unity Package Manager
+ (UPM) registry addition using unitypackage plugins.
+* Version Handler: Added a window that allows plugins to managed by the
+ Version Handler to be uninstalled.
+* Version Handler: Added support for displaying installed plugins.
+* Version Handler: Added support for moving files in plugins to their install
+ locations (if the plugin has been configured to support this).
+* iOS Resolver: Added the ability to configure the Xcode target a Cocoapod is
+ added to.
+
+## Bug Fixes
+* Fixed upgrade from version 1.2.137 and below after the plugin rename to
+ EDM4U broke the upgrade process.
+* Android Resolver: Worked around PlayerSettings.Android.targetSdkVersion
+ returning empty names for some values in 2019.x.
+* Version Handler: Fixed the display of the obsolete files clean up window.
+* Version Handler: Fixed managed file check when assets are modified in the
+ project after plugin import.
+
+# Version 1.2.137 - Mar 6, 2020
+## Changed
+* Renamed package to External Package Manager for Unity (EDM4U).
+ We changed this to reflect what this plugin is doing today which is far more
+ than the original scope which just consisted of importing jar files from the
+ Android SDK maven repository.
+ Scripts that used to pull `play-services-resolver*.unitypackage` will now have
+ to request `external-dependency-manager*.unitypackage` instead.
+ We'll still be shipping a `play-services-resolver*_manifest.txt` file to
+ handle upgrading from older versions of the plugin.
+
+## New Features
+* All Components: Added reporting of usage so that we can remotely detect
+ errors and target improvements.
+* Android Resolver: Added support for *Dependencies.xml files in Unity Package
+ Manager packages.
+* iOS Resolver: Added support for *Dependencies.xml files in Unity Package
+ Manager packages.
+
+## Bug Fixes
+* Version Handler: Disabled attempts to disable asset metadata modification
+ when assets are in a Unity Package Manager managed package.
+
+# Version 1.2.136 - Feb 19, 2019
+## Bug Fixes
+* Android Resolver: Fixed OpenJDK path discovery in Unity 2019.3.1.
+
+# Version 1.2.135 - Dec 5, 2019
+## Bug Fixes
+* All Components: Fixed stack overflow when loading project settings.
+
+# Version 1.2.134 - Dec 4, 2019
+## Bug Fixes
+* All Components: Fixed an issue which caused project settings to be cleared
+ when running in batch mode.
+
+# Version 1.2.133 - Nov 18, 2019
+## Bug Fixes
+* All Components: Failure to save project settings will now report an error
+ to the log rather than throwing an exception.
+
+# Version 1.2.132 - Nov 11, 2019
+## Bug Fixes
+* Android Resolver: Worked around expansion of DIR_UNITYPROJECT on Windows
+ breaking Gradle builds when used as part of a file URI.
+* Android Resolver: mainTemplate.gradle is only written if it needs to be
+ modified.
+
+# Version 1.2.131 - Oct 29, 2019
+## Bug Fixes
+* Version Handler: Improved execution of events on the main thread in batch
+ mode.
+* Version Handler: Improved log level configuration at startup.
+* Version Handler: Improved performance of class lookup in deferred method
+ calls.
+* Version Handler: Fixed rename to enable / disable for editor assets.
+* iOS Resolver: Improved log level configuration at startup.
+* Android Resolver: Improved local maven repo path reference in
+ mainTemplate.gradle using DIR_UNITYPROJECT. DIR_UNITYPROJECT by Unity
+ to point to the local filesystem path of the Unity project when Unity
+ generates the Gradle project.
+
+# Version 1.2.130 - Oct 23, 2019
+## New Features
+* iOS Resolver: Added support for modifying the Podfile before `pod install`
+ is executed.
+
+## Bug Fixes
+* Version Handler: Fixed invalid classname error when calling
+ `VersionHandler.UpdateVersionedAssets()`.
+
+# Version 1.2.129 - Oct 2, 2019
+## Bug Fixes
+* iOS Resolver: Changed Cocoapod integration in Unity 2019.3+ to
+ only add Pods to the UnityFramework target.
+
+# Version 1.2.128 - Oct 1, 2019
+## Bug Fixes
+* iOS Resolver: Fixed Cocoapod project integration mode with Unity
+ 2019.3+.
+
+# Version 1.2.127 - Sep 30, 2019
+## Changes
+* Android Resolver: All Android Resolver settings File paths are now
+ serialized with POSIX directory separators.
+
+# Version 1.2.126 - Sep 27, 2019
+## Changes
+* Android Resolver: File paths are now serialized with POSIX directory
+ separators.
+## Bug Fixes
+* Android Resolver: Fixed resolution when the parent directory of a Unity
+ project contains a Gradle project (i.e `settings.gradle` file).
+
+# Version 1.2.125 - Sep 23, 2019
+## Bug Fixes
+* All components: Silenced a warning about not being able to set the console
+ encoding to UTF8.
+* Android Resolver: Worked around broken AndroidSDKTools class in some
+ versions of Unity.
+* iOS Resolver: Fixed iOS target SDK version check
+* Version Handler: Changed clean up obsolete files window so that it doesn't
+ exceed the screen size.
+
+# Version 1.2.124 - Jul 28, 2019
+## Bug Fixes
+* All components: Fixed regression with source control integration when using
+ Unity 2019.1+.
+
+# Version 1.2.123 - Jul 23, 2019
+## New Features
+* All components: Source control integration for project settings.
+## Changes
+* Android Resolver: Removed AAR cache as it now makes little difference to
+ incremental resolution performance.
+* Android Resolver: Improved embedded resource management so that embedded
+ resources should upgrade when the plugin is updated without restarting
+ the Unity editor.
+## Bug Fixes
+* Version Handler: Fixed InvokeMethod() and InvokeStaticMethod() when calling
+ methods that have interface typed arguments.
+
+# Version 1.2.122 - Jul 2, 2019
+## Bug Fixes
+* iOS Resolver: Worked around Unity not loading the iOS Resolver DLL as it
+ referenced the Xcode extension in a public interface. The iOS Resolver
+ DLL still references the Xcode extension internally and just handles
+ missing type exceptions dynamically.
+
+# Version 1.2.121 - Jun 27, 2019
+## Bug Fixes
+* Android Resolver: Fixed warning about missing Packages folder when loading
+ XML dependencies files in versions of Unity without the package manager.
+* Android Resolver: Fixed resolution window progress bar exceeding 100%.
+* Android Resolver: If AndroidX is detected in the set of resolved libraries,
+ the user will be prompted to enable the Jetifier.
+* Android Resolver: Improved text splitting in text area windows.
+* iOS Resolver: Added support for Unity's breaking changes to the Xcode API
+ in 2019.3.+. Cocoapods are now added to build targets, Unity-iPhone and
+ UnityFramework in Unity 2019.3+.
+
+# Version 1.2.120 - Jun 26, 2019
+## New Features
+* Android Resolver: Added support for loading *Dependencies.xml files from
+ Unity Package Manager packages.
+* Android Resolver: Resolution window is now closed if resolution runs as
+ a pre-build step.
+* iOS Resolver: Added support for loading *Dependencies.xml files from
+ Unity Package Manager packages.
+## Bug Fixes
+* Android Resolver: Fixed generation of relative repo paths when using
+ mainTemplate.gradle resolver.
+* Android Resolver: Fixed copy of .srcaar to .aar files in repos embedded in a
+ project when a project path has characters (e.g whitespace) that are escaped
+ during conversion to URIs.
+* Android Resolver: Fixed auto-resolution always running if the Android SDK
+ is managed by Unity Hub.
+
+# Version 1.2.119 - Jun 19, 2019
+## Bug Fixes
+* Android Resolver: Fixed error reported when using Jetifier integration
+ in Unity 2018+ if the target SDK is set to "highest installed".
+
+# Version 1.2.118 - Jun 18, 2019
+## New Features
+* Android Resolver: Added initial
+ [Jetifier](https://developer.android.com/studio/command-line/jetifier)
+ integration which simplifies
+ [migration](ttps://developer.android.com/jetpack/androidx/migrate)
+ to Jetpack ([AndroidX](https://developer.android.com/jetpack/androidx))
+ libraries in cases where all dependencies are managed by the Android
+ Resolver.
+ This can be enabled via the `Use Jetifier` option in the
+ `Assets > Play Services Resolver > Android Resolver > Settings` menu.
+ Caveats:
+ - If your project contains legacy Android Support Library .jar and .aar
+ files, these files will need to be removed and replaced with references to
+ artifacts on Maven via `*Dependencies.xml` files so that the Jetifier
+ can map them to Jetpack (AndroidX) libraries.
+ For example, remove the file `support-v4-27.0.2.jar` and replace it with
+ `` in a
+ `*Dependencies.xml` file.
+ - If your project contains .jar or .aar files that use the legacy Android
+ Support Libraries, these will need to be moved into a local Maven repo
+ [See this guide](https://maven.apache.org/guides/mini/guide-3rd-party-jars-local.html)
+ and then these files should be removed from your Unity project and instead
+ referenced via `*Dependencies.xml` files so that the Jetifier can
+ patch them to reference the Jetpack lirbaries.
+
+## Bug Fixes
+* Android Resolver: Disabled version locking of com.android.support:multidex
+ does not use the same versioning scheme as other legacy Android support
+ libraries.
+* Version Handler: Made Google.VersionHandler.dll's asset GUID stable across
+ releases. This faciliates error-free import into projects where
+ Google.VersionHandler.dll is moved from the default install location.
+
+# Version 1.2.117 - Jun 12, 2019
+## Bug Fixes
+* Android Resolver: Fix copying of .srcaar to .aar files for
+ mainTemplate.gradle resolution. PluginImporter configuration was previously
+ not being applied to .aar files unless the Unity project was saved.
+
+# Version 1.2.116 - Jun 7, 2019
+## Bug Fixes
+* Android Resolver: Fixed resolution of Android dependencies without version
+ specifiers.
+* Android Resolver: Fixed Maven repo not found warning in Android Resolver.
+* Android Resolver: Fixed Android Player directory not found exception in
+ Unity 2019.x when the Android Player isn't installed.
+
+# Version 1.2.115 - May 28, 2019
+## Bug Fixes
+* Android Resolver: Fixed exception due to Unity 2019.3.0a4 removing
+ x86 from the set of supported ABIs.
+
+# Version 1.2.114 - May 27, 2019
+## New Features
+* Android Resolver: Added support for ABI stripping when using
+ mainTemplate.gradle. This only works with AARs stored in repos
+ on the local filesystem.
+
+# Version 1.2.113 - May 24, 2019
+## New Features
+* Android Resolver: If local repos are moved, the plugin will search the
+ project for matching directories in an attempt to correct the error.
+* Version Handler: Files can be now targeted to multiple build targets
+ using multiple "gvh_" asset labels.
+## Bug Fixes
+* Android Resolver: "implementation" or "compile" are now added correctly
+ to mainTemplate.gradle in Unity versions prior to 2019.
+
+# Version 1.2.112 - May 22, 2019
+## New Features
+* Android Resolver: Added option to disable addition of dependencies to
+ mainTemplate.gradle.
+ See `Assets > Play Services Resolver > Android Resolver > Settings`.
+* Android Resolver: Made paths to local maven repositories in
+ mainTemplate.gradle relative to the Unity project when a project is not
+ being exported.
+## Bug Fixes
+* Android Resolver: Fixed builds with mainTemplate.gradle integration in
+ Unity 2019.
+* Android Resolver: Changed dependency inclusion in mainTemplate.gradle to
+ use "implementation" or "compile" depending upon the version of Gradle
+ included with Unity.
+* Android Resolver: Gracefully handled exceptions if the console encoding
+ can't be modified.
+* Android Resolver: Now gracefully fails if the AndroidPlayer directory
+ can't be found.
+
+# Version 1.2.111 - May 9, 2019
+## Bug Fixes
+* Version Handler: Fixed invocation of methods with named arguments.
+* Version Handler: Fixed occasional hang when the editor is compiling
+ while activating plugins.
+
+# Version 1.2.110 - May 7, 2019
+## Bug Fixes
+* Android Resolver: Fixed inclusion of some srcaar artifacts in builds with
+ Gradle builds when using mainTemplate.gradle.
+
+# Version 1.2.109 - May 6, 2019
+## New Features:
+* Added links to documentation from menu.
+* Android Resolver: Added option to auto-resolve Android libraries on build.
+* Android Resolver: Added support for packaging specs of Android libraries.
+* Android Resolver: Pop up a window when displaying Android dependencies.
+
+## Bug Fixes
+* Android Resolver: Support for Unity 2019 Android SDK and JDK install locations
+* Android Resolver: e-enable AAR explosion if internal builds are enabled.
+* Android Resolver: Gracefully handle exceptions on file deletion.
+* Android Resolver: Fixed Android Resolver log spam on load.
+* Android Resolver: Fixed save of Android Resolver PromptBeforeAutoResolution
+ setting.
+* Android Resolver: Fixed AAR processing failure when an AAR without
+ classes.jar is found.
+* Android Resolver: Removed use of EditorUtility.DisplayProgressBar which
+ was occasionally left displayed when resolution had completed.
+* Version Handler: Fixed asset rename to disable when a disabled file exists.
+
+# Version 1.2.108 - May 3, 2019
+## Bug Fixes:
+* Version Handler: Fixed occasional hang on startup.
+
+# Version 1.2.107 - May 3, 2019
+## New Features:
+* Version Handler: Added support for enabling / disabling assets that do not
+ support the PluginImporter, based upon build target selection.
+* Android Resolver: Added support for the global specification of maven repos.
+* iOS Resolver: Added support for the global specification of Cocoapod sources.
+
+# Version 1.2.106 - May 1, 2019
+## New Features
+* iOS Resolver: Added support for development pods in Xcode project integration
+ mode.
+* iOS Resolver: Added support for source pods with resources in Xcode project
+ integration mode.
+
+# Version 1.2.105 - Apr 30, 2019
+## Bug fixes
+* Android Resolver: Fixed reference to Java tool path in logs.
+* Android and iOS Resolvers: Changed command line execution to emit a warning
+ rather than throwing an exception and failing, when it is not possible to
+ change the console input and output encoding to UTF-8.
+* Android Resolver: Added menu option and API to delete resolved libraries.
+* Android Resolver: Added menu option and API to log the repos and libraries
+ currently included in the project.
+* Android Resolver: If Plugins/Android/mainTemplate.gradle file is present and
+ Gradle is selected as the build type, resolution will simply patch the file
+ with Android dependencies specified by plugins in the project.
+
+# Version 1.2.104 - Apr 10, 2019
+## Bug Fixes
+* Android Resolver: Changed Android ABI selection method from using whitelisted
+ Unity versions to type availability. This fixes an exception on resolution
+ in some versions of Unity 2017.4.
+
+# Version 1.2.103 - Apr 2, 2019
+## Bug Fixes
+* Android Resolver: Whitelisted Unity 2017.4 and above with ARM64 support.
+* Android Resolver: Fixed Java version check to work with Java SE 12 and above.
+
+# Version 1.2.102 - Feb 13, 2019
+## Bug Fixes
+* Android Resolver: Fixed the text overflow on the Android Resolver
+ prompt before initial run to fit inside the buttons for
+ smaller screens.
+
+# Version 1.2.101 - Feb 12, 2019
+## New Features
+* Android Resolver: Prompt the user before the resolver runs for the
+ first time and allow the user to elect to disable from the prompt.
+* Android Resolver: Change popup warning when resolver is disabled
+ to be a console warning.
+
+# Version 1.2.100 - Jan 25, 2019
+## Bug Fixes
+* Android Resolver: Fixed AAR processing sometimes failing on Windows
+ due to file permissions.
+
+# Version 1.2.99 - Jan 23, 2019
+## Bug Fixes
+* Android Resolver: Improved performance of project property polling.
+* Version Handler: Fixed callback of VersionHandler.UpdateCompleteMethods
+ when the update process is complete.
+
+# Version 1.2.98 - Jan 9, 2019
+## New Features
+* iOS Resolver: Pod declaration properties can now be set via XML pod
+ references. For example, this can enable pods for a subset of build
+ configurations.
+## Bug Fixes
+* iOS Resolver: Fixed incremental builds after local pods support caused
+ regression in 1.2.96.
+
+# Version 1.2.97 - Dec 17, 2018
+## Bug Fixes
+* Android Resolver: Reduced memory allocation for logic that monitors build
+ settings when auto-resolution is enabled. If auto-resolution is disabled,
+ almost all build settings are no longer polled for changes.
+
+# Version 1.2.96 - Dec 17, 2018
+## Bug Fixes
+* Android Resolver: Fixed repacking of AARs to exclude .meta files.
+* Android Resolver: Only perform auto-resolution on the first scene while
+ building.
+* Android Resolver: Fixed parsing of version ranges that include whitespace.
+* iOS Resolver: Added support for local development pods.
+* Version Handler: Fixed Version Handler failing to rename some files.
+
+# Version 1.2.95 - Oct 23, 2018
+## Bug Fixes:
+* Android Resolver: Fixed auto-resolution running in a loop in some scenarios.
+
+# Version 1.2.94 - Oct 22, 2018
+## Bug Fixes
+* iOS Resolver: Added support for PODS_TARGET_SRCROOT in source Cocoapods.
+
+# Version 1.2.93 - Oct 22, 2018
+## Bug Fixes
+* Android Resolver: Fixed removal of Android libraries on auto-resolution when
+ `*Dependencies.xml` files are deleted.
+
+# Version 1.2.92 - Oct 2, 2018
+## Bug Fixes
+* Android Resolver: Worked around auto-resolution hang on Windows if
+ resolution starts before compilation is finished.
+
+# Version 1.2.91 - Sep 27, 2018
+## Bug Fixes
+* Android Resolver: Fixed Android Resolution when the selected build target
+ isn't Android.
+* Added C# assembly symbols the plugin to simplify debugging bug reports.
+
+# Version 1.2.90 - Sep 21, 2018
+## Bug Fixes
+* Android Resolver: Fixed transitive dependency selection of version locked
+ packages.
+
+# Version 1.2.89 - Aug 31, 2018
+## Bug Fixes
+* Fixed FileLoadException in ResolveUnityEditoriOSXcodeExtension an assembly
+ can't be loaded.
+
+# Version 1.2.88 - Aug 29, 2018
+## Changed
+* Improved reporting of resolution attempts and conflicts found in the Android
+ Resolver.
+## Bug Fixes
+* iOS Resolver now correctly handles sample code in CocoaPods. Previously it
+ would add all sample code to the project when using project level
+ integration.
+* Android Resolver now correctly handles Gradle conflict resolution when the
+ resolution results in a package that is compatible with all requested
+ dependencies.
+
+# Version 1.2.87 - Aug 23, 2018
+## Bug Fixes
+* Fixed Android Resolver "Processing AARs" dialog getting stuck in Unity 5.6.
+
+# Version 1.2.86 - Aug 22, 2018
+## Bug Fixes
+* Fixed Android Resolver exception in OnPostProcessScene() when the Android
+ platform isn't selected.
+
+# Version 1.2.85 - Aug 17, 2018
+## Changes
+* Added support for synchronous resolution in the Android Resolver.
+ PlayServicesResolver.ResolveSync() now performs resolution synchronously.
+* Auto-resolution in the Android Resolver now results in synchronous resolution
+ of Android dependencies before the Android application build starts via
+ UnityEditor.Callbacks.PostProcessSceneAttribute.
+
+# Version 1.2.84 - Aug 16, 2018
+## Bug Fixes
+* Fixed Android Resolver crash when the AndroidResolverDependencies.xml
+ file can't be written.
+* Reduced log spam when a conflicting Android library is pinned to a
+ specific version.
+
+# Version 1.2.83 - Aug 15, 2018
+## Bug Fixes
+* Fixed Android Resolver failures due to an in-accessible AAR / JAR explode
+ cache file. If the cache can't be read / written the resolver now continues
+ with reduced performance following recompilation / DLL reloads.
+* Fixed incorrect version number in plugin manifest on install.
+ This was a minor issue since the version handler rewrote the metadata
+ after installation.
+
+# Version 1.2.82 - Aug 14, 2018
+## Changed
+* Added support for alphanumeric versions in the Android Resolver.
+
+## Bug Fixes
+* Fixed Android Resolver selection of latest duplicated library.
+* Fixed Android Resolver conflict resolution when version locked and non-version
+ locked dependencies are specified.
+* Fixed Android Resolver conflict resolution when non-existent artifacts are
+ referenced.
+
+# Version 1.2.81 - Aug 9, 2018
+## Bug Fixes
+* Fixed editor error that would occur when when
+ `PlayerSettings.Android.targetArchitectures` was set to
+ `AndroidArchitecture.All`.
+
+# Version 1.2.80 - Jul 24, 2018
+## Bug Fixes
+* Fixed project level settings incorrectly falling back to system wide settings
+ when default property values were set.
+
+# Version 1.2.79 - Jul 23, 2018
+## Bug Fixes
+* Fixed AndroidManifest.xml patching on Android Resolver load in Unity 2018.x.
+
+# Version 1.2.78 - Jul 19, 2018
+## Changed
+* Added support for overriding conflicting dependencies.
+
+# Version 1.2.77 - Jul 19, 2018
+## Changed
+* Android Resolver now supports Unity's 2018 ABI filter (i.e arm64-v8a).
+* Reduced Android Resolver build option polling frequency.
+* Disabled Android Resolver auto-resolution in batch mode. Users now need
+ to explicitly kick off resolution through the API.
+* All Android Resolver and Version Handler dialogs are now disabled in batch
+ mode.
+* Verbose logging for all plugins is now enabled by default in batch mode.
+* Version Handler bootstrapper has been improved to no longer call
+ UpdateComplete multiple times. However, since Unity can still reload the
+ app domain after plugins have been enabled, users still need to store their
+ plugin state to persistent storage to handle reloads.
+
+## Bug Fixes
+* Android Resolver no longer incorrectly adds MANIFEST.MF files to AARs.
+* Android Resolver auto-resolution jobs are now unscheduled when an explicit
+ resolve job is started.
+
+# Version 1.2.76 - Jul 16, 2018
+## Bug Fixes
+* Fixed variable replacement in AndroidManifest.xml files in the Android
+ Resolver.
+ Version 1.2.75 introduced a regression which caused all variable replacement
+ to replace the *entire* property value rather than the component of the
+ property that referenced a variable. For example,
+ given "applicationId = com.my.app", "${applicationId}.foo" would be
+ incorrectly expanded as "com.my.app" rather than "com.my.app.foo". This
+ resulted in numerous issues for Android builds where content provider
+ initialization would fail and services may not start.
+
+## Changed
+* Gradle prebuild experimental feature has been removed from the Android
+ Resolver. The feature has been broken for some time and added around 8MB
+ to the plugin size.
+* Added better support for execution of plugin components in batch mode.
+ In batch mode UnityEditor.update is sometimes never called - like when a
+ single method is executed - so the new job scheduler will execute all jobs
+ synchronously from the main thread.
+
+# Version 1.2.75 - Jun 20, 2018
+## New Features
+* Android Resolver now monitors the Android SDK path when
+ auto-resolution is enabled and triggers resolution when the path is
+ modified.
+
+## Changed
+* Android auto-resolution is now delayed by 3 seconds when the following build
+ settings are changed:
+ - Target ABI.
+ - Gradle build vs. internal build.
+ - Project export.
+* Added a progress bar display when AARs are being processed during Android
+ resolution.
+
+## Bug Fixes
+* Fixed incorrect Android package version selection when a mix of
+ version-locked and non-version-locked packages are specified.
+* Fixed non-deterministic Android package version selection to select
+ the highest version of a specified package rather than the last
+ package specification passed to the Gradle resolution script.
+
+# Version 1.2.74 - Jun 19, 2018
+## New Features
+* Added workaround for broken AndroidManifest.xml variable replacement in
+ Unity 2018.x. By default ${applicationId} variables will be replaced by
+ the bundle ID in the Plugins/Android/AndroidManifest.xml file. The
+ behavior can be disabled via the Android Resolver settings menu.
+
+# Version 1.2.73 - May 30, 2018
+## Bug Fixes
+* Fixed spurious warning message about missing Android plugins directory on
+ Windows.
+
+# Version 1.2.72 - May 23, 2018
+## Bug Fixes
+* Fixed spurious warning message about missing Android plugins directory.
+
+# Version 1.2.71 - May 10, 2018
+## Bug Fixes
+* Fixed resolution of Android dependencies when the `Assets/Plugins/Android`
+ directory is named in a different case e.g `Assets/plugins/Android`.
+
+# Version 1.2.70 - May 7, 2018
+## Bug Fixes
+* Fixed bitcode flag being ignored for iOS pods.
+
+# Version 1.2.69 - May 7, 2018
+## Bug Fixes
+* Fixed escaping of local repository paths in Android Resolver.
+
+# Version 1.2.68 - May 3, 2018
+## Changes
+* Added support for granular builds of Google Play Services.
+
+# Version 1.2.67 - May 1, 2018
+## Changes
+* Improved support for iOS source-only pods in Unity 5.5 and below.
+
+# Version 1.2.66 - April 27, 2018
+## Bug Fixes
+* Fixed Version Handler renaming of Linux libraries with hyphens in filenames.
+ Previously, libraries named Foo-1.2.3.so were not being renamed to
+ libFoo-1.2.3.so on Linux which could break native library loading on some
+ versions of Unity.
+
+# Version 1.2.65 - April 26, 2018
+## Bug Fixes
+* Fix CocoaPods casing in logs and comments.
+
+# Version 1.2.64 - Mar 16, 2018
+## Bug Fixes
+* Fixed bug in download_artifacts.gradle (used by Android Resolver) which
+ reported a failure if required artifacts already exist.
+
+# Version 1.2.63 - Mar 15, 2018
+## Bug Fixes
+* Fixed iOS Resolver include search paths taking precedence over system headers
+ when using project level resolution.
+* Fixed iOS Resolver includes relative to library root, when using project level
+ resolution.
+
+# Version 1.2.62 - Mar 12, 2018
+## Changes
+* Improved error reporting when a file can't be moved to trash by the
+ Version Handler.
+## Bug Fixes
+* Fixed Android Resolver throwing NullReferenceException when the Android SDK
+ path isn't set.
+* Fixed Version Handler renaming files with underscores if the
+ "Rename to Canonical Filenames" setting is enabled.
+
+# Version 1.2.61 - Jan 22, 2018
+## Bug Fixes
+* Fixed Android Resolver reporting non-existent conflicting dependencies when
+ Gradle build system is enabled.
+
+# Version 1.2.60 - Jan 12, 2018
+## Changes
+* Added support for Maven / Ivy version specifications for Android packages.
+* Added support for Android SNAPSHOT packages.
+
+## Bug Fixes
+* Fixed Openjdk version check.
+* Fixed non-deterministic Android package resolution when two packages contain
+ an artifact with the same name.
+
+# Version 1.2.59 - Oct 19, 2017
+## Bug Fixes
+* Fixed execution of Android Gradle resolution script when it's located
+ in a path with whitespace.
+
+# Version 1.2.58 - Oct 19, 2017
+## Changes
+* Removed legacy resolution method from Android Resolver.
+ It is now only possible to use the Gradle or Gradle prebuild resolution
+ methods.
+
+# Version 1.2.57 - Oct 18, 2017
+## Bug Fixes
+* Updated Gradle wrapper to 4.2.1 to fix issues using Gradle with the
+ latest Openjdk.
+* Android Gradle resolution now also uses gradle.properties to pass
+ parameters to Gradle in an attempt to workaround problems with
+ command line argument parsing on Windows 10.
+
+# Version 1.2.56 - Oct 12, 2017
+## Bug Fixes
+* Fixed Gradle artifact download with non-version locked artifacts.
+* Changed iOS resolver to only load dependencies at build time.
+
+# Version 1.2.55 - Oct 4, 2017
+## Bug Fixes
+* Force Android Resolution when the "Install Android Packages" setting changes.
+
+# Version 1.2.54 - Oct 4, 2017
+## Bug Fixes
+* Fixed execution of command line tools on Windows when the path to the tool
+ contains a single quote (apostrophe). In this case we fallback to executing
+ the tool via the system shell.
+
+# Version 1.2.53 - Oct 2, 2017
+## New Features
+* Changed Android Resolver "resolution complete" dialog so that it now displays
+ failures.
+* Android Resolver now detects conflicting libraries that it does not manage
+ warning the user if they're newer than the managed libraries and prompting
+ the user to clean them up if they're older or at the same version.
+
+## Bug Fixes
+* Improved Android Resolver auto-resolution speed.
+* Fixed bug in the Gradle Android Resolver which would result in resolution
+ succeeding when some dependencies are not found.
+
+# Version 1.2.52 - Sep 25, 2017
+## New Features
+* Changed Android Resolver's Gradle resolution to resolve conflicting
+ dependencies across Google Play services and Android Support library packages.
+
+# Version 1.2.51 - Sep 20, 2017
+## Changes
+* Changed iOS Resolver to execute the CocoaPods "pod" command via the shell
+ by default. Some developers customize their shell environment to use
+ custom ssh certs to access internal git repositories that host pods so
+ executing "pod" via the shell will work for these scenarios.
+ The drawback of executing "pod" via the shell could potentially cause
+ users problems if they break their shell environment. Though users who
+ customize their shell environments will be able to resolve these issues.
+
+# Version 1.2.50 - Sep 18, 2017
+## New Features
+* Added option to disable the Gradle daemon in the Android Resolver.
+ This daemon is now disabled by default as some users are getting into a state
+ where multiple daemon instances are being spawned when changing dependencies
+ which eventually results in Android resolution failing until all daemon
+ processes are manually killed.
+
+## Bug Fixes
+* Android resolution is now always executed if the user declines the update
+ of their Android SDK. This ensure users can continue to use out of date
+ Android SDK packages if they desire.
+
+# Version 1.2.49 - Sep 18, 2017
+## Bug Fixes
+* Removed modulemap parsing in iOS Resolver.
+ The framework *.modulemap did not need to be parsed by the iOS Resolver
+ when injecting Cocoapods into a Xcode project. Simply adding a modular
+ framework to a Xcode project results in Xcode's Clang parsing the associated
+ modulemap and injecting any compile and link flags into the build process.
+
+# Version 1.2.48 - Sep 12, 2017
+## New Features
+* Changed settings to be per-project by default.
+
+## Bug Fixes
+* Added Google maven repository to fix GradlePrebuild resolution with Google
+ components.
+* Fixed Android Resolution failure with spaces in paths.
+
+# Version 1.2.47 - Aug 29, 2017
+## New Features
+* Android and iOS dependencies can now be specified using *Dependencies.xml
+ files. This is now the preferred method for registering dependencies,
+ we may remove the API for dependency addition in future.
+* Added "Reset to Defaults" button to each settings dialog to restore default
+ settings.
+* Android Resolver now validates the configured JDK is new enough to build
+ recently released Android libraries.
+## Bug Fixes
+* Fixed a bug that caused dependencies with the "LATEST" version specification
+ to be ignored when using the Gradle mode of the Android Resolver.
+* Fixed a race condition when running Android Resolution.
+* Fixed Android Resolver logging if a PlayServicesSupport instance is created
+ with no logging enabled before the Android Resolver is initialized.
+* Fixed iOS resolver dialog in Unity 4.
+* Fixed iOS Cocoapod Xcode project integration in Unity 4.
+
+# Version 1.2.46 - Aug 22, 2017
+## Bug Fixes
+* GradlePrebuild Android resolver on Windows now correctly locates dependent
+ data files.
+
+# Version 1.2.45 - Aug 22, 2017
+## Bug Fixes
+* Improved Android package auto-resolution and fixed clean up of stale
+ dependencies when using Gradle dependency resolution.
+
+# Version 1.2.44 - Aug 21, 2017
+## Bug Fixes
+* Enabled autoresolution for Gradle Prebuild.
+* Made the command line dialog windows have selectable text.
+* Fixed incorrect "Android Settings" dialog disabled groups.
+* Updated PlayServicesResolver android platform detection to use the package
+ manager instead of the 'android' tool.
+* UnityCompat reflection methods 'GetAndroidPlatform' and
+ 'GetAndroidBuildToolsVersion' are now Obsolete due to dependence on the
+ obsolete 'android' build tool.
+
+# Version 1.2.43 - Aug 18, 2017
+## Bug Fixes
+* Fixed Gradle resolution in the Android Resolver when running
+ PlayServicesResolver.Resolve() in parallel or spawning multiple
+ resolutions before the previous resolve completed.
+
+# Version 1.2.42 - Aug 17, 2017
+## Bug Fixes
+* Fixed Xcode project level settings not being applied by IOS Resolver when
+ Xcode project pod integration is enabled.
+
+# Version 1.2.41 - Aug 15, 2017
+## Bug Fixes
+* IOS Resolver's Xcode workspace pod integration is now disabled when Unity
+ Cloud Build is detected. Unity Cloud Build does not follow the same build
+ process as the Unity editor and fails to open the generated xcworkspace at
+ this time.
+
+# Version 1.2.40 - Aug 15, 2017
+## Bug Fixes
+* Moved Android Resolver Gradle Prebuild scripts into Google.JarResolver.dll.
+ They are now extracted from the DLL when required.
+* AARs / JARs are now cleaned up when switching the Android resolution
+ strategy.
+
+# Version 1.2.39 - Aug 10, 2017
+## New Features
+* Android Resolver now supports resolution with Gradle. This enables support
+ for non-local artifacts.
+## Bug Fixes
+* Android Resolver's Gradle Prebuild now uses Android build tools to determine
+ the Android platform tools version rather than relying upon internal Unity
+ APIs.
+* Android Resolver's Gradle Prebuild now correctly strips binaries that are
+ not required for the target ABI.
+
+# Version 1.2.38 - Aug 7, 2017
+## Bug Fixes
+* Fixed an issue in VersionHandler where disabled targets are ignored if
+ the "Any Platform" flag is set on a plugin DLL.
+
+# Version 1.2.37 - Aug 3, 2017
+## New Features
+* Exposed GooglePlayServices.PlayServicesResolver.Resolve() so that it's
+ possible for a script to be notified when AAR / Jar resolution is complete.
+ This makes it easier to setup a project to build from the command line.
+
+# Version 1.2.36 - Aug 3, 2017
+## New Features
+* VersionHandler.UpdateCompleteMethods allows a user to provide a list of
+ methods to be called when VersionHandlerImpl has completed an update.
+ This makes it easier to import a plugin and wait for VersionHandler to
+ execute prior executing a build.
+
+# Version 1.2.35 - Jul 28, 2017
+## New Features
+* VersionHandler will now rename Linux libraries so they can target Unity
+ versions that require different file naming. Libraries need to be labelled
+ gvh_linuxlibname-${basename} in order to be considered for renaming.
+ e.g gvh\_linuxlibname-MyLib will be named MyLib.so in Unity 5.5 and below and
+ libMyLib.so in Unity 5.6 and above.
+
+# Version 1.2.34 - Jul 28, 2017
+## Bug Fixes
+* Made VersionHandler bootstrap module more robust when calling static
+ methods before the implementation DLL is loaded.
+
+# Version 1.2.33 - Jul 27, 2017
+## New Features
+* Added a bootstrap module for VersionHandler so the implementation
+ of the VersionHandler module can be versioned without resulting in
+ a compile error when imported at different versions across multiple
+ plugins.
+
+# Version 1.2.32 - Jul 20, 2017
+## New Features
+* Added support for build target selection based upon .NET framework
+ version in the VersionHandler.
+ When applying either gvh\_dotnet-3.5 or gvh\_dotnet-4.5 labels to
+ assets, the VersionHandler will only enable the asset for the
+ specified set of build targets when the matching .NET framework version
+ is selected in Unity 2017's project settings. This allows assets
+ to be provided in a plugin that need to differ based upon .NET version.
+
+# Version 1.2.31 - Jul 5, 2017
+## Bug Fixes
+* Force expansion of AARs with native components when using Unity 2017
+ with the internal build system. In contrast to Unity 5.x, Unity 2017's
+ internal build system does not include native libraries included in AARs.
+ Forcing expansion of AARs with native components generates an
+ Ant / Eclipse project for each AAR which is correctly included by Unity
+ 2017's internal build system.
+
+# Version 1.2.30 - Jul 5, 2017
+## Bug Fixes
+* Fixed Cocoapods being installed when the build target isn't iOS.
+* Added support for malformed AARs with missing classes.jar.
+
+# Version 1.2.29 - Jun 16, 2017
+## New Features
+* Added support for the Android sdkmanager tool.
+
+# Version 1.2.28 - Jun 8, 2017
+## Bug Fixes
+* Fixed non-shell command line execution (regression from
+ Cocoapod installation patch).
+
+# Version 1.2.27 - Jun 7, 2017
+## Bug Fixes
+* Added support for stdout / stderr redirection when executing
+ commands in shell mode.
+ This fixes CocoaPod tool installation when shell mode is
+ enabled.
+* Fixed incremental builds when additional sources are specified
+ in the Podfile.
+
+# Version 1.2.26 - Jun 7, 2017
+## Bug Fixes
+* Fixed a crash when importing Version Handler into Unity 4.7.x.
+
+# Version 1.2.25 - Jun 7, 2017
+## Bug Fixes
+* Fixed an issue in the Jar Resolver which incorrectly notified
+ event handlers of bundle ID changes when the currently selected
+ (not active) build target changed in Unity 5.6 and above.
+
+# Version 1.2.24 - Jun 6, 2017
+## New Features
+* Added option to control file renaming in Version Handler settings.
+ Disabling file renaming (default option) significantly increases
+ the speed of file version management operations with the downside
+ that any files that are referenced directly by canonical filename
+ rather than asset ID will no longer be valid.
+* Improved logging in the Version Handler.
+## Bug Fixes
+* Fixed an issue in the Version Handler which caused it to not
+ re-enable plugins when re-importing a custom package with disabled
+ version managed files.
+
+# Version 1.2.23 - May 26, 2017
+## Bug Fixes
+* Fixed a bug with gradle prebuild resolver on windows.
+
+# Version 1.2.22 - May 19, 2017
+## Bug Fixes
+* Fixed a bug in the iOS resolver with incremental builds.
+* Fixed misdetection of Cocoapods support with Unity beta 5.6.
+
+# Version 1.2.21 - May 8, 2017
+## Bug Fixes
+* Fix for https://github.com/googlesamples/unity-jar-resolver/issues/48
+ Android dependency version number parsing when "-alpha" (etc.) are
+ included in dependency (AAR / JAR) versions.
+
+# Version 1.2.20 - May 8, 2017
+## Bug Fixes
+* Attempted to fix
+ https://github.com/googlesamples/unity-jar-resolver/issues/48
+ where a NullReferenceException could occur if a target file does not
+ have a valid version string.
+
+# Version 1.2.19 - May 4, 2017
+## Bug Fixes
+* Fixed Jar Resolver exploding and deleting AAR files it isn't managing.
+
+# Version 1.2.18 - May 4, 2017
+## New Features
+* Added support for preserving Unity pods such as when GVR is enabled.
+
+# Version 1.2.17 - Apr 20, 2017
+## Bug Fixes
+* Fixed auto-resolution when an Android application ID is modified.
+
+# Version 1.2.16 - Apr 17, 2017
+## Bug Fixes
+* Fixed Unity version number parsing on machines with a locale that uses
+ "," for decimal points.
+* Fixed null reference exception if JDK path isn't set.
+
+# Version 1.2.15 - Mar 17, 2017
+## New Features
+* Added warning when the Jar Resolver's background resolution is disabled.
+## Bug Fixes
+* Fixed support of AARs with native libraries when using Gradle.
+* Fixed extra repository paths when resolving dependencies.
+
+# Version 1.2.14 - Mar 7, 2017
+## New Features
+* Added experimental Android resolution using Gradle.
+ This alternative resolver supports proguard stripping with Unity's
+ internal build system.
+* Added Android support for single ABI builds when using AARs include
+ native libraries.
+* Disabled Android resolution on changes to all .cs and .js files.
+ File patterns that are monitored for auto-resolution can be added
+ using PlayServicesResolver.AddAutoResolutionFilePatterns().
+* Added tracking of resolved AARs and JARs so they can be cleaned up
+ if they're no longer referenced by a project.
+* Added persistence of AAR / JAR version replacement for each Unity
+ session.
+* Added settings dialog to the iOS resolver.
+* Integrated Cocoapod tool installation in the iOS resolver.
+* Added option to run pod tool via the shell.
+## Bug Fixes
+* Fixed build of some source Cocoapods (e.g Protobuf).
+* VersionHandler no longer prompts to delete obsolete manifests.
+* iOS resolver handles Cocoapod installation when using Ruby < 2.2.2.
+* Added workaround for package version selection when including
+ Google Play Services on Android.
+* Fixed support for pods that reference static libraries.
+* Fixed support for resource-only pods.
+
+# Version 1.2.12 - Feb 14, 2017
+## Bug Fixes
+* Fixed re-explosion of AARs when the bundle ID is modified.
+
+# Version 1.2.11 - Jan 30, 2017
+## New Features
+* Added support for Android Studio builds.
+* Added support for native (C/C++) shared libraries in AARs.
+
+# Version 1.2.10 - Jan 11, 2017
+## Bug Fixes
+* Fixed SDK manager path retrieval.
+* Also, report stderr when it's not possible to run the "pod" tool.
+* Handle exceptions thrown by Unity.Cecil on asset rename
+* Fixed IOSResolver to handle PlayerSettings.iOS.targetOSVersionString
+
+# Version 1.2.9 - Dec 7, 2016
+## Bug Fixes
+* Improved error reporting when "pod repo update" fails.
+* Added detection of xml format xcode projects generated by old Cocoapods
+ installations.
+
+# Version 1.2.8 - Dec 6, 2016
+## Bug Fixes
+* Increased speed of JarResolver resolution.
+* Fixed JarResolver caches getting out of sync with requested dependencies
+ by removing the caches.
+* Fixed JarResolver explode cache always being rewritten even when no
+ dependencies change.
+
+# Version 1.2.7 - Dec 2, 2016
+## Bug Fixes
+* Fixed VersionHandler build errors with Unity 5.5, due to the constantly
+ changing BuildTarget enum.
+* Added support for Unity configured JDK Path rather than requiring
+ JAVA_HOME to be set in the Jar Resolver.
+
+# Version 1.2.6 - Nov 15, 2016
+## Bug Fixes
+* Fixed IOSResolver errors when iOS support is not installed.
+* Added fallback to "pod" executable search which queries the Ruby Gems
+ package manager for the binary install location.
+
+# Version 1.2.5 - Nov 3, 2016
+## Bug Fixes
+* Added crude support for source only Cocoapods to the IOSResolver.
+
+# Version 1.2.4 - Oct 27, 2016
+## Bug Fixes
+* Automated resolution of out of date pod repositories.
+
+# Version 1.2.3 - Oct 25, 2016
+## Bug Fixes
+* Fixed exception when reporting conflicting dependencies.
+
+# Version 1.2.2 - Oct 17, 2016
+## Bug Fixes
+* Fixed issue working with Unity 5.5
+* Fixed issue with PlayServicesResolver corrupting other iOS dependencies.
+* Updated build script to use Unity distributed tools for building.
+
+# Version 1.2.1 - Jul 25, 2016
+## Bug Fixes
+* Removed 1.2 Resolver and hardcoded whitelist of AARs to expand.
+* Improved error reporting when the "jar" executable can't be found.
+* Removed the need to set JAVA_HOME if "jar" is in the user's path.
+* Fixed spurious copying of partially matching AARs.
+* Changed resolver to only copy / expand when source AARs change.
+* Auto-resolution of dependencies is now performed when the Android
+ build target is selected.
+
+## New Features
+* Expand AARs that contain manifests with variable expansion like
+ ${applicationId}.
+* Added optional logging in the JarResolverLib module.
+* Integration with the Android SDK manager for dependencies that
+ declare required Android SDK packages.
+
+# Version 1.2.0 - May 11 2016
+## Bug Fixes
+* Handles resolving dependencies when the artifacts are split across 2 repos.
+* #4 Misdetecting version for versions like 1.2-alpha. These are now string
+ compared if alphanumeric
+* Removed resolver creation via reflection since it did not work all the time.
+ Now a resolver needs to be loaded externally (which is existing behavior).
+
+## New Features
+* Expose PlayServicesResolver properties to allow for script access.
+* Explodes firebase-common and firebase-measurement aar files to support
+ ${applicationId} substitution.
+
+# Version 1.1.1 - 25 Feb 2016
+## Bug Fixes
+* #1 Spaces in project path not handled when exploding Aar file.
+* #2 Script compilation error: TypeLoadException.
+
+# Version 1.1.0 - 5 Feb 2016
+## New Features
+* Adds friendly alert when JAVA_HOME is not set on Windows platforms.
+* Adds flag for disabling background resolution.
+* Expands play-services-measurement and replaces ${applicationId} with the
+ bundle Id.
+
+ ## Bug Fixes
+* Fixes infinite loop of resolution triggered by resolution.
diff --git a/Assets/ExternalDependencyManager/Editor/CHANGELOG.md.meta b/Assets/ExternalDependencyManager/Editor/CHANGELOG.md.meta
new file mode 100644
index 0000000..2adfcdc
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/CHANGELOG.md.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b699fc553a294dbcad96ff0365038f6a
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/CHANGELOG.md
+timeCreated: 1584567712
+licenseType: Pro
+TextScriptImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll b/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll
new file mode 100755
index 0000000..ac732a7
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll differ
diff --git a/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll.meta b/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll.meta
new file mode 100644
index 0000000..f007e6e
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll.meta
@@ -0,0 +1,36 @@
+fileFormatVersion: 2
+guid: 61128ff4560e43ddb606dc203efe7799
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/Google.VersionHandler.dll
+- gvhp_targets-editor
+timeCreated: 1480838400
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ isPreloaded: 0
+ isOverridable: 0
+ platformData:
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 1
+ settings:
+ DefaultValueInitialized: true
+ - first:
+ Windows Store Apps: WindowsStoreApps
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb b/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb
new file mode 100755
index 0000000..217ac51
Binary files /dev/null and b/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb differ
diff --git a/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb.meta b/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb.meta
new file mode 100644
index 0000000..73c66db
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ef0c2c4976ca4593860e7d5871a226bf
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/Google.VersionHandler.pdb
+timeCreated: 1538009133
+licenseType: Pro
+DefaultImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/LICENSE b/Assets/ExternalDependencyManager/Editor/LICENSE
new file mode 100755
index 0000000..6258cc4
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/LICENSE
@@ -0,0 +1,245 @@
+Copyright (C) 2014 Google Inc.
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+====================================================================================================
+This package uses MiniJSON
+
+Copyright (c) 2013 Calvin Rien
+
+Based on the JSON parser by Patrick van Bergen
+http://techblog.procurios.nl/k/618/news/view/14605/14863/How-do-I-write-my-own-parser-for-JSON.html
+
+Simplified it so that it doesn't throw exceptions
+and can be used in Unity iPhone with maximum code stripping.
+
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of this software and associated documentation files (the
+"Software"), to deal in the Software without restriction, including
+without limitation the rights to use, copy, modify, merge, publish,
+distribute, sublicense, and/or sell copies of the Software, and to
+permit persons to whom the Software is furnished to do so, subject to
+the following conditions:
+
+The above copyright notice and this permission notice shall be
+included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
diff --git a/Assets/ExternalDependencyManager/Editor/LICENSE.meta b/Assets/ExternalDependencyManager/Editor/LICENSE.meta
new file mode 100644
index 0000000..811edab
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/LICENSE.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: b91d7551a5d9453e914e5295af46195e
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/LICENSE
+timeCreated: 1584567712
+licenseType: Pro
+TextScriptImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/README.md b/Assets/ExternalDependencyManager/Editor/README.md
new file mode 100755
index 0000000..3a7a5a2
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/README.md
@@ -0,0 +1,897 @@
+# External Dependency Manager for Unity
+
+[](https://openupm.com/packages/com.google.external-dependency-manager/)
+[](https://openupm.com/packages/com.google.external-dependency-manager/)
+
+## Overview
+
+The External Dependency Manager for Unity (EDM4U) (formerly Play Services
+Resolver/Jar Resolver) is intended to be used by any Unity package or user that
+requires:
+
+* Android specific libraries (e.g
+ [AARs](https://developer.android.com/studio/projects/android-library.html))
+
+* iOS [CocoaPods](https://cocoapods.org/)
+
+* Version management of transitive dependencies
+
+* Management of Package Manager (PM) Registries
+
+If you want to add and use iOS/Android dependencies directly in your project,
+then you should to install EDM4U in your project.
+
+If you are a package user and the plugin you are using depends on EDM4U, *and*
+the package does not include EDM4U as a package dependency already, then you
+should to install EDM4U in your project.
+
+If you are a UPM package maintainer and your package requires EDM4U, then you
+should add EDM4U as a
+[package dependency](https://docs.unity3d.com/2019.3/Documentation/Manual/upm-dependencies.html)
+in your package manifest (`package.json`):
+
+```json
+{
+ "dependencies": {
+ "com.google.external-dependency-manager": "1.2.178"
+ }
+}
+```
+
+You should still install EDM4U to test out the package during development.
+
+If you are a legacy `.unitypackage` package maintainer and your package requires
+EDM4U, please ask the user to install EDM4U separately. You should install EDM4U
+to test out the package during development.
+
+Updated releases are available on
+[GitHub](https://github.com/googlesamples/unity-jar-resolver)
+
+## Requirements
+
+The *Android Resolver* and *iOS Resolver* components of the plugin only work
+with Unity version 4.6.8 or higher.
+
+The *Version Handler* component only works with Unity 5.x or higher as it
+depends upon the `PluginImporter` UnityEditor API.
+
+The *Package Manager Resolver* component only works with Unity 2018.4 or above,
+when [scoped registry](https://docs.unity3d.com/Manual/upm-scoped.html) support
+was added to the Package Manager.
+
+## Getting Started
+
+Check out [troubleshooting](troubleshooting-faq.md) if you need help.
+
+### Install via OpenUPM
+
+EDM4U is available on
+[OpenUPM](https://openupm.com/packages/com.google.external-dependency-manager/):
+
+```shell
+openupm add com.google.external-dependency-manager
+```
+
+### Install via Google APIs for Unity
+
+EDM4U is available both in UPM and legacy `.unitypackage` formats on
+[Google APIs for Unity](https://developers.google.com/unity/archive#external_dependency_manager_for_unity).
+
+You may install the UPM version (.tgz) as a
+[local UPM package](https://docs.unity3d.com/Manual/upm-ui-local.html).
+
+You can also install EDM4U in your project as a `.unitypackage`. This is not
+recommended due to potential conflicts.
+
+### Conflict Resolution
+
+For historical reasons, a package maintainer may choose to embed EDM4U in their
+package for ease of installation. This will create a conflict when you try to
+install EDM4U with the steps above, or with another package with embedded EDM4U.
+If your project imported a `.unitypackage` that has a copy of EDM4U embedded in
+it, you may safely delete it from your Assets folder. If your project depends on
+another UPM package with EDM4U, please reach out to the package maintainer and
+ask them to replace it with a dependency to this package. In the meantime, you
+can workaround the issue by copying the package to your Packages folder (to
+create an
+[embedded package](https://docs.unity3d.com/Manual/upm-concepts.html#Embedded))
+and perform the steps yourself to avoid a dependency conflict.
+
+### Config file
+
+To start adding dependencies to your project, copy and rename the
+[SampleDependencies.xml](https://github.com/googlesamples/unity-jar-resolver/blob/master/sample/Assets/ExternalDependencyManager/Editor/SampleDependencies.xml)
+file into your plugin and add the dependencies your project requires.
+
+The XML file needs to be under an `Editor` directory and match the name
+`*Dependencies.xml`. For example, `MyPlugin/Editor/MyPluginDependencies.xml`.
+
+## Usages
+
+### Android Resolver
+
+The Android Resolver copies specified dependencies from local or remote Maven
+repositories into the Unity project when a user selects Android as the build
+target in the Unity editor.
+
+For example, to add the Google Play Games library
+(`com.google.android.gms:play-services-games` package) at version `9.8.0` to the
+set of a plugin's Android dependencies:
+
+```xml
+
+
+
+
+ extra-google-m2repository
+
+
+
+
+```
+
+The version specification (last component) supports:
+
+* Specific versions e.g `9.8.0`
+
+* Partial matches e.g `9.8.+` would match 9.8.0, 9.8.1 etc. choosing the most
+ recent version
+
+* Latest version using `LATEST` or `+`. We do *not* recommend using this
+ unless you're 100% sure the library you depend upon will not break your
+ Unity plugin in future
+
+The above example specifies the dependency as a component of the Android SDK
+manager such that the Android SDK manager will be executed to install the
+package if it's not found. If your Android dependency is located on Maven
+central it's possible to specify the package simply using the `androidPackage`
+element:
+
+```xml
+
+
+
+
+
+```
+
+#### Auto-resolution
+
+By default the Android Resolver automatically monitors the dependencies you have
+specified and the `Plugins/Android` folder of your Unity project. The resolution
+process runs when the specified dependencies are not present in your project.
+
+The *auto-resolution* process can be disabled via the `Assets > External
+Dependency Manager > Android Resolver > Settings` menu.
+
+Manual resolution can be performed using the following menu options:
+
+* `Assets > External Dependency Manager > Android Resolver > Resolve`
+
+* `Assets > External Dependency Manager > Android Resolver > Force Resolve`
+
+#### Deleting libraries
+
+Resolved packages are tracked via asset labels by the Android Resolver. They can
+easily be deleted using the `Assets > External Dependency Manager > Android
+Resolver > Delete Resolved Libraries` menu item.
+
+#### Android Manifest Variable Processing
+
+Some AAR files (for example play-services-measurement) contain variables that
+are processed by the Android Gradle plugin. Unfortunately, Unity does not
+perform the same processing when using Unity's Internal Build System, so the
+Android Resolver plugin handles known cases of this variable substitution by
+exploding the AAR into a folder and replacing `${applicationId}` with the
+`bundleID`.
+
+Disabling AAR explosion and therefore Android manifest processing can be done
+via the `Assets > External Dependency Manager > Android Resolver > Settings`
+menu. You may want to disable explosion of AARs if you're exporting a project to
+be built with Gradle/Android Studio.
+
+#### ABI Stripping
+
+Some AAR files contain native libraries (.so files) for each ABI supported by
+Android. Unfortunately, when targeting a single ABI (e.g x86), Unity does not
+strip native libraries for unused ABIs. To strip unused ABIs, the Android
+Resolver plugin explodes an AAR into a folder and removes unused ABIs to reduce
+the built APK size. Furthermore, if native libraries are not stripped from an
+APK (e.g you have a mix of Unity's x86 library and some armeabi-v7a libraries)
+Android may attempt to load the wrong library for the current runtime ABI
+completely breaking your plugin when targeting some architectures.
+
+AAR explosion and therefore ABI stripping can be disabled via the `Assets >
+External Dependency Manager > Android Resolver > Settings` menu. You may want to
+disable explosion of AARs if you're exporting a project to be built with
+Gradle/Android Studio.
+
+#### Resolution Strategies
+
+By default the Android Resolver will use Gradle to download dependencies prior
+to integrating them into a Unity project. This works with Unity's internal build
+system and Gradle/Android Studio project export.
+
+It's possible to change the resolution strategy via the `Assets > External
+Dependency Manager > Android Resolver > Settings` menu.
+
+##### Download Artifacts with Gradle
+
+Using the default resolution strategy, the Android resolver executes the
+following operations:
+
+- Remove the result of previous Android resolutions. E.g Delete all files and
+ directories labeled with "gpsr" under `Plugins/Android` from the project.
+
+- Collect the set of Android dependencies (libraries) specified by a project's
+ `*Dependencies.xml` files.
+
+- Run `download_artifacts.gradle` with Gradle to resolve conflicts and, if
+ successful, download the set of resolved Android libraries (AARs, JARs).
+
+- Process each AAR/JAR so that it can be used with the currently selected
+ Unity build system (e.g Internal vs. Gradle, Export vs. No Export). This
+ involves patching each reference to `applicationId` in the
+ `AndroidManifest.xml` with the project's bundle ID. This means resolution
+ must be run again if the bundle ID has changed.
+
+- Move the processed AARs to `Plugins/Android` so they will be included when
+ Unity invokes the Android build.
+
+##### Integrate into mainTemplate.gradle
+
+Unity 5.6 introduced support for customizing the `build.gradle` used to build
+Unity projects with Gradle. When the *Patch mainTemplate.gradle* setting is
+enabled, rather than downloading artifacts before the build, Android resolution
+results in the execution of the following operations:
+
+- Remove the result of previous Android resolutions. E.g Delete all files and
+ directories labeled with "gpsr" under `Plugins/Android` from the project and
+ remove sections delimited with `// Android Resolver * Start` and `// Android
+ Resolver * End` lines.
+
+- Collect the set of Android dependencies (libraries) specified by a project's
+ `*Dependencies.xml` files.
+
+- Rename any `.srcaar` files in the build to `.aar` and exclude them from
+ being included directly by Unity in the Android build as
+ `mainTemplate.gradle` will be patched to include them instead from their
+ local maven repositories.
+
+- Inject the required Gradle repositories into `mainTemplate.gradle` at the
+ line matching the pattern `.*apply plugin:
+ 'com\.android\.(application|library)'.*` or the section starting at the line
+ `// Android Resolver Repos Start`. If you want to control the injection
+ point in the file, the section delimited by the lines `// Android Resolver
+ Repos Start` and `// Android Resolver Repos End` should be placed in the
+ global scope before the `dependencies` section.
+
+- Inject the required Android dependencies (libraries) into
+ `mainTemplate.gradle` at the line matching the pattern `***DEPS***` or the
+ section starting at the line `// Android Resolver Dependencies Start`. If
+ you want to control the injection point in the file, the section delimited
+ by the lines `// Android Resolver Dependencies Start` and `// Android
+ Resolver Dependencies End` should be placed in the `dependencies` section.
+
+- Inject the packaging options logic, which excludes architecture specific
+ libraries based upon the selected build target, into `mainTemplate.gradle`
+ at the line matching the pattern `android +{` or the section starting at the
+ line `// Android Resolver Exclusions Start`. If you want to control the
+ injection point in the file, the section delimited by the lines `// Android
+ Resolver Exclusions Start` and `// Android Resolver Exclusions End` should
+ be placed in the global scope before the `android` section.
+
+#### Dependency Tracking
+
+The Android Resolver creates the
+`ProjectSettings/AndroidResolverDependencies.xml` to quickly determine the set
+of resolved dependencies in a project. This is used by the auto-resolution
+process to only run the expensive resolution process when necessary.
+
+#### Displaying Dependencies
+
+It's possible to display the set of dependencies the Android Resolver would
+download and process in your project via the `Assets > External Dependency
+Manager > Android Resolver > Display Libraries` menu item.
+
+### iOS Resolver
+
+The iOS resolver component of this plugin manages
+[CocoaPods](https://cocoapods.org/). A CocoaPods `Podfile` is generated and the
+`pod` tool is executed as a post build process step to add dependencies to the
+Xcode project exported by Unity.
+
+Dependencies for iOS are added by referring to CocoaPods.
+
+For example, to add the AdMob pod, version 7.0 or greater with bitcode enabled:
+
+```xml
+
+
+
+
+
+```
+
+#### Integration Strategies
+
+The `CocoaPods` are either:
+
+* Downloaded and injected into the Xcode project file directly, rather than
+ creating a separate xcworkspace. We call this `Xcode project` integration.
+
+* If the Unity version supports opening a xcworkspace file, the `pod` tool is
+ used as intended to generate a xcworkspace which references the CocoaPods.
+ We call this `Xcode workspace` integration.
+
+The resolution strategy can be changed via the `Assets > External Dependency
+Manager > iOS Resolver > Settings` menu.
+
+##### Appending text to generated Podfile
+
+In order to modify the generated Podfile you can create a script like this:
+
+```csharp
+using System.IO;
+
+using UnityEditor;
+using UnityEditor.Callbacks;
+using UnityEngine;
+
+public class PostProcessIOS : MonoBehaviour
+{
+ // Must be between 40 and 50 to ensure that it's not overriden by Podfile generation (40) and
+ // that it's added before "pod install" (50).
+ [PostProcessBuildAttribute(45)]
+ private static void PostProcessBuild_iOS(BuildTarget target, string buildPath)
+ {
+ if (target == BuildTarget.iOS)
+ {
+ using (StreamWriter sw = File.AppendText(buildPath + "/Podfile"))
+ {
+ // E.g. add an app extension
+ sw.WriteLine("\ntarget 'NSExtension' do\n pod 'Firebase/Messaging', '6.6.0'\nend");
+ }
+ }
+ }
+}
+```
+
+### Package Manager Resolver
+
+Adding registries to the
+[Package Manager](https://docs.unity3d.com/Manual/Packages.html) (PM) is a
+manual process. The Package Manager Resolver (PMR) component of this plugin
+makes it easy for plugin maintainers to distribute new PM registry servers and
+easy for plugin users to manage PM registry servers.
+
+#### Adding Registries
+
+For example, to add a registry for plugins in the scope `com.coolstuff`:
+
+```xml
+
+
+
+ com.coolstuff
+
+
+
+```
+
+When PMR is loaded it will prompt the developer to add the registry to their
+project if it isn't already present in the `Packages/manifest.json` file.
+
+For more information, see Unity's documentation on
+[scoped package registries](https://docs.unity3d.com/Manual/upm-scoped.html).
+
+#### Managing Registries
+
+It's possible to add and remove registries that are specified via PMR XML
+configuration files via the following menu options:
+
+* `Assets > External Dependency Manager > Package Manager Resolver > Add
+ Registries` will prompt the user with a window which allows them to add
+ registries discovered in the project to the Package Manager.
+
+* `Assets > External Dependency Manager > Package Manager Resolver > Remove
+ Registries` will prompt the user with a window which allows them to remove
+ registries discovered in the project from the Package Manager.
+
+* `Assets > External Dependency Manager > Package Manager Resolver > Modify
+ Registries` will prompt the user with a window which allows them to add or
+ remove registries discovered in the project.
+
+#### Migration
+
+PMR can migrate Version Handler packages installed in the `Assets` folder to PM
+packages. This requires the plugins to implement the following:
+
+* `.unitypackage` must include a Version Handler manifests that describes the
+ components of the plugin. If the plugin has no dependencies the manifest
+ would just include the files in the plugin.
+
+* The PM package JSON provided by the registry must include a keyword (in the
+ `versions.VERSION.keyword` list) that maps the PM package to a Version
+ Handler package using the format `vh-name:VERSION_HANDLER_MANIFEST_NAME`
+ where `VERSION_HANDLER_MANIFEST_NAME` is the name of the manifest defined in
+ the `.unitypackage`. For more information see the description of the
+ `gvhp_manifestname` asset label in the [Version Handler](#version-handler)
+ section.
+
+When using the `Assets > External Dependency Manager > Package Manager
+Resolver > Migrate Packages` menu option, PMR then will:
+
+* List all Version Handler manager packages in the project.
+
+* Search all available packages in the PM registries and fetch keywords
+ associated with each package parsing the Version Handler manifest names for
+ each package.
+
+* Map each installed Version Handler package to a PM package.
+
+* Prompt the user to migrate the discovered packages.
+
+* Perform package migration for all selected packages if the user clicks the
+ `Apply` button.
+
+#### Configuration
+
+PMR can be configured via the `Assets > External Dependency Manager > Package
+Manager Resolver > Settings` menu option:
+
+* `Add package registries` when enabled, when the plugin loads or registry
+ configuration files change, this will prompt the user to add registries that
+ are not present in the Package Manager.
+
+* `Prompt to add package registries` will cause a developer to be prompted
+ with a window that will ask for confirmation before adding registries. When
+ this is disabled registries are added silently to the project.
+
+* `Prompt to migrate packages` will cause a developer to be prompted with a
+ window that will ask for confirmation before migrating packages installed in
+ the `Assets` directory to PM packages.
+
+* `Enable Analytics Reporting` when enabled, reports the use of the plugin to
+ the developers so they can make imrpovements.
+
+* `Verbose logging` when enabled prints debug information to the console which
+ can be useful when filing bug reports.
+
+### Version Handler
+
+The Version Handler component of this plugin manages:
+
+* Shared Unity plugin dependencies.
+
+* Upgrading Unity plugins by cleaning up old files from previous versions.
+
+* Uninstallation of plugins that are distributed with manifest files.
+
+* Restoration of plugin assets to their original install locations if assets
+ are tagged with the `exportpath` label.
+
+Since the Version Handler needs to modify Unity asset metadata (`.meta` files),
+to enable/disable components, rename and delete asset files it does not work
+with Package Manager installed packages. It's still possible to include EDM4U in
+Package Manager packages, the Version Handler component simply won't do anything
+to PM plugins in this case.
+
+#### Using Version Handler Managed Plugins
+
+If a plugin is imported at multiple different versions into a project, if the
+Version Handler is enabled, it will automatically check all managed assets to
+determine the set of assets that are out of date and assets that should be
+removed. To disable automatic checking managed assets disable the `Enable
+version management` option in the `Assets > External Dependency Manager >
+Version Handler > Settings` menu.
+
+If version management is disabled, it's possible to check managed assets
+manually using the `Assets > External Dependency Manager > Version Handler >
+Update` menu option.
+
+##### Listing Managed Plugins
+
+Plugins managed by the Version Handler, those that ship with manifest files, can
+displayed using the `Assets > External Dependency Manager > Version Handler >
+Display Managed Packages` menu option. The list of plugins are written to the
+console window along with the set of files used by each plugin.
+
+##### Uninstalling Managed Plugins
+
+Plugins managed by the Version Handler, those that ship with manifest files, can
+be removed using the `Assets > External Dependency Manager > Version Handler >
+Uninstall Managed Packages` menu option. This operation will display a window
+that allows a developer to select a set of plugins to remove which will remove
+all files owned by each plugin excluding those that are in use by other
+installed plugins.
+
+Files managed by the Version Handler, those labeled with the `gvh` asset label,
+can be checked to see whether anything needs to be upgraded, disabled or removed
+using the `Assets > External Dependency Manager > Version Handler > Update` menu
+option.
+
+##### Restore Install Paths
+
+Some developers move assets around in their project which can make it harder for
+plugin maintainers to debug issues if this breaks Unity's
+[special folders](https://docs.unity3d.com/Manual/SpecialFolders.html) rules. If
+assets are labeled with their original install/export path (see
+`gvhp_exportpath` below), Version Handler can restore assets to their original
+locations when using the `Assets > External Dependency Manager > Version
+Handler > Move Files To Install Locations` menu option.
+
+##### Settings
+
+Some behavior of the Version Handler can be configured via the `Assets >
+External Dependency Manager > Version Handler > Settings` menu option.
+
+* `Enable version management` controls whether the plugin should automatically
+ check asset versions and apply changes. If this is disabled the process
+ should be run manually when installing or upgrading managed plugins using
+ `Assets > External Dependency Manager > Version Handler > Update`.
+
+* `Rename to canonical filenames` is a legacy option that will rename files to
+ remove version numbers and other labels from filenames.
+
+* `Prompt for obsolete file deletion` enables the display of a window when
+ obsolete files are deleted allowing the developer to select which files to
+ delete and those to keep.
+
+* `Allow disabling files via renaming` controls whether obsolete or disabled
+ files should be disabled by renaming them to `myfilename_DISABLED`. Renaming
+ to disable files is required in some scenarios where Unity doesn't support
+ removing files from the build via the PluginImporter.
+
+* `Enable Analytics Reporting` enables/disables usage reporting to plugin
+ developers to improve the product.
+
+* `Verbose logging` enables *very* noisy log output that is useful for
+ debugging while filing a bug report or building a new managed plugin.
+
+* `Use project settings` saves settings for the plugin in the project rather
+ than system-wide.
+
+#### Redistributing a Managed Plugin
+
+The Version Handler employs a couple of methods for managing version selection,
+upgrade and removal of plugins.
+
+* Each plugin can ship with a manifest file that lists the files it includes.
+ This makes it possible for Version Handler to calculate the difference in
+ assets between the most recent release of a plugin and the previous release
+ installed in a project. If a files are removed the Version Handler will
+ prompt the user to clean up obsolete files.
+
+* Plugins can ship using assets with unique names, unique GUIDs and version
+ number labels. Version numbers can be attached to assets using labels or
+ added to the filename (e.g `myfile.txt` would be `myfile_version-x.y.z.txt).
+ This allows the Version Handler to determine which set of files are the same
+ file at different versions, select the most recent version and prompt the
+ developer to clean up old versions.
+
+Unity plugins can be managed by the Version Handler using the following steps:
+
+1. Add the `gvh` asset label to each asset (file) you want Version Handler to
+ manage.
+
+1. Add the `gvh_version-VERSION` label to each asset where `VERSION` is the
+ version of the plugin you're releasing (e.g 1.2.3).
+
+1. Add the `gvhp_exportpath-PATH` label to each asset where `PATH` is the
+ export path of the file when the `.unitypackage` is created. This is used to
+ track files if they're moved around in a project by developers.
+
+1. Optional: Add `gvh_targets-editor` label to each editor DLL in your plugin
+ and disable `editor` as a target platform for the DLL. The Version Handler
+ will enable the most recent version of this DLL when the plugin is imported.
+
+1. Optional: If your plugin is included in other Unity plugins, you should add
+ the version number to each filename and change the GUID of each asset. This
+ allows multiple versions of your plugin to be imported into a Unity project,
+ with the Version Handler component activating only the most recent version.
+
+1. Create a manifest text file named `MY_UNIQUE_PLUGIN_NAME_VERSION.txt` that
+ lists all the files in your plugin relative to the project root. Then add
+ the `gvh_manifest` label to the asset to indicate this file is a plugin
+ manifest.
+
+1. Optional: Add a `gvhp_manifestname-NAME` label to your manifest file to
+ provide a human readable name for your package. If this isn't provided the
+ name of the manifest file will be used as the package name. NAME can match
+ the pattern `[0-9]+[a-zA-Z -]` where a leading integer will set the priority
+ of the name where `0` is the highest priority and preferably used as the
+ display name. The lowest value (i.e highest priority name) will be used as
+ the display name and all other specified names will be aliases of the
+ display name. Aliases can refer to previous names of the package allowing
+ renaming across published versions.
+
+1. Redistribute EDM4U Unity plugin with your plugin. See the
+ [Plugin Redistribution](#plugin-redistribution) section for details.
+
+If you follow these steps:
+
+* When users import a newer version of your plugin, files referenced by the
+ older version's manifest are cleaned up.
+
+* The latest version of the plugin will be selected when users import multiple
+ packages that include your plugin, assuming the steps in
+ [Plugin Redistribution](#plugin-redistribution) are followed.
+
+## Background
+
+Many Unity plugins have dependencies upon Android specific libraries, iOS
+CocoaPods, and sometimes have transitive dependencies upon other Unity plugins.
+This causes the following problems:
+
+* Integrating platform specific (e.g Android and iOS) libraries within a Unity
+ project can be complex and a burden on a Unity plugin maintainer.
+* The process of resolving conflicting dependencies on platform specific
+ libraries is pushed to the developer attempting to use a Unity plugin. The
+ developer trying to use your plugin is very likely to give up when faced
+ with Android or iOS specific build errors.
+* The process of resolving conflicting Unity plugins (due to shared Unity
+ plugin components) is pushed to the developer attempting to use your Unity
+ plugin. In an effort to resolve conflicts, the developer will very likely
+ attempt to resolve problems by deleting random files in your plugin, report
+ bugs when that doesn't work and finally give up.
+
+EDM4U provides solutions for each of these problems.
+
+### Android Dependency Management
+
+The *Android Resolver* component of this plugin will download and integrate
+Android library dependencies and handle any conflicts between plugins that share
+the same dependencies.
+
+Without the Android Resolver, typically Unity plugins bundle their AAR and JAR
+dependencies, e.g. a Unity plugin `SomePlugin` that requires the Google Play
+Games Android library would redistribute the library and its transitive
+dependencies in the folder `SomePlugin/Android/`. When a user imports
+`SomeOtherPlugin` that includes the same libraries (potentially at different
+versions) in `SomeOtherPlugin/Android/`, the developer using `SomePlugin` and
+`SomeOtherPlugin` will see an error when building for Android that can be hard
+to interpret.
+
+Using the Android Resolver to manage Android library dependencies:
+
+* Solves Android library conflicts between plugins.
+* Handles all of the various processing steps required to use Android
+ libraries (AARs, JARs) in Unity 4.x and above projects. Almost all versions
+ of Unity have - at best - partial support for AARs.
+* (Experimental) Supports minification of included Java components without
+ exporting a project.
+
+### iOS Dependency Management
+
+The *iOS Resolver* component of this plugin integrates with
+[CocoaPods](https://cocoapods.org/) to download and integrate iOS libraries and
+frameworks into the Xcode project Unity generates when building for iOS. Using
+CocoaPods allows multiple plugins to utilize shared components without forcing
+developers to fix either duplicate or incompatible versions of libraries
+included through multiple Unity plugins in their project.
+
+### Package Manager Registry Setup
+
+The [Package Manager](https://docs.unity3d.com/Manual/Packages.html) (PM) makes
+use of [NPM](https://www.npmjs.com/) registry servers for package hosting and
+provides ways to discover, install, upgrade and uninstall packages. This makes
+it easier for developers to manage plugins within their projects.
+
+However, installing additional package registries requires a few manual steps
+that can potentially be error prone. The *Package Manager Resolver* component of
+this plugin integrates with [PM](https://docs.unity3d.com/Manual/Packages.html)
+to provide a way to auto-install PM package registries when a `.unitypackage` is
+installed which allows plugin maintainers to ship a `.unitypackage` that can
+provide access to their own PM registry server to make it easier for developers
+to manage their plugins.
+
+### Unity Plugin Version Management
+
+Finally, the *Version Handler* component of this plugin simplifies the process
+of managing transitive dependencies of Unity plugins and each plugin's upgrade
+process.
+
+For example, without the Version Handler plugin, if:
+
+* Unity plugin `SomePlugin` includes `EDM4U` plugin at version 1.1.
+* Unity plugin `SomeOtherPlugin` includes `EDM4U` plugin at version 1.2.
+
+The version of `EDM4U` included in the developer's project depends upon the
+order the developer imports `SomePlugin` or `SomeOtherPlugin`.
+
+This results in:
+
+* `EDM4U` at version 1.2, if `SomePlugin` is imported then `SomeOtherPlugin`
+ is imported.
+* `EDM4U` at version 1.1, if `SomeOtherPlugin` is imported then `SomePlugin`
+ is imported.
+
+The Version Handler solves the problem of managing transitive dependencies by:
+
+* Specifying a set of packaging requirements that enable a plugin at different
+ versions to be imported into a Unity project.
+* Providing activation logic that selects the latest version of a plugin
+ within a project.
+
+When using the Version Handler to manage `EDM4U` included in `SomePlugin` and
+`SomeOtherPlugin`, from the prior example, version 1.2 will always be the
+version activated in a developer's Unity project.
+
+Plugin creators are encouraged to adopt this library to ease integration for
+their customers. For more information about integrating EDM4U into your own
+plugin, see the [Plugin Redistribution](#plugin-redistribution) section of this
+document.
+
+## Analytics
+
+The External Dependency Manager for Unity plugin by default logs usage to Google
+Analytics. The purpose of the logging is to quantitatively measure the usage of
+functionality, to gather reports on integration failures and to inform future
+improvements to the developer experience of the External Dependency Manager
+plugin. Note that the analytics collected are limited to the scope of the EDM4U
+plugin’s usage.
+
+For details of what is logged, please refer to the usage of
+`EditorMeasurement.Report()` in the source code.
+
+## Plugin Redistribution
+
+If you are a package maintainer and your package depends on EDM4U, it is highly
+recommended to use the UPM format and add EDM4U as a dependency. If you must
+include it in your `.unitypackage`, redistributing `EDM4U` inside your own
+plugin might ease the integration process for your users.
+
+If you wish to redistribute `EDM4U` inside your plugin, you **must** follow
+these steps when importing the `external-dependency-manager-*.unitypackage`, and
+when exporting your own plugin package:
+
+1. Import the `external-dependency-manager-*.unitypackage` into your plugin
+ project by
+ [running Unity from the command line](https://docs.unity3d.com/Manual/CommandLineArguments.html),
+ ensuring that you add the `-gvh_disable` option.
+1. Export your plugin by
+ [running Unity from the command line](https://docs.unity3d.com/Manual/CommandLineArguments.html),
+ ensuring that you:
+ - Include the contents of the `Assets/PlayServicesResolver` and
+ `Assets/ExternalDependencyManager` directory.
+ - Add the `-gvh_disable` option.
+
+You **must** specify the `-gvh_disable` option in order for the Version Handler
+to work correctly!
+
+For example, the following command will import the
+`external-dependency-manager-1.2.46.0.unitypackage` into the project
+`MyPluginProject` and export the entire Assets folder to
+`MyPlugin.unitypackage`:
+
+```shell
+Unity -gvh_disable \
+ -batchmode \
+ -importPackage external-dependency-manager-1.2.46.0.unitypackage \
+ -projectPath MyPluginProject \
+ -exportPackage Assets MyPlugin.unitypackage \
+ -quit
+```
+
+### Background
+
+The *Version Handler* component relies upon deferring the load of editor DLLs so
+that it can run first and determine the latest version of a plugin component to
+activate. The build of `EDM4U` plugin has Unity asset metadata that is
+configured so that the editor components are not initially enabled when it's
+imported into a Unity project. To maintain this configuration when importing the
+`external-dependency-manager.unitypackage` into a Unity plugin project, you
+*must* specify the command line option `-gvh_disable` which will prevent the
+Version Handler component from running and changing the Unity asset metadata.
+
+## Building from Source
+
+To build this plugin from source you need the following tools installed: * Unity
+2021 and below (with iOS and Android modules installed) * Java 11
+
+You can build the plugin by running the following from your shell (Linux / OSX):
+
+```shell
+./gradlew build
+
+```
+
+or Windows:
+
+```shell
+./gradlew.bat build
+```
+
+If Java 11 is not your default Java command, add
+`-Dorg.gradle.java.home=` to the command above.
+
+## Testing
+
+You can run the tests by running the following from your shell (Linux / OSX):
+
+```shell
+./gradlew test
+```
+
+or Windows:
+
+```shell
+./gradlew.bat test
+```
+
+The following properties can be set to narrow down the tests to run or change
+the test run behavior.
+
+* `INTERACTIVE_MODE_TESTS_ENABLED` - Default to `1`. Set to `1` to enable
+ interactive mode tests, which requires GPU on the machine. Otherwise, only
+ run tests in the batch mode.
+* `INCLUDE_TEST_TYPES` - Default to empty string, which means to include every
+ type of the test. To narrow down the types of test to run, set this
+ properties with a list of case-insensitive type strings separated by comma.
+ For instance, `-PINCLUDE_TEST_TYPES="Python,NUnit"` means to include only
+ Python tests and NUnit tests. See `TestTypeEnum` in `build.gradle` for
+ available options.
+* `EXCLUDE_TEST_TYPES` - Default to empty string, which means to exclude none.
+ To add types of tests to exclude, set this properties with a list of
+ case-insensitive type strings separated by comma. For instance,
+ `-PEXCLUDE_TEST_TYPES="Python,NUnit"` means to exclude Python tests and
+ NUnit tests. See `TestTypeEnum` in `build.gradle` for available options.
+* `INCLUDE_TEST_MODULES` - Default to empty string, which means to include the
+ tests for every modules. To narrow down modules to test, set this properties
+ with a list of case-insensitive module strings separated by comma. For
+ instance, `-PINCLUDE_TEST_MODULES="Tool,AndroidResolver"` means to run tests
+ for tools and Android Resolver only. See `TestModuleEnum` in `build.gradle`
+ for available options.
+* `EXCLUDE_TEST_MODULES` - Default to empty string, which means to exclude
+ none. To add modules to exclude, set this properties with a list of
+ case-insensitive module strings separated by comma. For instance,
+ `-PEXCLUDE_TEST_MODULES="Tool,AndroidResolver"` means to run tests for any
+ modules other than tools and Android Resolver. See `TestModuleEnum` in
+ `build.gradle` for available options.
+* `EXCLUDE_TESTS` - Default to empty string, which means to exclude none. To
+ add tests to exclude, set this properties with a list of case-insensitive
+ test names separated by comma. For instance,
+ `-PEXCLUDE_TESTS="testGenGuids,testDownloadArtifacts"` means to run tests
+ except the tests with name of `testGenGuids` and `testDownloadArtifacts`.
+* `CONTINUE_ON_FAIL_FOR_TESTS_ENABLED` - Default to `1`. Set to `1` to
+ continue running the next test when the current one fails. Otherwise, the
+ build script stops whenever any test fails.
+
+For instance, by running the following command, it only runs the Unity
+integration tests that does not requires GPU, but exclude tests for Android
+Resolver module and iOS Resolver module.
+
+```shell
+./gradlew test \
+ -PINTERACTIVE_MODE_TESTS_ENABLED=0 \
+ -PINCLUDE_TEST_TYPES="Integration" \
+ -PEXCLUDE_TEST_MODULES="AndroidResolver,iOSResolver"
+```
+
+## Releasing
+
+Each time a new build of this plugin is checked into the source tree you need to
+do the following:
+
+* Bump the plugin version variable `pluginVersion` in `build.gradle`
+* Update `CHANGELOG.md` with the new version number and changes included in
+ the release.
+* Build the release using `./gradlew release` which performs the following:
+ * Updates `external-dependency-manager-*.unitypackage`
+ * Copies the unpacked plugin to the `exploded` directory.
+ * Updates template metadata files in the `plugin` directory. The GUIDs of
+ all asset metadata is modified due to the version number change. Each
+ file within the plugin is versioned to allow multiple versions of the
+ plugin to be imported into a Unity project which allows the most recent
+ version to be activated by the Version Handler component.
+* Create release commit using `./gradlew gitCreateReleaseCommit` which
+ performs `git commit -a -m "description from CHANGELOG.md"`
+* Once the release commit is merge, tag the release using `./gradlew
+ gitTagRelease` which performs the following:
+ * `git tag -a pluginVersion -m "version RELEASE"` to tag the release.
+* Update tags on remote branch using `git push --tag REMOTE HEAD:master`
diff --git a/Assets/ExternalDependencyManager/Editor/README.md.meta b/Assets/ExternalDependencyManager/Editor/README.md.meta
new file mode 100644
index 0000000..1246827
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/README.md.meta
@@ -0,0 +1,12 @@
+fileFormatVersion: 2
+guid: ee2d63ed1abf409b893e36120a404f03
+labels:
+- gvh
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/README.md
+timeCreated: 1584567712
+licenseType: Pro
+TextScriptImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.182_manifest.txt b/Assets/ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.182_manifest.txt
new file mode 100755
index 0000000..d1496bc
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.182_manifest.txt
@@ -0,0 +1,13 @@
+Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.dll
+Assets/ExternalDependencyManager/Editor/1.2.182/Google.IOSResolver.pdb
+Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.dll
+Assets/ExternalDependencyManager/Editor/1.2.182/Google.JarResolver.pdb
+Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.dll
+Assets/ExternalDependencyManager/Editor/1.2.182/Google.PackageManagerResolver.pdb
+Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.dll
+Assets/ExternalDependencyManager/Editor/1.2.182/Google.VersionHandlerImpl.pdb
+Assets/ExternalDependencyManager/Editor/CHANGELOG.md
+Assets/ExternalDependencyManager/Editor/Google.VersionHandler.dll
+Assets/ExternalDependencyManager/Editor/Google.VersionHandler.pdb
+Assets/ExternalDependencyManager/Editor/LICENSE
+Assets/ExternalDependencyManager/Editor/README.md
diff --git a/Assets/ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.182_manifest.txt.meta b/Assets/ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.182_manifest.txt.meta
new file mode 100644
index 0000000..caa5f7c
--- /dev/null
+++ b/Assets/ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.182_manifest.txt.meta
@@ -0,0 +1,15 @@
+fileFormatVersion: 2
+guid: d602686ba68d4bfea020d161c28431a9
+labels:
+- gvh
+- gvh_manifest
+- gvh_version-1.2.182
+- gvhp_exportpath-ExternalDependencyManager/Editor/external-dependency-manager_version-1.2.182_manifest.txt
+- gvhp_manifestname-0External Dependency Manager
+- gvhp_manifestname-play-services-resolver
+timeCreated: 1474401009
+licenseType: Pro
+TextScriptImporter:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk.meta b/Assets/MaxSdk.meta
new file mode 100644
index 0000000..112b305
--- /dev/null
+++ b/Assets/MaxSdk.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2d216a0b84c904a88a10298f917ba2cd
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin.meta b/Assets/MaxSdk/AppLovin.meta
new file mode 100644
index 0000000..2129078
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 79c57f6056b814a999d4ffd4d2946e01
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Editor.meta b/Assets/MaxSdk/AppLovin/Editor.meta
new file mode 100644
index 0000000..68829d8
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Editor.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 231b46ea1453e421ca13827c9289af31
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Editor/Dependencies.xml b/Assets/MaxSdk/AppLovin/Editor/Dependencies.xml
new file mode 100644
index 0000000..6afe371
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Editor/Dependencies.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/AppLovin/Editor/Dependencies.xml.meta b/Assets/MaxSdk/AppLovin/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..3360fd3
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 746da98302dc0402aaa37e8f7bd97e77
+labels:
+- al_max
+- al_max_export_path-MaxSdk/AppLovin/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Plugins.meta b/Assets/MaxSdk/AppLovin/Plugins.meta
new file mode 100644
index 0000000..f556718
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 08b49ba194f43487398896a722dc111a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Plugins/Android.meta b/Assets/MaxSdk/AppLovin/Plugins/Android.meta
new file mode 100644
index 0000000..d2cbee9
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/Android.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 4054ff186f3d14e4ba68a803fd7b933e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Plugins/Android/applovin-max-unity-plugin.aar b/Assets/MaxSdk/AppLovin/Plugins/Android/applovin-max-unity-plugin.aar
new file mode 100644
index 0000000..ef1e929
Binary files /dev/null and b/Assets/MaxSdk/AppLovin/Plugins/Android/applovin-max-unity-plugin.aar differ
diff --git a/Assets/MaxSdk/AppLovin/Plugins/Android/applovin-max-unity-plugin.aar.meta b/Assets/MaxSdk/AppLovin/Plugins/Android/applovin-max-unity-plugin.aar.meta
new file mode 100644
index 0000000..224076a
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/Android/applovin-max-unity-plugin.aar.meta
@@ -0,0 +1,35 @@
+fileFormatVersion: 2
+guid: 1db980bd612824d2097f78fd779e6051
+labels:
+- al_max
+- al_max_export_path-MaxSdk/AppLovin/Plugins/Android/applovin-max-unity-plugin.aar
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ Android: Android
+ second:
+ enabled: 1
+ settings: {}
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ DefaultValueInitialized: true
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Plugins/iOS.meta b/Assets/MaxSdk/AppLovin/Plugins/iOS.meta
new file mode 100644
index 0000000..1109614
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/iOS.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 215d193da15e944e0b8ac7e4978b8f20
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.h b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.h
new file mode 100644
index 0000000..117c7fd
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.h
@@ -0,0 +1,97 @@
+//
+// MAUnityAdManager.h
+// AppLovin MAX Unity Plugin
+//
+
+#import
+#import
+
+NS_ASSUME_NONNULL_BEGIN
+typedef const void *MAUnityRef;
+typedef void (*ALUnityBackgroundCallback)(const char* args);
+
+@interface MAUnityAdManager : NSObject
+
+- (void)initializeSdkWithConfiguration:(ALSdkInitializationConfiguration *)initConfig andCompletionHandler:(ALSdkInitializationCompletionHandler)completionHandler;
+
+- (void)createBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier atPosition:(nullable NSString *)bannerPosition isAdaptive:(BOOL)isAdaptive;
+- (void)createBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier x:(CGFloat)xOffset y:(CGFloat)yOffset isAdaptive:(BOOL)isAdaptive;
+- (void)loadBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)setBannerBackgroundColorForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier hexColorCode:(nullable NSString *)hexColorCode;
+- (void)setBannerPlacement:(nullable NSString *)placement forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)startBannerAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)stopBannerAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)setBannerExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value;
+- (void)setBannerLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value;
+- (void)setBannerCustomData:(nullable NSString *)customData forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)setBannerWidth:(CGFloat)width forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)updateBannerPosition:(nullable NSString *)bannerPosition forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)updateBannerPosition:(CGFloat)xOffset y:(CGFloat)yOffset forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)showBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)destroyBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)hideBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (NSString *)bannerLayoutForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
++ (CGFloat)adaptiveBannerHeightForWidth:(CGFloat)width;
+
+- (void)createMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier atPosition:(nullable NSString *)mrecPosition;
+- (void)createMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier x:(CGFloat)xOffset y:(CGFloat)yOffset;
+- (void)loadMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)setMRecPlacement:(nullable NSString *)placement forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)startMRecAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)stopMRecAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifer;
+- (void)setMRecExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value;
+- (void)setMRecLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value;
+- (void)setMRecCustomData:(nullable NSString *)customData forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)showMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)destroyMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)hideMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)updateMRecPosition:(nullable NSString *)mrecPosition forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)updateMRecPosition:(CGFloat)xOffset y:(CGFloat)yOffset forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (NSString *)mrecLayoutForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+
+- (void)loadInterstitialWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (BOOL)isInterstitialReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)showInterstitialWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData;
+- (void)setInterstitialExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value;
+- (void)setInterstitialLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value;
+
+- (void)loadAppOpenAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (BOOL)isAppOpenAdReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)showAppOpenAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData;
+- (void)setAppOpenAdExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value;
+- (void)setAppOpenAdLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value;
+
+- (void)loadRewardedAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (BOOL)isRewardedAdReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+- (void)showRewardedAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData;
+- (void)setRewardedAdExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value;
+- (void)setRewardedAdLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value;
+
+// Event Tracking
+- (void)trackEvent:(nullable NSString *)event parameters:(nullable NSString *)parameters;
+
+// Ad Value
+- (NSString *)adValueForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier withKey:(nullable NSString *)key;
+
+// User Service
+- (void)didDismissUserConsentDialog;
+
+// CMP Service
+- (void)showCMPForExistingUser;
+
+// Utils
++ (NSString *)serializeParameters:(NSDictionary *)dict;
++ (NSDictionary *)deserializeParameters:(nullable NSString *)serialized;
+
++ (void)setUnityBackgroundCallback:(ALUnityBackgroundCallback)unityBackgroundCallback;
+
+/**
+ * Creates an instance of @c MAUnityAdManager if needed and returns the singleton instance.
+ */
++ (instancetype)shared;
+
+- (instancetype)init NS_UNAVAILABLE;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.h.meta b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.h.meta
new file mode 100644
index 0000000..aef1db5
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.h.meta
@@ -0,0 +1,115 @@
+fileFormatVersion: 2
+guid: 4209563f82b8a4f7dabf03705ab761c6
+labels:
+- al_max
+- al_max_export_path-MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.h
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ '': Any
+ second:
+ enabled: 0
+ settings:
+ Exclude Android: 1
+ Exclude Editor: 1
+ Exclude Linux: 1
+ Exclude Linux64: 1
+ Exclude LinuxUniversal: 1
+ Exclude OSXUniversal: 1
+ Exclude Win: 1
+ Exclude Win64: 1
+ Exclude iOS: 0
+ Exclude tvOS: 1
+ - first:
+ Android: Android
+ second:
+ enabled: 0
+ settings:
+ CPU: ARMv7
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ DefaultValueInitialized: true
+ OS: AnyOS
+ - first:
+ Facebook: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Facebook: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Linux
+ second:
+ enabled: 0
+ settings:
+ CPU: x86
+ - first:
+ Standalone: Linux64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: LinuxUniversal
+ second:
+ enabled: 0
+ settings:
+ CPU: None
+ - first:
+ Standalone: OSXUniversal
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ iPhone: iOS
+ second:
+ enabled: 1
+ settings:
+ CompileFlags:
+ FrameworkDependencies:
+ - first:
+ tvOS: tvOS
+ second:
+ enabled: 0
+ settings:
+ CompileFlags:
+ FrameworkDependencies:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.m b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.m
new file mode 100644
index 0000000..a7bd666
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.m
@@ -0,0 +1,2067 @@
+//
+// MAUnityAdManager.m
+// AppLovin MAX Unity Plugin
+//
+
+#import "MAUnityAdManager.h"
+
+#define KEY_WINDOW [UIApplication sharedApplication].keyWindow
+#define DEVICE_SPECIFIC_ADVIEW_AD_FORMAT ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad) ? MAAdFormat.leader : MAAdFormat.banner
+#define IS_VERTICAL_BANNER_POSITION(_POS) ( [@"center_left" isEqual: adViewPosition] || [@"center_right" isEqual: adViewPosition] )
+#define DEGREES_TO_RADIANS(angle) ((angle) / 180.0 * M_PI)
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+ extern bool max_unity_should_disable_all_logs(void); // Forward declaration
+
+ // UnityAppController.mm
+ UIViewController* UnityGetGLViewController(void);
+ UIWindow* UnityGetMainWindow(void);
+
+ // life cycle management
+ int UnityIsPaused(void);
+ void UnityPause(int pause);
+
+ void max_unity_dispatch_on_main_thread(dispatch_block_t block)
+ {
+ if ( block )
+ {
+ if ( [NSThread isMainThread] )
+ {
+ block();
+ }
+ else
+ {
+ dispatch_async(dispatch_get_main_queue(), block);
+ }
+ }
+ }
+#ifdef __cplusplus
+}
+#endif
+
+@interface MAUnityAdManager()
+
+// Parent Fields
+@property (nonatomic, weak) ALSdk *sdk;
+
+// Fullscreen Ad Fields
+@property (nonatomic, strong) NSMutableDictionary *interstitials;
+@property (nonatomic, strong) NSMutableDictionary *appOpenAds;
+@property (nonatomic, strong) NSMutableDictionary *rewardedAds;
+
+// AdView Fields
+@property (nonatomic, strong) NSMutableDictionary *adViews;
+@property (nonatomic, strong) NSMutableDictionary *adViewAdFormats;
+@property (nonatomic, strong) NSMutableDictionary *adViewPositions;
+@property (nonatomic, strong) NSMutableDictionary *adViewOffsets;
+@property (nonatomic, strong) NSMutableDictionary *adViewWidths;
+@property (nonatomic, strong) NSMutableDictionary *verticalAdViewFormats;
+@property (nonatomic, strong) NSMutableDictionary *> *adViewConstraints;
+@property (nonatomic, strong) NSMutableDictionary *> *adViewExtraParametersToSetAfterCreate;
+@property (nonatomic, strong) NSMutableDictionary *> *adViewLocalExtraParametersToSetAfterCreate;
+@property (nonatomic, strong) NSMutableDictionary *adViewCustomDataToSetAfterCreate;
+@property (nonatomic, strong) NSMutableArray *adUnitIdentifiersToShowAfterCreate;
+@property (nonatomic, strong) NSMutableSet *disabledAdaptiveBannerAdUnitIdentifiers;
+@property (nonatomic, strong) NSMutableSet *disabledAutoRefreshAdViewAdUnitIdentifiers;
+@property (nonatomic, strong) NSMutableSet *ignoreSafeAreaLandscapeAdUnitIdentifiers;
+@property (nonatomic, strong) UIView *safeAreaBackground;
+@property (nonatomic, strong, nullable) UIColor *publisherBannerBackgroundColor;
+
+@property (nonatomic, strong) NSMutableDictionary *adInfoDict;
+@property (nonatomic, strong) NSObject *adInfoDictLock;
+
+@property (nonatomic, strong) NSOperationQueue *backgroundCallbackEventsQueue;
+@property (nonatomic, assign) BOOL resumeUnityAfterApplicationBecomesActive;
+
+@end
+
+// Internal
+@interface UIColor (ALUtils)
++ (nullable UIColor *)al_colorWithHexString:(NSString *)hexString;
+@end
+
+@interface NSNumber (ALUtils)
++ (NSNumber *)al_numberWithString:(NSString *)string;
+@end
+
+@interface NSString (ALUtils)
+@property (assign, readonly, getter=al_isValidString) BOOL al_validString;
+@end
+
+@interface MAAdFormat (ALUtils)
+@property (nonatomic, assign, readonly, getter=isFullscreenAd) BOOL fullscreenAd;
+@property (nonatomic, assign, readonly, getter=isAdViewAd) BOOL adViewAd;
+@end
+
+@implementation MAUnityAdManager
+static NSString *const SDK_TAG = @"AppLovinSdk";
+static NSString *const TAG = @"MAUnityAdManager";
+static NSString *const DEFAULT_AD_VIEW_POSITION = @"top_left";
+static ALUnityBackgroundCallback backgroundCallback;
+
+#pragma mark - Initialization
+
+- (instancetype)init
+{
+ self = [super init];
+ if ( self )
+ {
+ self.interstitials = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.appOpenAds = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.rewardedAds = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.adViews = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.adViewAdFormats = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.adViewPositions = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.adViewOffsets = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.adViewWidths = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.verticalAdViewFormats = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.adViewConstraints = [NSMutableDictionary dictionaryWithCapacity: 2];
+ self.adViewExtraParametersToSetAfterCreate = [NSMutableDictionary dictionaryWithCapacity: 1];
+ self.adViewLocalExtraParametersToSetAfterCreate = [NSMutableDictionary dictionaryWithCapacity: 1];
+ self.adViewCustomDataToSetAfterCreate = [NSMutableDictionary dictionaryWithCapacity: 1];
+ self.adUnitIdentifiersToShowAfterCreate = [NSMutableArray arrayWithCapacity: 2];
+ self.disabledAdaptiveBannerAdUnitIdentifiers = [NSMutableSet setWithCapacity: 2];
+ self.disabledAutoRefreshAdViewAdUnitIdentifiers = [NSMutableSet setWithCapacity: 2];
+ self.ignoreSafeAreaLandscapeAdUnitIdentifiers = [NSMutableSet setWithCapacity: 2];
+ self.adInfoDict = [NSMutableDictionary dictionary];
+ self.adInfoDictLock = [[NSObject alloc] init];
+
+ self.backgroundCallbackEventsQueue = [[NSOperationQueue alloc] init];
+ self.backgroundCallbackEventsQueue.maxConcurrentOperationCount = 1;
+
+ max_unity_dispatch_on_main_thread(^{
+ self.safeAreaBackground = [[UIView alloc] init];
+ self.safeAreaBackground.hidden = YES;
+ self.safeAreaBackground.backgroundColor = UIColor.clearColor;
+ self.safeAreaBackground.translatesAutoresizingMaskIntoConstraints = NO;
+ self.safeAreaBackground.userInteractionEnabled = NO;
+
+ UIViewController *rootViewController = [self unityViewController];
+ [rootViewController.view addSubview: self.safeAreaBackground];
+ });
+
+ // Enable orientation change listener, so that the position can be updated for vertical banners.
+ [[NSNotificationCenter defaultCenter] addObserverForName: UIDeviceOrientationDidChangeNotification
+ object: nil
+ queue: [NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification *notification) {
+
+ for ( NSString *adUnitIdentifier in self.verticalAdViewFormats )
+ {
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: self.verticalAdViewFormats[adUnitIdentifier]];
+ }
+ }];
+
+ [[NSNotificationCenter defaultCenter] addObserver: self
+ selector: @selector(applicationPaused:)
+ name: UIApplicationDidEnterBackgroundNotification
+ object: nil];
+
+ [[NSNotificationCenter defaultCenter] addObserver: self
+ selector: @selector(applicationResumed:)
+ name: UIApplicationDidBecomeActiveNotification
+ object: nil];
+
+ [[NSNotificationCenter defaultCenter] addObserverForName: UIApplicationDidBecomeActiveNotification
+ object: nil
+ queue: [NSOperationQueue mainQueue]
+ usingBlock:^(NSNotification *notification) {
+
+#if !IS_TEST_APP
+ if ( self.resumeUnityAfterApplicationBecomesActive && UnityIsPaused() )
+ {
+ UnityPause(NO);
+ }
+#endif
+
+ self.backgroundCallbackEventsQueue.suspended = NO;
+ }];
+ }
+ return self;
+}
+
++ (MAUnityAdManager *)shared
+{
+ static dispatch_once_t token;
+ static MAUnityAdManager *shared;
+ dispatch_once(&token, ^{
+ shared = [[MAUnityAdManager alloc] init];
+ });
+ return shared;
+}
+
++ (void)setUnityBackgroundCallback:(ALUnityBackgroundCallback)unityBackgroundCallback
+{
+ backgroundCallback = unityBackgroundCallback;
+}
+
+#pragma mark - Plugin Initialization
+
+- (void)initializeSdkWithConfiguration:(ALSdkInitializationConfiguration *)initConfig andCompletionHandler:(ALSdkInitializationCompletionHandler)completionHandler;
+{
+ self.sdk = [ALSdk shared];
+ [self.sdk initializeWithConfiguration: initConfig completionHandler:^(ALSdkConfiguration *configuration) {
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ // Note: internal state should be updated first
+ completionHandler( configuration );
+
+ NSString *consentFlowUserGeographyStr = @(configuration.consentFlowUserGeography).stringValue;
+ NSString *consentDialogStateStr = @(configuration.consentDialogState).stringValue;
+ NSString *appTrackingStatus = @(configuration.appTrackingTransparencyStatus).stringValue; // Deliberately name it `appTrackingStatus` to be a bit more generic (in case Android introduces a similar concept)
+ [self forwardUnityEventWithArgs: @{@"name" : @"OnSdkInitializedEvent",
+ @"consentFlowUserGeography" : consentFlowUserGeographyStr,
+ @"consentDialogState" : consentDialogStateStr,
+ @"countryCode" : configuration.countryCode,
+ @"appTrackingStatus" : appTrackingStatus,
+ @"isSuccessfullyInitialized" : @([self.sdk isInitialized]),
+ @"isTestModeEnabled" : @([configuration isTestModeEnabled])}];
+ });
+ }];
+}
+
+#pragma mark - Banners
+
+- (void)createBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier atPosition:(nullable NSString *)bannerPosition isAdaptive:(BOOL)isAdaptive
+{
+ [self createAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] atPosition: bannerPosition withOffset: CGPointZero isAdaptive: isAdaptive];
+}
+
+- (void)createBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier x:(CGFloat)xOffset y:(CGFloat)yOffset isAdaptive:(BOOL)isAdaptive
+{
+ [self createAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] atPosition: DEFAULT_AD_VIEW_POSITION withOffset: CGPointMake(xOffset, yOffset) isAdaptive: isAdaptive];
+}
+
+- (void)loadBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self loadAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)setBannerBackgroundColorForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier hexColorCode:(nullable NSString *)hexColorCode
+{
+ [self setAdViewBackgroundColorForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] hexColorCode: hexColorCode];
+}
+
+- (void)setBannerPlacement:(nullable NSString *)placement forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self setAdViewPlacement: placement forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)startBannerAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self startAdViewAutoRefreshForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)stopBannerAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self stopAdViewAutoRefreshForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)setBannerWidth:(CGFloat)width forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self setAdViewWidth: width forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)updateBannerPosition:(nullable NSString *)bannerPosition forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self updateAdViewPosition: bannerPosition withOffset: CGPointZero forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)updateBannerPosition:(CGFloat)xOffset y:(CGFloat)yOffset forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self updateAdViewPosition: DEFAULT_AD_VIEW_POSITION withOffset: CGPointMake(xOffset, yOffset) forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)setBannerExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
+{
+ [self setAdViewExtraParameterForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] key: key value: value];
+}
+
+- (void)setBannerLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value
+{
+ if ( !key )
+ {
+ [self log: @"Failed to set local extra parameter: No key specified"];
+ return;
+ }
+
+ [self setAdViewLocalExtraParameterForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier] key: key value: value];
+}
+
+- (void)setBannerCustomData:(nullable NSString *)customData forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self setAdViewCustomData: customData forAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)showBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self showAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)hideBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self hideAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (NSString *)bannerLayoutForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ return [self adViewLayoutForAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
+- (void)destroyBannerWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self destroyAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: [self adViewAdFormatForAdUnitIdentifier: adUnitIdentifier]];
+}
+
++ (CGFloat)adaptiveBannerHeightForWidth:(CGFloat)width
+{
+ return [DEVICE_SPECIFIC_ADVIEW_AD_FORMAT adaptiveSizeForWidth: width].height;
+}
+
+#pragma mark - MRECs
+
+- (void)createMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier atPosition:(nullable NSString *)mrecPosition
+{
+ [self createAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec atPosition: mrecPosition withOffset: CGPointZero isAdaptive: NO];
+}
+
+- (void)createMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier x:(CGFloat)xOffset y:(CGFloat)yOffset
+{
+ [self createAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec atPosition: DEFAULT_AD_VIEW_POSITION withOffset: CGPointMake(xOffset, yOffset) isAdaptive: NO];
+}
+
+- (void)loadMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self loadAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)setMRecPlacement:(nullable NSString *)placement forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self setAdViewPlacement: placement forAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)startMRecAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self startAdViewAutoRefreshForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)stopMRecAutoRefreshForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self stopAdViewAutoRefreshForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)updateMRecPosition:(nullable NSString *)mrecPosition forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self updateAdViewPosition: mrecPosition withOffset: CGPointZero forAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)updateMRecPosition:(CGFloat)xOffset y:(CGFloat)yOffset forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self updateAdViewPosition: DEFAULT_AD_VIEW_POSITION withOffset: CGPointMake(xOffset, yOffset) forAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)setMRecExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
+{
+ [self setAdViewExtraParameterForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec key: key value: value];
+}
+
+- (void)setMRecLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value
+{
+ if ( !key )
+ {
+ [self log: @"Failed to set local extra parameter: No key specified"];
+ return;
+ }
+
+ [self setAdViewLocalExtraParameterForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec key: key value: value];
+}
+
+- (void)setMRecCustomData:(nullable NSString *)customData forAdUnitIdentifier:(nullable NSString *)adUnitIdentifier;
+{
+ [self setAdViewCustomData: customData forAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)showMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self showAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)destroyMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self destroyAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (void)hideMRecWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ [self hideAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+- (NSString *)mrecLayoutForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ return [self adViewLayoutForAdUnitIdentifier: adUnitIdentifier adFormat: MAAdFormat.mrec];
+}
+
+#pragma mark - Interstitials
+
+- (void)loadInterstitialWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
+ [interstitial loadAd];
+}
+
+- (BOOL)isInterstitialReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
+ return [interstitial isReady];
+}
+
+- (void)showInterstitialWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData
+{
+ MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
+ [interstitial showAdForPlacement: placement customData: customData];
+}
+
+- (void)setInterstitialExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
+{
+ MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
+ [interstitial setExtraParameterForKey: key value: value];
+}
+
+- (void)setInterstitialLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value
+{
+ if ( !key )
+ {
+ [self log: @"Failed to set local extra parameter: No key specified"];
+ return;
+ }
+
+ MAInterstitialAd *interstitial = [self retrieveInterstitialForAdUnitIdentifier: adUnitIdentifier];
+ [interstitial setLocalExtraParameterForKey: key value: value];
+}
+
+#pragma mark - App Open Ads
+
+- (void)loadAppOpenAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
+ [appOpenAd loadAd];
+}
+
+- (BOOL)isAppOpenAdReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
+ return [appOpenAd isReady];
+}
+
+- (void)showAppOpenAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData
+{
+ MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
+ [appOpenAd showAdForPlacement: placement customData: customData];
+}
+
+- (void)setAppOpenAdExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
+{
+ MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
+ [appOpenAd setExtraParameterForKey: key value: value];
+}
+
+- (void)setAppOpenAdLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value
+{
+ if ( !key )
+ {
+ [self log: @"Failed to set local extra parameter: No key specified"];
+ return;
+ }
+
+ MAAppOpenAd *appOpenAd = [self retrieveAppOpenAdForAdUnitIdentifier: adUnitIdentifier];
+ [appOpenAd setLocalExtraParameterForKey: key value: value];
+}
+
+#pragma mark - Rewarded
+
+- (void)loadRewardedAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
+ [rewardedAd loadAd];
+}
+
+- (BOOL)isRewardedAdReadyWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier
+{
+ MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
+ return [rewardedAd isReady];
+}
+
+- (void)showRewardedAdWithAdUnitIdentifier:(nullable NSString *)adUnitIdentifier placement:(nullable NSString *)placement customData:(nullable NSString *)customData
+{
+ MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
+ [rewardedAd showAdForPlacement: placement customData: customData];
+}
+
+- (void)setRewardedAdExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable NSString *)value
+{
+ MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
+ [rewardedAd setExtraParameterForKey: key value: value];
+}
+
+- (void)setRewardedAdLocalExtraParameterForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier key:(nullable NSString *)key value:(nullable id)value;
+{
+ if ( !key )
+ {
+ [self log: @"Failed to set local extra parameter: No key specified"];
+ return;
+ }
+
+ MARewardedAd *rewardedAd = [self retrieveRewardedAdForAdUnitIdentifier: adUnitIdentifier];
+ [rewardedAd setLocalExtraParameterForKey: key value: value];
+}
+
+#pragma mark - Event Tracking
+
+- (void)trackEvent:(nullable NSString *)event parameters:(nullable NSString *)parameters
+{
+ NSDictionary *deserializedParameters = [MAUnityAdManager deserializeParameters: parameters];
+ [self.sdk.eventService trackEvent: event parameters: deserializedParameters];
+}
+
+#pragma mark - Ad Info
+
+- (NSDictionary *)adInfoForAd:(MAAd *)ad
+{
+ return @{@"adUnitId" : ad.adUnitIdentifier,
+ @"adFormat" : ad.format.label,
+ @"networkName" : ad.networkName,
+ @"networkPlacement" : ad.networkPlacement,
+ @"creativeId" : ad.creativeIdentifier ?: @"",
+ @"placement" : ad.placement ?: @"",
+ @"revenue" : [@(ad.revenue) stringValue],
+ @"revenuePrecision" : ad.revenuePrecision,
+ @"waterfallInfo" : [self createAdWaterfallInfo: ad.waterfall],
+ @"latencyMillis" : [self requestLatencyMillisFromRequestLatency: ad.requestLatency],
+ @"dspName" : ad.DSPName ?: @""};
+}
+
+#pragma mark - Waterfall Information
+
+- (NSDictionary *)createAdWaterfallInfo:(MAAdWaterfallInfo *)waterfallInfo
+{
+ NSMutableDictionary *waterfallInfoDict = [NSMutableDictionary dictionary];
+ if ( !waterfallInfo ) return waterfallInfoDict;
+
+ waterfallInfoDict[@"name"] = waterfallInfo.name;
+ waterfallInfoDict[@"testName"] = waterfallInfo.testName;
+
+ NSMutableArray *> *networkResponsesArray = [NSMutableArray arrayWithCapacity: waterfallInfo.networkResponses.count];
+ for ( MANetworkResponseInfo *response in waterfallInfo.networkResponses )
+ {
+ [networkResponsesArray addObject: [self createNetworkResponseInfo: response]];
+ }
+
+ waterfallInfoDict[@"networkResponses"] = networkResponsesArray;
+ waterfallInfoDict[@"latencyMillis"] = [self requestLatencyMillisFromRequestLatency: waterfallInfo.latency];
+
+ return waterfallInfoDict;
+}
+
+- (NSDictionary *)createNetworkResponseInfo:(MANetworkResponseInfo *)response
+{
+ NSMutableDictionary *networkResponseDict = [NSMutableDictionary dictionary];
+
+ networkResponseDict[@"adLoadState"] = @(response.adLoadState).stringValue;
+
+ MAMediatedNetworkInfo *mediatedNetworkInfo = response.mediatedNetwork;
+ if ( mediatedNetworkInfo )
+ {
+ NSMutableDictionary *networkInfoObject = [NSMutableDictionary dictionary];
+ networkInfoObject[@"name"] = response.mediatedNetwork.name;
+ networkInfoObject[@"adapterClassName"] = response.mediatedNetwork.adapterClassName;
+ networkInfoObject[@"adapterVersion"] = response.mediatedNetwork.adapterVersion;
+ networkInfoObject[@"sdkVersion"] = response.mediatedNetwork.sdkVersion;
+ networkInfoObject[@"initializationStatus"] = @(response.mediatedNetwork.initializationStatus);
+
+ networkResponseDict[@"mediatedNetwork"] = networkInfoObject;
+ }
+
+ networkResponseDict[@"credentials"] = response.credentials;
+ networkResponseDict[@"isBidding"] = @([response isBidding]);
+
+ MAError *error = response.error;
+ if ( error )
+ {
+ NSMutableDictionary *errorObject = [NSMutableDictionary dictionary];
+ errorObject[@"errorMessage"] = error.message;
+ errorObject[@"adLoadFailure"] = error.adLoadFailureInfo;
+ errorObject[@"errorCode"] = @(error.code).stringValue;
+ errorObject[@"latencyMillis"] = [self requestLatencyMillisFromRequestLatency: error.requestLatency];
+
+ networkResponseDict[@"error"] = errorObject;
+ }
+
+ networkResponseDict[@"latencyMillis"] = [self requestLatencyMillisFromRequestLatency: response.latency];
+
+ return networkResponseDict;
+}
+
+#pragma mark - Ad Value
+
+- (NSString *)adValueForAdUnitIdentifier:(nullable NSString *)adUnitIdentifier withKey:(nullable NSString *)key
+{
+ if ( adUnitIdentifier == nil || adUnitIdentifier.length == 0 ) return @"";
+ if ( key == nil || key.length == 0 ) return @"";
+
+ MAAd *ad = [self adWithAdUnitIdentifier: adUnitIdentifier];
+ if ( !ad ) return @"";
+
+ return [ad adValueForKey: key];
+}
+
+#pragma mark - Ad Callbacks
+
+- (void)didLoadAd:(MAAd *)ad
+{
+ NSString *name;
+ MAAdFormat *adFormat = ad.format;
+ if ( [adFormat isAdViewAd] )
+ {
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: ad.adUnitIdentifier adFormat: adFormat];
+ // An ad is now being shown, enable user interaction.
+ adView.userInteractionEnabled = YES;
+
+ if ( MAAdFormat.mrec == adFormat )
+ {
+ name = @"OnMRecAdLoadedEvent";
+ }
+ else
+ {
+ name = @"OnBannerAdLoadedEvent";
+ }
+ [self positionAdViewForAd: ad];
+
+ // Do not auto-refresh by default if the ad view is not showing yet (e.g. first load during app launch and publisher does not automatically show banner upon load success)
+ // We will resume auto-refresh in -[MAUnityAdManager showBannerWithAdUnitIdentifier:].
+ if ( adView && [adView isHidden] )
+ {
+ [adView stopAutoRefresh];
+ }
+ }
+ else if ( MAAdFormat.interstitial == adFormat )
+ {
+ name = @"OnInterstitialLoadedEvent";
+ }
+ else if ( MAAdFormat.appOpen == adFormat )
+ {
+ name = @"OnAppOpenAdLoadedEvent";
+ }
+ else if ( MAAdFormat.rewarded == adFormat )
+ {
+ name = @"OnRewardedAdLoadedEvent";
+ }
+ else
+ {
+ [self logInvalidAdFormat: adFormat];
+ return;
+ }
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ @synchronized ( self.adInfoDictLock )
+ {
+ self.adInfoDict[ad.adUnitIdentifier] = ad;
+ }
+
+ NSDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didFailToLoadAdForAdUnitIdentifier:(NSString *)adUnitIdentifier withError:(MAError *)error
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ if ( !adUnitIdentifier )
+ {
+ [self log: @"adUnitIdentifier cannot be nil from %@", [NSThread callStackSymbols]];
+ return;
+ }
+
+ NSString *name;
+ if ( self.adViews[adUnitIdentifier] )
+ {
+ MAAdFormat *adFormat = self.adViewAdFormats[adUnitIdentifier];
+ if ( MAAdFormat.mrec == adFormat )
+ {
+ name = @"OnMRecAdLoadFailedEvent";
+ }
+ else
+ {
+ name = @"OnBannerAdLoadFailedEvent";
+ }
+ }
+ else if ( self.interstitials[adUnitIdentifier] )
+ {
+ name = @"OnInterstitialLoadFailedEvent";
+ }
+ else if ( self.appOpenAds[adUnitIdentifier] )
+ {
+ name = @"OnAppOpenAdLoadFailedEvent";
+ }
+ else if ( self.rewardedAds[adUnitIdentifier] )
+ {
+ name = @"OnRewardedAdLoadFailedEvent";
+ }
+ else
+ {
+ [self log: @"invalid adUnitId from %@", [NSThread callStackSymbols]];
+ return;
+ }
+
+ @synchronized ( self.adInfoDictLock )
+ {
+ [self.adInfoDict removeObjectForKey: adUnitIdentifier];
+ }
+
+ [self forwardUnityEventWithArgs: @{@"name" : name,
+ @"adUnitId" : adUnitIdentifier,
+ @"errorCode" : [@(error.code) stringValue],
+ @"errorMessage" : error.message,
+ @"waterfallInfo" : [self createAdWaterfallInfo: error.waterfall],
+ @"adLoadFailureInfo" : error.adLoadFailureInfo ?: @"",
+ @"latencyMillis" : [self requestLatencyMillisFromRequestLatency: error.requestLatency]}];
+ });
+}
+
+- (void)didClickAd:(MAAd *)ad
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSString *name;
+ MAAdFormat *adFormat = ad.format;
+ if ( MAAdFormat.banner == adFormat || MAAdFormat.leader == adFormat )
+ {
+ name = @"OnBannerAdClickedEvent";
+ }
+ else if ( MAAdFormat.mrec == adFormat )
+ {
+ name = @"OnMRecAdClickedEvent";
+ }
+ else if ( MAAdFormat.interstitial == adFormat )
+ {
+ name = @"OnInterstitialClickedEvent";
+ }
+ else if ( MAAdFormat.appOpen == adFormat )
+ {
+ name = @"OnAppOpenAdClickedEvent";
+ }
+ else if ( MAAdFormat.rewarded == adFormat )
+ {
+ name = @"OnRewardedAdClickedEvent";
+ }
+ else
+ {
+ [self logInvalidAdFormat: adFormat];
+ return;
+ }
+
+ NSDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didDisplayAd:(MAAd *)ad
+{
+ // BMLs do not support [DISPLAY] events in Unity
+ MAAdFormat *adFormat = ad.format;
+ if ( ![adFormat isFullscreenAd] ) return;
+
+#if !IS_TEST_APP
+ // UnityPause needs to be called on the main thread.
+ UnityPause(YES);
+#endif
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSString *name;
+ if ( MAAdFormat.interstitial == adFormat )
+ {
+ name = @"OnInterstitialDisplayedEvent";
+ }
+ else if ( MAAdFormat.appOpen == adFormat )
+ {
+ name = @"OnAppOpenAdDisplayedEvent";
+ }
+ else // rewarded
+ {
+ name = @"OnRewardedAdDisplayedEvent";
+ }
+
+ NSDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didFailToDisplayAd:(MAAd *)ad withError:(MAError *)error
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ // BMLs do not support [DISPLAY] events in Unity
+ MAAdFormat *adFormat = ad.format;
+ if ( ![adFormat isFullscreenAd] ) return;
+
+ NSString *name;
+ if ( MAAdFormat.interstitial == adFormat )
+ {
+ name = @"OnInterstitialAdFailedToDisplayEvent";
+ }
+ else if ( MAAdFormat.appOpen == adFormat )
+ {
+ name = @"OnAppOpenAdFailedToDisplayEvent";
+ }
+ else // rewarded
+ {
+ name = @"OnRewardedAdFailedToDisplayEvent";
+ }
+
+ NSMutableDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ args[@"errorCode"] = [@(error.code) stringValue];
+ args[@"errorMessage"] = error.message;
+ args[@"mediatedNetworkErrorCode"] = [@(error.mediatedNetworkErrorCode) stringValue];
+ args[@"mediatedNetworkErrorMessage"] = error.mediatedNetworkErrorMessage;
+ args[@"waterfallInfo"] = [self createAdWaterfallInfo: error.waterfall];
+ args[@"latencyMillis"] = [self requestLatencyMillisFromRequestLatency: error.requestLatency];
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didHideAd:(MAAd *)ad
+{
+ // BMLs do not support [HIDDEN] events in Unity
+ MAAdFormat *adFormat = ad.format;
+ if ( ![adFormat isFullscreenAd] ) return;
+
+#if !IS_TEST_APP
+ extern bool _didResignActive;
+ if ( _didResignActive )
+ {
+ // If the application is not active, we should wait until application becomes active to resume unity.
+ self.resumeUnityAfterApplicationBecomesActive = YES;
+ }
+ else
+ {
+ // UnityPause needs to be called on the main thread.
+ UnityPause(NO);
+ }
+#endif
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSString *name;
+ if ( MAAdFormat.interstitial == adFormat )
+ {
+ name = @"OnInterstitialHiddenEvent";
+ }
+ else if ( MAAdFormat.appOpen == adFormat )
+ {
+ name = @"OnAppOpenAdHiddenEvent";
+ }
+ else // rewarded
+ {
+ name = @"OnRewardedAdHiddenEvent";
+ }
+
+ NSDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didExpandAd:(MAAd *)ad
+{
+ MAAdFormat *adFormat = ad.format;
+ if ( ![adFormat isAdViewAd] )
+ {
+ [self logInvalidAdFormat: adFormat];
+ return;
+ }
+
+#if !IS_TEST_APP
+ // UnityPause needs to be called on the main thread.
+ UnityPause(YES);
+#endif
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSString *name;
+ if ( MAAdFormat.mrec == adFormat )
+ {
+ name = @"OnMRecAdExpandedEvent";
+ }
+ else
+ {
+ name = @"OnBannerAdExpandedEvent";
+ }
+
+ NSDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didCollapseAd:(MAAd *)ad
+{
+ MAAdFormat *adFormat = ad.format;
+ if ( ![adFormat isAdViewAd] )
+ {
+ [self logInvalidAdFormat: adFormat];
+ return;
+ }
+
+#if !IS_TEST_APP
+ extern bool _didResignActive;
+ if ( _didResignActive )
+ {
+ // If the application is not active, we should wait until application becomes active to resume unity.
+ self.resumeUnityAfterApplicationBecomesActive = YES;
+ }
+ else
+ {
+ // UnityPause needs to be called on the main thread.
+ UnityPause(NO);
+ }
+#endif
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSString *name;
+ if ( MAAdFormat.mrec == adFormat )
+ {
+ name = @"OnMRecAdCollapsedEvent";
+ }
+ else
+ {
+ name = @"OnBannerAdCollapsedEvent";
+ }
+
+ NSDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didRewardUserForAd:(MAAd *)ad withReward:(MAReward *)reward
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ MAAdFormat *adFormat = ad.format;
+ if ( adFormat != MAAdFormat.rewarded )
+ {
+ [self logInvalidAdFormat: adFormat];
+ return;
+ }
+
+ NSString *rewardLabel = reward ? reward.label : @"";
+ NSInteger rewardAmountInt = reward ? reward.amount : 0;
+ NSString *rewardAmount = [@(rewardAmountInt) stringValue];
+ NSString *name = @"OnRewardedAdReceivedRewardEvent";
+
+ NSMutableDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ args[@"rewardLabel"] = rewardLabel;
+ args[@"rewardAmount"] = rewardAmount;
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didPayRevenueForAd:(MAAd *)ad
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSString *name;
+ MAAdFormat *adFormat = ad.format;
+ if ( MAAdFormat.banner == adFormat || MAAdFormat.leader == adFormat )
+ {
+ name = @"OnBannerAdRevenuePaidEvent";
+ }
+ else if ( MAAdFormat.mrec == adFormat )
+ {
+ name = @"OnMRecAdRevenuePaidEvent";
+ }
+ else if ( MAAdFormat.interstitial == adFormat )
+ {
+ name = @"OnInterstitialAdRevenuePaidEvent";
+ }
+ else if ( MAAdFormat.appOpen == adFormat )
+ {
+ name = @"OnAppOpenAdRevenuePaidEvent";
+ }
+ else if ( MAAdFormat.rewarded == adFormat )
+ {
+ name = @"OnRewardedAdRevenuePaidEvent";
+ }
+ else
+ {
+ [self logInvalidAdFormat: adFormat];
+ return;
+ }
+
+ NSMutableDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ args[@"keepInBackground"] = @([adFormat isFullscreenAd]);
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didReloadExpiredAd:(MAAd *)expiredAd withNewAd:(MAAd *)newAd;
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSString *name;
+ MAAdFormat *adFormat = newAd.format;
+ if ( MAAdFormat.interstitial == adFormat )
+ {
+ name = @"OnExpiredInterstitialAdReloadedEvent";
+ }
+ else if ( MAAdFormat.appOpen == adFormat )
+ {
+ name = @"OnExpiredAppOpenAdReloadedEvent";
+ }
+ else if ( MAAdFormat.rewarded == adFormat )
+ {
+ name = @"OnExpiredRewardedAdReloadedEvent ";
+ }
+ else
+ {
+ [self logInvalidAdFormat: adFormat];
+ return;
+ }
+
+ @synchronized ( self.adInfoDictLock )
+ {
+ self.adInfoDict[newAd.adUnitIdentifier] = newAd;
+ }
+
+ NSMutableDictionary *args = [NSMutableDictionary dictionary];
+ args[@"expiredAdInfo"] = [self adInfoForAd: expiredAd];
+ args[@"newAdInfo"] = [self adInfoForAd: newAd];
+ args[@"name"] = name;
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (void)didGenerateCreativeIdentifier:(NSString *)creativeIdentifier forAd:(MAAd *)ad
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+
+ NSString *name;
+ MAAdFormat *adFormat = ad.format;
+ if ( MAAdFormat.banner == adFormat || MAAdFormat.leader == adFormat )
+ {
+ name = @"OnBannerAdReviewCreativeIdGeneratedEvent";
+ }
+ else if ( MAAdFormat.mrec == adFormat )
+ {
+ name = @"OnMRecAdReviewCreativeIdGeneratedEvent";
+ }
+ else if ( MAAdFormat.interstitial == adFormat )
+ {
+ name = @"OnInterstitialAdReviewCreativeIdGeneratedEvent";
+ }
+ else if ( MAAdFormat.rewarded == adFormat )
+ {
+ name = @"OnRewardedAdReviewCreativeIdGeneratedEvent";
+ }
+ else
+ {
+ [self logInvalidAdFormat: adFormat];
+ return;
+ }
+
+ NSMutableDictionary *args = [self defaultAdEventParametersForName: name withAd: ad];
+ args[@"adReviewCreativeId"] = creativeIdentifier;
+ args[@"keepInBackground"] = @([adFormat isFullscreenAd]);
+
+ // Forward the event in background for fullscreen ads so that the user gets the callback even while the ad is playing.
+ [self forwardUnityEventWithArgs: args];
+ });
+}
+
+- (NSMutableDictionary *)defaultAdEventParametersForName:(NSString *)name withAd:(MAAd *)ad
+{
+ NSMutableDictionary *args = [[self adInfoForAd: ad] mutableCopy];
+ args[@"name"] = name;
+
+ return args;
+}
+
+#pragma mark - Internal Methods
+
+- (void)createAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat atPosition:(NSString *)adViewPosition withOffset:(CGPoint)offset isAdaptive:(BOOL)isAdaptive
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Creating %@ with ad unit identifier \"%@\" and position: \"%@\"", adFormat, adUnitIdentifier, adViewPosition];
+
+ if ( self.adViews[adUnitIdentifier] )
+ {
+ [self log: @"Trying to create a %@ that was already created. This will cause the current ad to be hidden.", adFormat.label];
+ }
+
+ // Retrieve ad view from the map
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat atPosition: adViewPosition withOffset: offset isAdaptive: isAdaptive];
+ adView.hidden = YES;
+ self.safeAreaBackground.hidden = YES;
+
+ // Position ad view immediately so if publisher sets color before ad loads, it will not be the size of the screen
+ self.adViewAdFormats[adUnitIdentifier] = adFormat;
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+
+ NSDictionary *extraParameters = self.adViewExtraParametersToSetAfterCreate[adUnitIdentifier];
+
+ // Handle initial extra parameters if publisher sets it before creating ad view
+ if ( extraParameters )
+ {
+ for ( NSString *key in extraParameters )
+ {
+ [adView setExtraParameterForKey: key value: extraParameters[key]];
+
+ [self handleExtraParameterChangesIfNeededForAdUnitIdentifier: adUnitIdentifier
+ adFormat: adFormat
+ key: key
+ value: extraParameters[key]];
+ }
+
+ [self.adViewExtraParametersToSetAfterCreate removeObjectForKey: adUnitIdentifier];
+ }
+
+ // Handle initial local extra parameters if publisher sets it before creating ad view
+ if ( self.adViewLocalExtraParametersToSetAfterCreate[adUnitIdentifier] )
+ {
+ NSDictionary *localExtraParameters = self.adViewLocalExtraParametersToSetAfterCreate[adUnitIdentifier];
+ for ( NSString *key in localExtraParameters )
+ {
+ [adView setLocalExtraParameterForKey: key value: localExtraParameters[key]];
+ }
+
+ [self.adViewLocalExtraParametersToSetAfterCreate removeObjectForKey: adUnitIdentifier];
+ }
+
+ // Handle initial custom data if publisher sets it before creating ad view
+ if ( self.adViewCustomDataToSetAfterCreate[adUnitIdentifier] )
+ {
+ NSString *customData = self.adViewCustomDataToSetAfterCreate[adUnitIdentifier];
+ adView.customData = customData;
+
+ [self.adViewCustomDataToSetAfterCreate removeObjectForKey: adUnitIdentifier];
+ }
+
+ [adView loadAd];
+
+ // Disable auto-refresh if publisher sets it before creating the ad view.
+ if ( [self.disabledAutoRefreshAdViewAdUnitIdentifiers containsObject: adUnitIdentifier] )
+ {
+ [adView stopAutoRefresh];
+ }
+
+ // The publisher may have requested to show the banner before it was created. Now that the banner is created, show it.
+ if ( [self.adUnitIdentifiersToShowAfterCreate containsObject: adUnitIdentifier] )
+ {
+ [self showAdViewWithAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ [self.adUnitIdentifiersToShowAfterCreate removeObject: adUnitIdentifier];
+ }
+ });
+}
+
+- (void)loadAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ if ( !adView )
+ {
+ [self log: @"%@ does not exist for ad unit identifier \"%@\".", adFormat.label, adUnitIdentifier];
+ return;
+ }
+
+ if ( ![self.disabledAutoRefreshAdViewAdUnitIdentifiers containsObject: adUnitIdentifier] )
+ {
+ if ( [adView isHidden] )
+ {
+ [self log: @"Auto-refresh will resume when the %@ ad is shown. You should only call LoadBanner() or LoadMRec() if you explicitly pause auto-refresh and want to manually load an ad.", adFormat.label];
+ return;
+ }
+
+ [self log: @"You must stop auto-refresh if you want to manually load %@ ads.", adFormat.label];
+ return;
+ }
+
+ [adView loadAd];
+ });
+}
+
+- (void)setAdViewBackgroundColorForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat hexColorCode:(NSString *)hexColorCode
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Setting %@ with ad unit identifier \"%@\" to color: \"%@\"", adFormat, adUnitIdentifier, hexColorCode];
+
+ // In some cases, black color may get redrawn on each frame update, resulting in an undesired flicker
+ NSString *hexColorCodeToUse = [hexColorCode containsString: @"FF000000"] ? @"FF000001" : hexColorCode;
+ UIColor *convertedColor = [UIColor al_colorWithHexString: hexColorCodeToUse];
+
+ MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ self.publisherBannerBackgroundColor = convertedColor;
+ self.safeAreaBackground.backgroundColor = view.backgroundColor = convertedColor;
+
+ // Position adView to ensure logic that depends on background color is properly run
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ });
+}
+
+- (void)setAdViewPlacement:(nullable NSString *)placement forAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Setting placement \"%@\" for \"%@\" with ad unit identifier \"%@\"", placement, adFormat, adUnitIdentifier];
+
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ adView.placement = placement;
+ });
+}
+
+- (void)startAdViewAutoRefreshForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Starting %@ auto refresh for ad unit identifier \"%@\"", adFormat.label, adUnitIdentifier];
+
+ [self.disabledAutoRefreshAdViewAdUnitIdentifiers removeObject: adUnitIdentifier];
+
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ if ( !adView )
+ {
+ [self log: @"%@ does not exist for ad unit identifier %@.", adFormat.label, adUnitIdentifier];
+ return;
+ }
+
+ [adView startAutoRefresh];
+ });
+}
+
+- (void)stopAdViewAutoRefreshForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Stopping %@ auto refresh for ad unit identifier \"%@\"", adFormat.label, adUnitIdentifier];
+
+ [self.disabledAutoRefreshAdViewAdUnitIdentifiers addObject: adUnitIdentifier];
+
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ if ( !adView )
+ {
+ [self log: @"%@ does not exist for ad unit identifier %@.", adFormat.label, adUnitIdentifier];
+ return;
+ }
+
+ [adView stopAutoRefresh];
+ });
+}
+
+- (void)setAdViewWidth:(CGFloat)width forAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Setting width %f for \"%@\" with ad unit identifier \"%@\"", width, adFormat, adUnitIdentifier];
+
+ BOOL isBannerOrLeader = adFormat.isBannerOrLeaderAd;
+
+ // Banners and leaders need to be at least 320pts wide.
+ CGFloat minWidth = isBannerOrLeader ? MAAdFormat.banner.size.width : adFormat.size.width;
+ if ( width < minWidth )
+ {
+ [self log: @"The provided width: %f is smaller than the minimum required width: %f for ad format: %@. Automatically setting width to %f.", width, minWidth, adFormat, minWidth];
+ }
+
+ CGFloat widthToSet = MAX( minWidth, width );
+ self.adViewWidths[adUnitIdentifier] = @(widthToSet);
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ });
+}
+
+- (void)updateAdViewPosition:(NSString *)adViewPosition withOffset:(CGPoint)offset forAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ self.adViewPositions[adUnitIdentifier] = adViewPosition;
+ self.adViewOffsets[adUnitIdentifier] = [NSValue valueWithCGPoint: offset];
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ });
+}
+
+- (void)setAdViewExtraParameterForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat key:(NSString *)key value:(nullable NSString *)value
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Setting %@ extra with key: \"%@\" value: \"%@\"", adFormat, key, value];
+
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ if ( adView )
+ {
+ [adView setExtraParameterForKey: key value: value];
+ }
+ else
+ {
+ [self log: @"%@ does not exist for ad unit identifier \"%@\". Saving extra parameter to be set when it is created.", adFormat, adUnitIdentifier];
+
+ // The adView has not yet been created. Store the extra parameters, so that they can be added once the banner has been created.
+ NSMutableDictionary *extraParameters = self.adViewExtraParametersToSetAfterCreate[adUnitIdentifier];
+ if ( !extraParameters )
+ {
+ extraParameters = [NSMutableDictionary dictionaryWithCapacity: 1];
+ self.adViewExtraParametersToSetAfterCreate[adUnitIdentifier] = extraParameters;
+ }
+
+ extraParameters[key] = value;
+ }
+
+ // Certain extra parameters need to be handled immediately
+ [self handleExtraParameterChangesIfNeededForAdUnitIdentifier: adUnitIdentifier
+ adFormat: adFormat
+ key: key
+ value: value];
+ });
+}
+
+- (void)setAdViewLocalExtraParameterForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat key:(NSString *)key value:(nullable id)value
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Setting %@ local extra with key: \"%@\" value: \"%@\"", adFormat, key, value];
+
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ if ( adView )
+ {
+ [adView setLocalExtraParameterForKey: key value: value];
+ }
+ else
+ {
+ [self log: @"%@ does not exist for ad unit identifier \"%@\". Saving local extra parameter to be set when it is created.", adFormat, adUnitIdentifier];
+
+ // The adView has not yet been created. Store the loca extra parameters, so that they can be added once the adview has been created.
+ NSMutableDictionary *localExtraParameters = self.adViewLocalExtraParametersToSetAfterCreate[adUnitIdentifier];
+ if ( !localExtraParameters )
+ {
+ localExtraParameters = [NSMutableDictionary dictionaryWithCapacity: 1];
+ self.adViewLocalExtraParametersToSetAfterCreate[adUnitIdentifier] = localExtraParameters;
+ }
+
+ localExtraParameters[key] = value;
+ }
+ });
+}
+
+- (void)setAdViewCustomData:(nullable NSString *)customData forAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ if ( adView )
+ {
+ adView.customData = customData;
+ }
+ else
+ {
+ [self log: @"%@ does not exist for ad unit identifier %@. Saving custom data to be set when it is created.", adFormat, adUnitIdentifier];
+
+ // The adView has not yet been created. Store the custom data, so that they can be added once the AdView has been created.
+ self.adViewCustomDataToSetAfterCreate[adUnitIdentifier] = customData;
+ }
+ });
+}
+
+- (void)handleExtraParameterChangesIfNeededForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat key:(NSString *)key value:(nullable NSString *)value
+{
+ if ( MAAdFormat.mrec != adFormat )
+ {
+ if ( [@"force_banner" isEqualToString: key] )
+ {
+ BOOL shouldForceBanner = [NSNumber al_numberWithString: value].boolValue;
+ MAAdFormat *forcedAdFormat = shouldForceBanner ? MAAdFormat.banner : DEVICE_SPECIFIC_ADVIEW_AD_FORMAT;
+
+ self.adViewAdFormats[adUnitIdentifier] = forcedAdFormat;
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: forcedAdFormat];
+ }
+ else if ( [@"adaptive_banner" isEqualToString: key] )
+ {
+ [self log: @"Setting adaptive banners via extra parameters is deprecated and will be removed in a future plugin version. Use the CreateBanner(adUnitIdentifier, AdViewConfiguration) API to properly configure adaptive banners."];
+
+ BOOL shouldUseAdaptiveBanner = [NSNumber al_numberWithString: value].boolValue;
+ if ( shouldUseAdaptiveBanner )
+ {
+ [self.disabledAdaptiveBannerAdUnitIdentifiers removeObject: adUnitIdentifier];
+ }
+ else
+ {
+ [self.disabledAdaptiveBannerAdUnitIdentifiers addObject: adUnitIdentifier];
+ }
+
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ }
+ else if ( [@"ignore_safe_area_landscape" isEqualToString: key] && [NSNumber al_numberWithString: value].boolValue )
+ {
+ [self.ignoreSafeAreaLandscapeAdUnitIdentifiers addObject: adUnitIdentifier];
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ }
+ }
+
+ if ( [adFormat isAdViewAd] && [@"clips_to_bounds" isEqualToString: key] )
+ {
+ BOOL clipsToBounds = [NSNumber al_numberWithString: value].boolValue;
+ MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ view.clipsToBounds = clipsToBounds;
+ }
+}
+
+- (void)showAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Showing %@ with ad unit identifier \"%@\"", adFormat, adUnitIdentifier];
+
+ MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ if ( !view )
+ {
+ [self log: @"%@ does not exist for ad unit identifier %@.", adFormat, adUnitIdentifier];
+
+ // The adView has not yet been created. Store the ad unit ID, so that it can be displayed once the banner has been created.
+ [self.adUnitIdentifiersToShowAfterCreate addObject: adUnitIdentifier];
+ }
+ else
+ {
+ // Check edge case where ad may be detatched from view controller
+ if ( !view.window.rootViewController )
+ {
+ [self log: @"%@ missing view controller - re-attaching to %@...", adFormat, [self unityViewController]];
+
+ UIViewController *rootViewController = [self unityViewController];
+ [rootViewController.view addSubview: view];
+
+ [self positionAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ }
+ }
+
+ self.safeAreaBackground.hidden = NO;
+ view.hidden = NO;
+
+ if ( ![self.disabledAutoRefreshAdViewAdUnitIdentifiers containsObject: adUnitIdentifier] )
+ {
+ [view startAutoRefresh];
+ }
+ });
+}
+
+- (void)hideAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Hiding %@ with ad unit identifier \"%@\"", adFormat, adUnitIdentifier];
+ [self.adUnitIdentifiersToShowAfterCreate removeObject: adUnitIdentifier];
+
+ MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ view.hidden = YES;
+ self.safeAreaBackground.hidden = YES;
+
+ [view stopAutoRefresh];
+ });
+}
+
+- (NSString *)adViewLayoutForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ [self log: @"Getting %@ position with ad unit identifier \"%@\"", adFormat, adUnitIdentifier];
+
+ MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ if ( !view )
+ {
+ [self log: @"%@ does not exist for ad unit identifier %@", adFormat, adUnitIdentifier];
+
+ return @"";
+ }
+
+ CGRect adViewFrame = view.frame;
+ return [MAUnityAdManager serializeParameters: @{@"origin_x" : [NSString stringWithFormat: @"%f", adViewFrame.origin.x],
+ @"origin_y" : [NSString stringWithFormat: @"%f", adViewFrame.origin.y],
+ @"width" : [NSString stringWithFormat: @"%f", CGRectGetWidth(adViewFrame)],
+ @"height" : [NSString stringWithFormat: @"%f", CGRectGetHeight(adViewFrame)]}];
+}
+
+- (void)destroyAdViewWithAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ [self log: @"Destroying %@ with ad unit identifier \"%@\"", adFormat, adUnitIdentifier];
+
+ MAAdView *view = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ view.delegate = nil;
+ view.revenueDelegate = nil;
+ view.adReviewDelegate = nil;
+
+ [view removeFromSuperview];
+
+ [self.adViews removeObjectForKey: adUnitIdentifier];
+ [self.adViewAdFormats removeObjectForKey: adUnitIdentifier];
+ [self.adViewPositions removeObjectForKey: adUnitIdentifier];
+ [self.adViewOffsets removeObjectForKey: adUnitIdentifier];
+ [self.adViewWidths removeObjectForKey: adUnitIdentifier];
+ [self.verticalAdViewFormats removeObjectForKey: adUnitIdentifier];
+ [self.disabledAdaptiveBannerAdUnitIdentifiers removeObject: adUnitIdentifier];
+ });
+}
+
+- (void)logInvalidAdFormat:(MAAdFormat *)adFormat
+{
+ [self log: @"invalid ad format: %@, from %@", adFormat, [NSThread callStackSymbols]];
+}
+
+- (void)log:(NSString *)format, ...
+{
+ if (max_unity_should_disable_all_logs()) return;
+
+ va_list valist;
+ va_start(valist, format);
+ NSString *message = [[NSString alloc] initWithFormat: format arguments: valist];
+ va_end(valist);
+
+ NSLog(@"[%@] [%@] %@", SDK_TAG, TAG, message);
+}
+
++ (void)log:(NSString *)format, ...
+{
+ if (max_unity_should_disable_all_logs()) return;
+
+ va_list valist;
+ va_start(valist, format);
+ NSString *message = [[NSString alloc] initWithFormat: format arguments: valist];
+ va_end(valist);
+
+ NSLog(@"[%@] [%@] %@", SDK_TAG, TAG, message);
+}
+
+- (MAInterstitialAd *)retrieveInterstitialForAdUnitIdentifier:(NSString *)adUnitIdentifier
+{
+ MAInterstitialAd *result = self.interstitials[adUnitIdentifier];
+ if ( !result )
+ {
+ result = [[MAInterstitialAd alloc] initWithAdUnitIdentifier: adUnitIdentifier];
+ result.delegate = self;
+ result.revenueDelegate = self;
+ result.adReviewDelegate = self;
+ result.expirationDelegate = self;
+
+ self.interstitials[adUnitIdentifier] = result;
+ }
+
+ return result;
+}
+
+- (MAAppOpenAd *)retrieveAppOpenAdForAdUnitIdentifier:(NSString *)adUnitIdentifier
+{
+ MAAppOpenAd *result = self.appOpenAds[adUnitIdentifier];
+ if ( !result )
+ {
+ result = [[MAAppOpenAd alloc] initWithAdUnitIdentifier: adUnitIdentifier];
+ result.delegate = self;
+ result.revenueDelegate = self;
+ result.expirationDelegate = self;
+
+ self.appOpenAds[adUnitIdentifier] = result;
+ }
+
+ return result;
+}
+
+- (MARewardedAd *)retrieveRewardedAdForAdUnitIdentifier:(NSString *)adUnitIdentifier
+{
+ MARewardedAd *result = self.rewardedAds[adUnitIdentifier];
+ if ( !result )
+ {
+ result = [MARewardedAd sharedWithAdUnitIdentifier: adUnitIdentifier];
+ result.delegate = self;
+ result.revenueDelegate = self;
+ result.adReviewDelegate = self;
+ result.expirationDelegate = self;
+
+ self.rewardedAds[adUnitIdentifier] = result;
+ }
+
+ return result;
+}
+
+- (MAAdView *)retrieveAdViewForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ return [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat atPosition: nil withOffset: CGPointZero isAdaptive: YES];
+}
+
+- (MAAdView *)retrieveAdViewForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat atPosition:(NSString *)adViewPosition withOffset:(CGPoint)offset isAdaptive:(BOOL)isAdaptive
+{
+ MAAdView *result = self.adViews[adUnitIdentifier];
+ if ( !result && adViewPosition )
+ {
+ MAAdViewConfiguration *config = [MAAdViewConfiguration configurationWithBuilderBlock:^(MAAdViewConfigurationBuilder *builder) {
+
+ // Set adaptive type only for banner ads. If adaptive is enabled, use ANCHORED; otherwise, fall back to NONE.
+ if ( [adFormat isBannerOrLeaderAd] )
+ {
+ if ( isAdaptive )
+ {
+ builder.adaptiveType = MAAdViewAdaptiveTypeAnchored;
+ }
+ else
+ {
+ builder.adaptiveType = MAAdViewAdaptiveTypeNone;
+ [self.disabledAdaptiveBannerAdUnitIdentifiers addObject:adUnitIdentifier];
+ }
+ }
+ }];
+
+ // There is a Unity bug where if an empty UIView is on screen with user interaction enabled, and a user interacts with it, it just passes the events to random parts of the screen.
+ result = [[MAAdView alloc] initWithAdUnitIdentifier:adUnitIdentifier adFormat:adFormat configuration:config];
+ result.userInteractionEnabled = NO;
+ result.translatesAutoresizingMaskIntoConstraints = NO;
+ result.delegate = self;
+ result.revenueDelegate = self;
+ result.adReviewDelegate = self;
+
+ self.adViews[adUnitIdentifier] = result;
+ self.adViewPositions[adUnitIdentifier] = adViewPosition;
+ self.adViewOffsets[adUnitIdentifier] = [NSValue valueWithCGPoint: offset];
+
+ UIViewController *rootViewController = [self unityViewController];
+ [rootViewController.view addSubview: result];
+
+ // Allow pubs to pause auto-refresh immediately, by default.
+ [result setExtraParameterForKey: @"allow_pause_auto_refresh_immediately" value: @"true"];
+ }
+
+ return result;
+}
+
+- (void)positionAdViewForAd:(MAAd *)ad
+{
+ [self positionAdViewForAdUnitIdentifier: ad.adUnitIdentifier adFormat: ad.format];
+}
+
+- (void)positionAdViewForAdUnitIdentifier:(NSString *)adUnitIdentifier adFormat:(MAAdFormat *)adFormat
+{
+ max_unity_dispatch_on_main_thread(^{
+ MAAdView *adView = [self retrieveAdViewForAdUnitIdentifier: adUnitIdentifier adFormat: adFormat];
+ NSString *adViewPosition = self.adViewPositions[adUnitIdentifier];
+ NSValue *adViewPositionValue = self.adViewOffsets[adUnitIdentifier];
+ CGPoint adViewOffset = [adViewPositionValue CGPointValue];
+ BOOL isAdaptiveBannerDisabled = [self.disabledAdaptiveBannerAdUnitIdentifiers containsObject: adUnitIdentifier];
+ BOOL isWidthPtsOverridden = self.adViewWidths[adUnitIdentifier] != nil;
+
+ UIView *superview = adView.superview;
+ if ( !superview ) return;
+
+ // Deactivate any previous constraints and reset rotation so that the banner can be positioned again.
+ NSArray *activeConstraints = self.adViewConstraints[adUnitIdentifier];
+ [NSLayoutConstraint deactivateConstraints: activeConstraints];
+ adView.transform = CGAffineTransformIdentity;
+ [self.verticalAdViewFormats removeObjectForKey: adUnitIdentifier];
+
+ // Ensure superview contains the safe area background.
+ if ( ![superview.subviews containsObject: self.safeAreaBackground] )
+ {
+ [self.safeAreaBackground removeFromSuperview];
+ [superview insertSubview: self.safeAreaBackground belowSubview: adView];
+ }
+
+ // Deactivate any previous constraints and reset visibility state so that the safe area background can be positioned again.
+ [NSLayoutConstraint deactivateConstraints: self.safeAreaBackground.constraints];
+ self.safeAreaBackground.hidden = adView.hidden;
+
+ //
+ // Determine ad width
+ //
+ CGFloat adViewWidth;
+
+ // Check if publisher has overridden width as points
+ if ( isWidthPtsOverridden )
+ {
+ adViewWidth = self.adViewWidths[adUnitIdentifier].floatValue;
+ }
+ // Top center / bottom center stretches full screen
+ else if ( [adViewPosition isEqual: @"top_center"] || [adViewPosition isEqual: @"bottom_center"] )
+ {
+ adViewWidth = CGRectGetWidth(KEY_WINDOW.bounds);
+ }
+ // Else use standard widths of 320, 728, or 300
+ else
+ {
+ adViewWidth = adFormat.size.width;
+ }
+
+ //
+ // Determine ad height
+ //
+ CGFloat adViewHeight;
+
+ if ( (adFormat == MAAdFormat.banner || adFormat == MAAdFormat.leader) && !isAdaptiveBannerDisabled )
+ {
+ adViewHeight = [adFormat adaptiveSizeForWidth: adViewWidth].height;
+ }
+ else
+ {
+ adViewHeight = adFormat.size.height;
+ }
+
+ CGSize adViewSize = CGSizeMake(adViewWidth, adViewHeight);
+
+ // All positions have constant height
+ NSMutableArray *constraints = [NSMutableArray arrayWithObject: [adView.heightAnchor constraintEqualToConstant: adViewSize.height]];
+
+ UILayoutGuide *layoutGuide = superview.safeAreaLayoutGuide;
+ BOOL shouldIgnoreSafeArea = [self shouldIgnoreSafeAreaForAdUnitIdentifier: adUnitIdentifier];
+ NSLayoutAnchor *topAnchor = shouldIgnoreSafeArea ? superview.topAnchor : layoutGuide.topAnchor;
+
+ if ( [adViewPosition isEqual: @"top_center"] || [adViewPosition isEqual: @"bottom_center"] )
+ {
+ // Non AdMob banners will still be of 50/90 points tall. Set the auto sizing mask such that the inner ad view is pinned to the bottom or top according to the ad view position.
+ if ( !isAdaptiveBannerDisabled )
+ {
+ adView.autoresizingMask = UIViewAutoresizingFlexibleWidth;
+
+ if ( [@"top_center" isEqual: adViewPosition] )
+ {
+ adView.autoresizingMask |= UIViewAutoresizingFlexibleBottomMargin;
+ }
+ else // bottom_center
+ {
+ adView.autoresizingMask |= UIViewAutoresizingFlexibleTopMargin;
+ }
+ }
+
+ // If publisher actually provided a banner background color
+ if ( self.publisherBannerBackgroundColor && adFormat != MAAdFormat.mrec )
+ {
+ if ( isWidthPtsOverridden )
+ {
+ [constraints addObjectsFromArray: @[[adView.widthAnchor constraintEqualToConstant: adViewWidth],
+ [adView.centerXAnchor constraintEqualToAnchor: layoutGuide.centerXAnchor],
+ [self.safeAreaBackground.widthAnchor constraintEqualToConstant: adViewWidth],
+ [self.safeAreaBackground.centerXAnchor constraintEqualToAnchor: layoutGuide.centerXAnchor]]];
+
+ if ( [adViewPosition isEqual: @"top_center"] )
+ {
+ [constraints addObjectsFromArray: @[[adView.topAnchor constraintEqualToAnchor: topAnchor],
+ [self.safeAreaBackground.topAnchor constraintEqualToAnchor: superview.topAnchor],
+ [self.safeAreaBackground.bottomAnchor constraintEqualToAnchor: adView.topAnchor]]];
+ }
+ else // bottom_center
+ {
+ [constraints addObjectsFromArray: @[[adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor],
+ [self.safeAreaBackground.topAnchor constraintEqualToAnchor: adView.bottomAnchor],
+ [self.safeAreaBackground.bottomAnchor constraintEqualToAnchor: superview.bottomAnchor]]];
+ }
+ }
+ else
+ {
+ [constraints addObjectsFromArray: @[[adView.leftAnchor constraintEqualToAnchor: superview.leftAnchor],
+ [adView.rightAnchor constraintEqualToAnchor: superview.rightAnchor],
+ [self.safeAreaBackground.leftAnchor constraintEqualToAnchor: superview.leftAnchor],
+ [self.safeAreaBackground.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
+
+ if ( [adViewPosition isEqual: @"top_center"] )
+ {
+ [constraints addObjectsFromArray: @[[adView.topAnchor constraintEqualToAnchor: topAnchor],
+ [self.safeAreaBackground.topAnchor constraintEqualToAnchor: superview.topAnchor],
+ [self.safeAreaBackground.bottomAnchor constraintEqualToAnchor: adView.topAnchor]]];
+ }
+ else // bottom_center
+ {
+ [constraints addObjectsFromArray: @[[adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor],
+ [self.safeAreaBackground.topAnchor constraintEqualToAnchor: adView.bottomAnchor],
+ [self.safeAreaBackground.bottomAnchor constraintEqualToAnchor: superview.bottomAnchor]]];
+ }
+ }
+ }
+ // If pub does not have a background color set or this is not a banner
+ else
+ {
+ self.safeAreaBackground.hidden = YES;
+
+ [constraints addObjectsFromArray: @[[adView.widthAnchor constraintEqualToConstant: adViewWidth],
+ [adView.centerXAnchor constraintEqualToAnchor: layoutGuide.centerXAnchor]]];
+
+ if ( [adViewPosition isEqual: @"top_center"] )
+ {
+ [constraints addObject: [adView.topAnchor constraintEqualToAnchor: topAnchor]];
+ }
+ else // BottomCenter
+ {
+ [constraints addObject: [adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor]];
+ }
+ }
+ }
+ // Check if the publisher wants vertical banners.
+ else if ( [adViewPosition isEqual: @"center_left"] || [adViewPosition isEqual: @"center_right"] )
+ {
+ if ( MAAdFormat.mrec == adFormat )
+ {
+ [constraints addObject: [adView.widthAnchor constraintEqualToConstant: adViewSize.width]];
+
+ if ( [adViewPosition isEqual: @"center_left"] )
+ {
+ [constraints addObjectsFromArray: @[[adView.centerYAnchor constraintEqualToAnchor: layoutGuide.centerYAnchor],
+ [adView.leftAnchor constraintEqualToAnchor: superview.leftAnchor]]];
+
+ [constraints addObjectsFromArray: @[[self.safeAreaBackground.rightAnchor constraintEqualToAnchor: layoutGuide.leftAnchor],
+ [self.safeAreaBackground.leftAnchor constraintEqualToAnchor: superview.leftAnchor]]];
+ }
+ else // center_right
+ {
+ [constraints addObjectsFromArray: @[[adView.centerYAnchor constraintEqualToAnchor: layoutGuide.centerYAnchor],
+ [adView.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
+
+ [constraints addObjectsFromArray: @[[self.safeAreaBackground.leftAnchor constraintEqualToAnchor: layoutGuide.rightAnchor],
+ [self.safeAreaBackground.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
+ }
+ }
+ else
+ {
+ /* Align the center of the view such that when rotated it snaps into place.
+ *
+ * +---+---+-------+
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * | | |
+ * +-------------+---+-----------+--+
+ * | | + | + | |
+ * +-------------+---+-----------+--+
+ * <+> | |
+ * |+ | |
+ * || | |
+ * || | |
+ * || | |
+ * || | |
+ * +|--+-----------+
+ * v
+ * Banner Half Height
+ */
+ self.safeAreaBackground.hidden = YES;
+
+ adView.transform = CGAffineTransformRotate(CGAffineTransformIdentity, M_PI_2);
+
+ CGFloat width;
+ // If the publiser has a background color set - set the width to the height of the screen, to span the ad across the screen after it is rotated.
+ if ( self.publisherBannerBackgroundColor )
+ {
+ if ( isWidthPtsOverridden )
+ {
+ width = adViewWidth;
+ }
+ else
+ {
+ width = CGRectGetHeight(KEY_WINDOW.bounds);
+ }
+ }
+ // Otherwise - we shouldn't span the banner the width of the realm (there might be user-interactable UI on the sides)
+ else
+ {
+ width = adViewWidth;
+ }
+ [constraints addObject: [adView.widthAnchor constraintEqualToConstant: width]];
+
+ // Set constraints such that the center of the banner aligns with the center left or right as needed. That way, once rotated, the banner snaps into place.
+ [constraints addObject: [adView.centerYAnchor constraintEqualToAnchor: superview.centerYAnchor]];
+
+ // Place the center of the banner half the height of the banner away from the side. If we align the center exactly with the left/right anchor, only half the banner will be visible.
+ CGFloat bannerHalfHeight = adViewSize.height / 2.0;
+ UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
+ if ( [adViewPosition isEqual: @"center_left"] )
+ {
+ NSLayoutAnchor *anchor = ( orientation == UIInterfaceOrientationLandscapeRight ) ? layoutGuide.leftAnchor : superview.leftAnchor;
+ [constraints addObject: [adView.centerXAnchor constraintEqualToAnchor: anchor constant: bannerHalfHeight]];
+ }
+ else // CenterRight
+ {
+ NSLayoutAnchor *anchor = ( orientation == UIInterfaceOrientationLandscapeLeft ) ? layoutGuide.rightAnchor : superview.rightAnchor;
+ [constraints addObject: [adView.centerXAnchor constraintEqualToAnchor: anchor constant: -bannerHalfHeight]];
+ }
+
+ // Store the ad view with format, so that it can be updated when the orientation changes.
+ self.verticalAdViewFormats[adUnitIdentifier] = adFormat;
+
+ // If adaptive - make top flexible since we anchor with the bottom of the banner at the edge of the screen
+ if ( !isAdaptiveBannerDisabled )
+ {
+ adView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleTopMargin;
+ }
+ }
+ }
+ // Otherwise, publisher will likely construct his own views around the adview
+ else
+ {
+ self.safeAreaBackground.hidden = YES;
+
+ [constraints addObject: [adView.widthAnchor constraintEqualToConstant: adViewWidth]];
+
+ if ( [adViewPosition isEqual: @"top_left"] )
+ {
+ [constraints addObjectsFromArray: @[[adView.leftAnchor constraintEqualToAnchor: superview.leftAnchor constant: adViewOffset.x],
+ [adView.topAnchor constraintEqualToAnchor: topAnchor constant: adViewOffset.y]]];
+ }
+ else if ( [adViewPosition isEqual: @"top_right"] )
+ {
+ [constraints addObjectsFromArray: @[[adView.topAnchor constraintEqualToAnchor: topAnchor],
+ [adView.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
+ }
+ else if ( [adViewPosition isEqual: @"centered"] )
+ {
+ [constraints addObjectsFromArray: @[[adView.centerXAnchor constraintEqualToAnchor: layoutGuide.centerXAnchor],
+ [adView.centerYAnchor constraintEqualToAnchor: layoutGuide.centerYAnchor]]];
+ }
+ else if ( [adViewPosition isEqual: @"bottom_left"] )
+ {
+ [constraints addObjectsFromArray: @[[adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor],
+ [adView.leftAnchor constraintEqualToAnchor: superview.leftAnchor]]];
+ }
+ else if ( [adViewPosition isEqual: @"bottom_right"] )
+ {
+ [constraints addObjectsFromArray: @[[adView.bottomAnchor constraintEqualToAnchor: layoutGuide.bottomAnchor],
+ [adView.rightAnchor constraintEqualToAnchor: superview.rightAnchor]]];
+ }
+ }
+
+ self.adViewConstraints[adUnitIdentifier] = constraints;
+
+ [NSLayoutConstraint activateConstraints: constraints];
+ });
+}
+
+- (UIViewController *)unityViewController
+{
+ // Handle edge case where `UnityGetGLViewController()` returns nil
+ return UnityGetGLViewController() ?: UnityGetMainWindow().rootViewController ?: [KEY_WINDOW rootViewController];
+}
+
+- (void)forwardUnityEventWithArgs:(NSDictionary *)args
+{
+#if !IS_TEST_APP
+ extern bool _didResignActive;
+ // We should not call any script callbacks when application is not active. Suspend the callback queue if resign is active.
+ // We'll resume the queue once the application becomes active again.
+ self.backgroundCallbackEventsQueue.suspended = _didResignActive;
+#endif
+
+ [self.backgroundCallbackEventsQueue addOperationWithBlock:^{
+ NSString *serializedParameters = [MAUnityAdManager serializeParameters: args];
+ backgroundCallback(serializedParameters.UTF8String);
+ }];
+}
+
++ (NSString *)serializeParameters:(NSDictionary *)dict
+{
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject: dict options: 0 error: nil];
+ return [[NSString alloc] initWithData: jsonData encoding: NSUTF8StringEncoding];
+}
+
++ (NSDictionary *)deserializeParameters:(nullable NSString *)serialized
+{
+ if ( serialized.length > 0 )
+ {
+ NSError *error;
+ NSDictionary *deserialized = [NSJSONSerialization JSONObjectWithData: [serialized dataUsingEncoding:NSUTF8StringEncoding]
+ options: 0
+ error: &error];
+ if ( error )
+ {
+ [self log: @"Failed to deserialize (%@) with error %@", serialized, error];
+ return @{};
+ }
+
+ return deserialized;
+ }
+ else
+ {
+ return @{};
+ }
+}
+
+- (MAAdFormat *)adViewAdFormatForAdUnitIdentifier:(NSString *)adUnitIdentifier
+{
+ if ( self.adViewAdFormats[adUnitIdentifier] )
+ {
+ return self.adViewAdFormats[adUnitIdentifier];
+ }
+ else
+ {
+ return DEVICE_SPECIFIC_ADVIEW_AD_FORMAT;
+ }
+}
+
+- (NSString *)requestLatencyMillisFromRequestLatency:(NSTimeInterval)requestLatency
+{
+ // Convert latency from seconds to milliseconds to match Android.
+ long requestLatencyMillis = requestLatency * 1000;
+ return @(requestLatencyMillis).stringValue;
+}
+
+#pragma mark - User Service
+
+- (void)didDismissUserConsentDialog
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [self forwardUnityEventWithArgs: @{@"name" : @"OnSdkConsentDialogDismissedEvent"}];
+ });
+}
+
+#pragma mark - CMP Service
+
+- (void)showCMPForExistingUser
+{
+ [self.sdk.cmpService showCMPForExistingUserWithCompletion:^(ALCMPError * _Nullable error) {
+
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ NSMutableDictionary *args = [NSMutableDictionary dictionaryWithCapacity: 2];
+ args[@"name"] = @"OnCmpCompletedEvent";
+
+ if ( error )
+ {
+ args[@"error"] = @{@"code": @(error.code),
+ @"message": error.message,
+ @"cmpCode": @(error.cmpCode),
+ @"cmpMessage": error.cmpMessage,
+ @"keepInBackground": @(YES)};
+ }
+
+ [self forwardUnityEventWithArgs: args];
+ });
+ }];
+}
+
+#pragma mark - Application
+
+- (void)applicationPaused:(NSNotification *)notification
+{
+ [self notifyApplicationStateChangedEventForPauseState: YES];
+}
+
+- (void)applicationResumed:(NSNotification *)notification
+{
+ [self notifyApplicationStateChangedEventForPauseState: NO];
+}
+
+- (void)notifyApplicationStateChangedEventForPauseState:(BOOL)isPaused
+{
+ dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
+ [self forwardUnityEventWithArgs: @{@"name": @"OnApplicationStateChanged",
+ @"isPaused": @(isPaused)}];
+ });
+}
+
+- (MAAd *)adWithAdUnitIdentifier:(NSString *)adUnitIdentifier
+{
+ @synchronized ( self.adInfoDictLock )
+ {
+ return self.adInfoDict[adUnitIdentifier];
+ }
+}
+
+#pragma mark - Helper
+
+- (BOOL)shouldIgnoreSafeAreaForAdUnitIdentifier:(NSString *)adUnitIdentifier
+{
+ if ( ![self.ignoreSafeAreaLandscapeAdUnitIdentifiers containsObject: adUnitIdentifier] ) return NO;
+
+ // We should use the superview instead of layout guide if the application's orientation is landscape and the extra parameter is set
+ UIInterfaceOrientation orientation = [UIApplication sharedApplication].statusBarOrientation;
+ return orientation == UIInterfaceOrientationLandscapeRight || orientation == UIInterfaceOrientationLandscapeLeft;
+}
+
+@end
diff --git a/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.m.meta b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.m.meta
new file mode 100644
index 0000000..8c3581e
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.m.meta
@@ -0,0 +1,109 @@
+fileFormatVersion: 2
+guid: 2973e70bd2fa74984b35aea07ae9db52
+labels:
+- al_max
+- al_max_export_path-MaxSdk/AppLovin/Plugins/iOS/MAUnityAdManager.m
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ '': Any
+ second:
+ enabled: 0
+ settings:
+ Exclude Android: 1
+ Exclude Editor: 1
+ Exclude Linux: 1
+ Exclude Linux64: 1
+ Exclude LinuxUniversal: 1
+ Exclude OSXUniversal: 1
+ Exclude Win: 1
+ Exclude Win64: 1
+ Exclude iOS: 0
+ Exclude tvOS: 1
+ - first:
+ Android: Android
+ second:
+ enabled: 0
+ settings:
+ CPU: ARMv7
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ DefaultValueInitialized: true
+ OS: AnyOS
+ - first:
+ Facebook: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Facebook: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Linux
+ second:
+ enabled: 0
+ settings:
+ CPU: x86
+ - first:
+ Standalone: Linux64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: OSXUniversal
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ iPhone: iOS
+ second:
+ enabled: 1
+ settings:
+ CompileFlags:
+ FrameworkDependencies:
+ - first:
+ tvOS: tvOS
+ second:
+ enabled: 0
+ settings:
+ CompileFlags:
+ FrameworkDependencies:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityPlugin.mm b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityPlugin.mm
new file mode 100644
index 0000000..f31184a
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityPlugin.mm
@@ -0,0 +1,1081 @@
+//
+// MAUnityPlugin.mm
+// AppLovin MAX Unity Plugin
+//
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wdeprecated-declarations"
+
+#import "MAUnityAdManager.h"
+
+#define VERSION @"8.3.1"
+#define NSSTRING(_X) ( (_X != NULL) ? [NSString stringWithCString: _X encoding: NSStringEncodingConversionAllowLossy].al_stringByTrimmingWhitespace : nil)
+
+@interface NSString (ALUtils)
+@property (nonatomic, copy, readonly) NSString *al_stringByTrimmingWhitespace;
+@property (assign, readonly, getter=al_isValidString) BOOL al_validString;
+- (BOOL)al_isEqualToStringIgnoringCase:(nullable NSString *)otherString;
+@end
+
+@interface ALSdkInitializationConfigurationBuilder (ALUtils)
+- (void)setSdkKey:(NSString *)sdkKey;
+@end
+
+// When native code plugin is implemented in .mm / .cpp file, then functions
+// should be surrounded with extern "C" block to conform C function naming rules
+extern "C"
+{
+ static NSString *const TAG = @"MAUnityPlugin";
+ static NSString *const KeySdkKey = @"SdkKey";
+
+ UIView* UnityGetGLView();
+
+ static ALSdkInitializationConfigurationBuilder *_initConfigurationBuilder;
+ static ALSdk *_sdk;
+ static MAUnityAdManager *_adManager;
+
+ static bool _isSdkInitialized = false;
+ static bool _initializeSdkCalled = false;
+ static bool _disableAllLogs = false;
+
+ // Helper method to create C string copy
+ static const char * cStringCopy(NSString *string);
+ // Helper method to log errors
+ void max_unity_log_uninitialized_access_error(const char *callingMethod);
+ void max_unity_log_error(NSString *message);
+
+ ALSdk *getSdk()
+ {
+ if ( !_sdk )
+ {
+ _sdk = [ALSdk shared];
+ }
+
+ return _sdk;
+ }
+
+ MAUnityAdManager *getAdManager()
+ {
+ if ( !_adManager )
+ {
+ _adManager = [MAUnityAdManager shared];
+ }
+
+ return _adManager;
+ }
+
+ ALSdkInitializationConfigurationBuilder *getInitConfigurationBuilder()
+ {
+ if ( !_initConfigurationBuilder )
+ {
+ NSString *sdkKey = [getSdk().settings.extraParameters al_stringForKey: KeySdkKey];
+ _initConfigurationBuilder = [ALSdkInitializationConfiguration builderWithSdkKey: sdkKey];
+ }
+
+ return _initConfigurationBuilder;
+ }
+
+ bool max_unity_should_disable_all_logs()
+ {
+ return _disableAllLogs;
+ }
+
+ int getConsentStatusValue(NSNumber *consentStatus)
+ {
+ if ( consentStatus )
+ {
+ return consentStatus.intValue;
+ }
+ else
+ {
+ return -1;
+ }
+ }
+
+ id getLocalExtraParameterValue(const char *json)
+ {
+ NSData *jsonData = [NSSTRING(json) dataUsingEncoding: NSUTF8StringEncoding];
+ NSError *error;
+ NSDictionary *jsonDict = [NSJSONSerialization JSONObjectWithData: jsonData
+ options: 0
+ error: &error];
+
+ if ( error )
+ {
+ return nil;
+ }
+ else
+ {
+ return jsonDict[@"value"];
+ }
+ }
+
+ NSArray * toStringArray(char **arrayPointer, int size)
+ {
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity: size];
+ for ( int i = 0; i < size; i++ )
+ {
+ NSString *element = NSSTRING(arrayPointer[i]);
+ if ( element )
+ {
+ [array addObject: element];
+ }
+ }
+
+ return array;
+ }
+
+ MASegmentCollection *getSegmentCollection(const char *collectionJson)
+ {
+ MASegmentCollectionBuilder *segmentCollectionBuilder = [MASegmentCollection builder];
+
+ NSDictionary *jsonDict = [MAUnityAdManager deserializeParameters: [NSString stringWithUTF8String: collectionJson]];
+
+ NSArray *segmentsArray = jsonDict[@"segments"];
+ for (NSDictionary *segmentDict in segmentsArray)
+ {
+ NSNumber *key = segmentDict[@"key"];
+ NSArray *valuesArray = segmentDict[@"values"];
+ NSMutableArray *values = [NSMutableArray array];
+ for (NSNumber *value in valuesArray)
+ {
+ [values addObject:value];
+ }
+
+ MASegment *segment = [[MASegment alloc] initWithKey:key values:values];
+ [segmentCollectionBuilder addSegment:segment];
+ }
+
+ return [segmentCollectionBuilder build];
+ }
+
+ void _MaxSetBackgroundCallback(ALUnityBackgroundCallback backgroundCallback)
+ {
+ [MAUnityAdManager setUnityBackgroundCallback: backgroundCallback];
+ }
+
+ void _MaxSetSdkKey(const char *sdkKey)
+ {
+ if (!sdkKey) return;
+
+ NSString *sdkKeyStr = [NSString stringWithUTF8String: sdkKey];
+ [getInitConfigurationBuilder() setSdkKey: sdkKeyStr];
+ }
+
+ void _MaxInitializeSdk(const char *serializedAdUnitIdentifiers, const char *serializedMetaData)
+ {
+ ALSdkInitializationConfigurationBuilder *initConfigurationBuilder = getInitConfigurationBuilder();
+ initConfigurationBuilder.mediationProvider = @"max";
+ initConfigurationBuilder.pluginVersion = [@"Max-Unity-" stringByAppendingString: VERSION];
+ initConfigurationBuilder.adUnitIdentifiers = [[NSString stringWithUTF8String: serializedAdUnitIdentifiers] componentsSeparatedByString: @","];
+
+ [getSdk().settings setExtraParameterForKey: @"applovin_unity_metadata" value: NSSTRING(serializedMetaData)];
+
+ ALSdkInitializationConfiguration *initConfig = [initConfigurationBuilder build];
+
+ [getAdManager() initializeSdkWithConfiguration: initConfig andCompletionHandler:^(ALSdkConfiguration *configuration) {
+ _isSdkInitialized = true;
+ }];
+
+ _initializeSdkCalled = true;
+ }
+
+ bool _MaxIsInitialized()
+ {
+ return _isSdkInitialized;
+ }
+
+ const char * _MaxGetAvailableMediatedNetworks()
+ {
+ NSArray *availableMediatedNetworks = [getSdk() availableMediatedNetworks];
+
+ // Create array of serialized network strings
+ NSMutableArray *> *serializedNetworks = [NSMutableArray arrayWithCapacity: availableMediatedNetworks.count];
+ for ( MAMediatedNetworkInfo *mediatedNetwork in availableMediatedNetworks )
+ {
+ NSDictionary *mediatedNetworkDictionary = @{@"name" : mediatedNetwork.name,
+ @"adapterClassName" : mediatedNetwork.adapterClassName,
+ @"adapterVersion" : mediatedNetwork.adapterVersion,
+ @"sdkVersion" : mediatedNetwork.sdkVersion,
+ @"initializationStatus" : @(mediatedNetwork.initializationStatus)};
+ [serializedNetworks addObject: mediatedNetworkDictionary];
+ }
+
+ NSData *jsonData = [NSJSONSerialization dataWithJSONObject: serializedNetworks options: 0 error: nil];
+ return cStringCopy([[NSString alloc] initWithData: jsonData encoding: NSUTF8StringEncoding]);
+ }
+
+ void _MaxShowMediationDebugger()
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_error(@"Failed to show mediation debugger - please ensure the AppLovin MAX Unity Plugin has been initialized by calling 'MaxSdk.InitializeSdk();'!");
+ return;
+ }
+
+ [getSdk() showMediationDebugger];
+ }
+
+ void _MaxShowCreativeDebugger()
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_error(@"Failed to show creative debugger - please ensure the AppLovin MAX Unity Plugin has been initialized by calling 'MaxSdk.InitializeSdk();'!");
+ return;
+ }
+
+ [getSdk() showCreativeDebugger];
+ }
+
+ void _MaxSetUserId(const char *userId)
+ {
+ getSdk().settings.userIdentifier = NSSTRING(userId);
+ }
+
+ void _MaxSetSegmentCollection(const char *collectionJson)
+ {
+ if ( _initializeSdkCalled )
+ {
+ max_unity_log_error(@"Segment collection must be set before MAX SDK is initialized");
+ return;
+ }
+
+ getInitConfigurationBuilder().segmentCollection = getSegmentCollection(collectionJson);
+ }
+
+ const char * _MaxGetSdkConfiguration()
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxGetSdkConfiguration");
+ return cStringCopy(@"");
+ }
+
+ NSString *consentFlowUserGeographyStr = @(getSdk().configuration.consentFlowUserGeography).stringValue;
+ NSString *consentDialogStateStr = @(getSdk().configuration.consentDialogState).stringValue;
+ NSString *appTrackingStatus = @(getSdk().configuration.appTrackingTransparencyStatus).stringValue; // Deliberately name it `appTrackingStatus` to be a bit more generic (in case Android introduces a similar concept)
+
+ return cStringCopy([MAUnityAdManager serializeParameters: @{@"consentFlowUserGeography" : consentFlowUserGeographyStr,
+ @"consentDialogState" : consentDialogStateStr,
+ @"countryCode" : getSdk().configuration.countryCode,
+ @"appTrackingStatus" : appTrackingStatus,
+ @"isSuccessfullyInitialized" : @([getSdk() isInitialized]),
+ @"isTestModeEnabled" : @([getSdk().configuration isTestModeEnabled])}]);
+ }
+
+ void _MaxSetHasUserConsent(bool hasUserConsent)
+ {
+ [ALPrivacySettings setHasUserConsent: hasUserConsent];
+ }
+
+ bool _MaxHasUserConsent()
+ {
+ return [ALPrivacySettings hasUserConsent];
+ }
+
+ bool _MaxIsUserConsentSet()
+ {
+ return [ALPrivacySettings isUserConsentSet];
+ }
+
+ void _MaxSetDoNotSell(bool doNotSell)
+ {
+ [ALPrivacySettings setDoNotSell: doNotSell];
+ }
+
+ bool _MaxIsDoNotSell()
+ {
+ return [ALPrivacySettings isDoNotSell];
+ }
+
+ bool _MaxIsDoNotSellSet()
+ {
+ return [ALPrivacySettings isDoNotSellSet];
+ }
+
+ void _MaxCreateBanner(const char *adUnitIdentifier, const char *bannerPosition, bool isAdaptive)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxCreateBanner");
+ return;
+ }
+
+ [getAdManager() createBannerWithAdUnitIdentifier: NSSTRING(adUnitIdentifier) atPosition: NSSTRING(bannerPosition) isAdaptive: isAdaptive];
+ }
+
+ void _MaxCreateBannerXY(const char *adUnitIdentifier, const float x, const float y, bool isAdaptive)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxCreateBannerXY");
+ return;
+ }
+
+ [getAdManager() createBannerWithAdUnitIdentifier: NSSTRING(adUnitIdentifier) x: x y: y isAdaptive: isAdaptive];
+ }
+
+ void _MaxLoadBanner(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxLoadBanner");
+ return;
+ }
+
+ [getAdManager() loadBannerWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxSetBannerBackgroundColor(const char *adUnitIdentifier, const char *hexColorCode)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetBannerBackgroundColor");
+ return;
+ }
+
+ [getAdManager() setBannerBackgroundColorForAdUnitIdentifier: NSSTRING(adUnitIdentifier) hexColorCode: NSSTRING(hexColorCode)];
+ }
+
+ void _MaxSetBannerPlacement(const char *adUnitIdentifier, const char *placement)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetBannerPlacement");
+ return;
+ }
+
+ [getAdManager() setBannerPlacement: NSSTRING(placement) forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxStartBannerAutoRefresh(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxStartBannerAutoRefresh");
+ return;
+ }
+
+ [getAdManager() startBannerAutoRefreshForAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxStopBannerAutoRefresh(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxStopBannerAutoRefresh");
+ return;
+ }
+
+ [getAdManager() stopBannerAutoRefreshForAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxSetBannerExtraParameter(const char *adUnitIdentifier, const char *key, const char *value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetBannerExtraParameter");
+ return;
+ }
+
+ [getAdManager() setBannerExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: NSSTRING(value)];
+ }
+
+ void _MaxSetBannerLocalExtraParameter(const char *adUnitIdentifier, const char *key, MAUnityRef value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetBannerLocalExtraParameter");
+ return;
+ }
+
+ [getAdManager() setBannerLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: (__bridge id) value];
+ }
+
+ void _MaxSetBannerLocalExtraParameterJSON(const char *adUnitIdentifier, const char *key, const char *json)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetBannerLocalExtraParameter");
+ return;
+ }
+
+ id value = getLocalExtraParameterValue(json);
+ [getAdManager() setBannerLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: value];
+ }
+
+ void _MaxSetBannerCustomData(const char *adUnitIdentifier, const char *customData)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetBannerCustomData");
+ return;
+ }
+
+ [getAdManager() setBannerCustomData: NSSTRING(customData) forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxSetBannerWidth(const char *adUnitIdentifier, const float width)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetBannerWidth");
+ return;
+ }
+
+ [getAdManager() setBannerWidth: width forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxUpdateBannerPosition(const char *adUnitIdentifier, const char *bannerPosition)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxUpdateBannerPosition");
+ return;
+ }
+
+ [getAdManager() updateBannerPosition: NSSTRING(bannerPosition) forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxUpdateBannerPositionXY(const char *adUnitIdentifier, const float x, const float y)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxUpdateBannerPositionXY");
+ return;
+ }
+
+ [getAdManager() updateBannerPosition: x y: y forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxShowBanner(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxShowBanner");
+ return;
+ }
+
+ [getAdManager() showBannerWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxDestroyBanner(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxDestroyBanner");
+ return;
+ }
+
+ [getAdManager() destroyBannerWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxHideBanner(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxHideBanner");
+ return;
+ }
+
+ [getAdManager() hideBannerWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ const char * _MaxGetBannerLayout(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxGetBannerLayout");
+ return cStringCopy(@"");
+ }
+
+ return cStringCopy([getAdManager() bannerLayoutForAdUnitIdentifier: NSSTRING(adUnitIdentifier)]);
+ }
+
+ void _MaxCreateMRec(const char *adUnitIdentifier, const char *mrecPosition)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxCreateMRec");
+ return;
+ }
+
+ [getAdManager() createMRecWithAdUnitIdentifier: NSSTRING(adUnitIdentifier) atPosition: NSSTRING(mrecPosition)];
+ }
+
+ void _MaxCreateMRecXY(const char *adUnitIdentifier, const float x, const float y)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxCreateMRecXY");
+ return;
+ }
+
+ [getAdManager() createMRecWithAdUnitIdentifier: NSSTRING(adUnitIdentifier) x: x y: y];
+ }
+
+ void _MaxLoadMRec(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxLoadMRec");
+ return;
+ }
+
+ [getAdManager() loadMRecWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxSetMRecPlacement(const char *adUnitIdentifier, const char *placement)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetMRecPlacement");
+ return;
+ }
+
+ [getAdManager() setMRecPlacement: NSSTRING(placement) forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxStartMRecAutoRefresh(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxStartMRecAutoRefresh");
+ return;
+ }
+
+ [getAdManager() startMRecAutoRefreshForAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxStopMRecAutoRefresh(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxStopMRecAutoRefresh");
+ return;
+ }
+
+ [getAdManager() stopMRecAutoRefreshForAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxUpdateMRecPosition(const char *adUnitIdentifier, const char *mrecPosition)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxUpdateMRecPosition");
+ return;
+ }
+
+ [getAdManager() updateMRecPosition: NSSTRING(mrecPosition) forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxUpdateMRecPositionXY(const char *adUnitIdentifier, const float x, const float y)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxUpdateMRecPositionXY");
+ return;
+ }
+
+ [getAdManager() updateMRecPosition: x y: y forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxShowMRec(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxShowMRec");
+ return;
+ }
+
+ [getAdManager() showMRecWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxDestroyMRec(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxDestroyMRec");
+ return;
+ }
+
+ [getAdManager() destroyMRecWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxHideMRec(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxHideMRec");
+ return;
+ }
+
+ [getAdManager() hideMRecWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxSetMRecExtraParameter(const char *adUnitIdentifier, const char *key, const char *value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetMRecExtraParameter");
+ return;
+ }
+
+ [getAdManager() setMRecExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: NSSTRING(value)];
+ }
+
+ void _MaxSetMRecLocalExtraParameter(const char *adUnitIdentifier, const char *key, MAUnityRef value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetMRecLocalExtraParameter");
+ return;
+ }
+
+ [getAdManager() setMRecLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: (__bridge id)value];
+ }
+
+ void _MaxSetMRecLocalExtraParameterJSON(const char *adUnitIdentifier, const char *key, const char *json)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetMRecLocalExtraParameter");
+ return;
+ }
+
+ id value = getLocalExtraParameterValue(json);
+ [getAdManager() setMRecLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: value];
+ }
+
+ void _MaxSetMRecCustomData(const char *adUnitIdentifier, const char *customData)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetMRecCustomData");
+ return;
+ }
+
+ [getAdManager() setMRecCustomData: NSSTRING(customData) forAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ const char * _MaxGetMRecLayout(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxGetMRecLayout");
+ return cStringCopy(@"");
+ }
+
+ return cStringCopy([getAdManager() mrecLayoutForAdUnitIdentifier: NSSTRING(adUnitIdentifier)]);
+ }
+
+ void _MaxLoadInterstitial(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxLoadInterstitial");
+ return;
+ }
+
+ [getAdManager() loadInterstitialWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxSetInterstitialExtraParameter(const char *adUnitIdentifier, const char *key, const char *value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetInterstitialExtraParameter");
+ return;
+ }
+
+ [getAdManager() setInterstitialExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: NSSTRING(value)];
+ }
+
+ void _MaxSetInterstitialLocalExtraParameter(const char *adUnitIdentifier, const char *key, MAUnityRef value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetInterstitialLocalExtraParameter");
+ return;
+ }
+
+ [getAdManager() setInterstitialLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: (__bridge id)value];
+ }
+
+ void _MaxSetInterstitialLocalExtraParameterJSON(const char *adUnitIdentifier, const char *key, const char *json)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetInterstitialLocalExtraParameter");
+ return;
+ }
+
+ id value = getLocalExtraParameterValue(json);
+ [getAdManager() setInterstitialLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: value];
+ }
+
+ bool _MaxIsInterstitialReady(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxIsInterstitialReady");
+ return false;
+ }
+
+ return [getAdManager() isInterstitialReadyWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxShowInterstitial(const char *adUnitIdentifier, const char *placement, const char *customData)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxShowInterstitial");
+ return;
+ }
+
+ [getAdManager() showInterstitialWithAdUnitIdentifier: NSSTRING(adUnitIdentifier) placement: NSSTRING(placement) customData: NSSTRING(customData)];
+ }
+
+ void _MaxLoadAppOpenAd(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxLoadAppOpenAd");
+ return;
+ }
+
+ [getAdManager() loadAppOpenAdWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxSetAppOpenAdExtraParameter(const char *adUnitIdentifier, const char *key, const char *value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetAppOpenAdExtraParameter");
+ return;
+ }
+
+ [getAdManager() setAppOpenAdExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: NSSTRING(value)];
+ }
+
+ void _MaxSetAppOpenAdLocalExtraParameter(const char *adUnitIdentifier, const char *key, MAUnityRef value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetAppOpenAdLocalExtraParameter");
+ return;
+ }
+
+ [getAdManager() setAppOpenAdLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: (__bridge id)value];
+ }
+
+ void _MaxSetAppOpenAdLocalExtraParameterJSON(const char *adUnitIdentifier, const char *key, const char *json)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetAppOpenAdLocalExtraParameter");
+ return;
+ }
+
+ id value = getLocalExtraParameterValue(json);
+ [getAdManager() setAppOpenAdLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: value];
+ }
+
+ bool _MaxIsAppOpenAdReady(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxIsAppOpenAdReady");
+ return false;
+ }
+
+ return [getAdManager() isAppOpenAdReadyWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxShowAppOpenAd(const char *adUnitIdentifier, const char *placement, const char *customData)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxShowAppOpenAd");
+ return;
+ }
+
+ [getAdManager() showAppOpenAdWithAdUnitIdentifier: NSSTRING(adUnitIdentifier) placement: NSSTRING(placement) customData: NSSTRING(customData)];
+ }
+
+ void _MaxLoadRewardedAd(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxLoadRewardedAd");
+ return;
+ }
+
+ [getAdManager() loadRewardedAdWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxSetRewardedAdExtraParameter(const char *adUnitIdentifier, const char *key, const char *value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetRewardedAdExtraParameter");
+ return;
+ }
+
+ [getAdManager() setRewardedAdExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: NSSTRING(value)];
+ }
+
+ void _MaxSetRewardedAdLocalExtraParameter(const char *adUnitIdentifier, const char *key, MAUnityRef value)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetRewardedAdLocalExtraParameter");
+ return;
+ }
+
+ [getAdManager() setRewardedAdLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: (__bridge id)value];
+ }
+
+ void _MaxSetRewardedAdLocalExtraParameterJSON(const char *adUnitIdentifier, const char *key, const char *json)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxSetRewardedAdLocalExtraParameter");
+ return;
+ }
+
+ id value = getLocalExtraParameterValue(json);
+ [getAdManager() setRewardedAdLocalExtraParameterForAdUnitIdentifier: NSSTRING(adUnitIdentifier)
+ key: NSSTRING(key)
+ value: value];
+ }
+
+ bool _MaxIsRewardedAdReady(const char *adUnitIdentifier)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxIsRewardedAdReady");
+ return false;
+ }
+
+ return [getAdManager() isRewardedAdReadyWithAdUnitIdentifier: NSSTRING(adUnitIdentifier)];
+ }
+
+ void _MaxShowRewardedAd(const char *adUnitIdentifier, const char *placement, const char *customData)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxShowRewardedAd");
+ return;
+ }
+
+ [getAdManager() showRewardedAdWithAdUnitIdentifier: NSSTRING(adUnitIdentifier) placement: NSSTRING(placement) customData: NSSTRING(customData)];
+ }
+
+ void _MaxTrackEvent(const char *event, const char *parameters)
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxTrackEvent");
+ return;
+ }
+
+ [getAdManager() trackEvent: NSSTRING(event) parameters: NSSTRING(parameters)];
+ }
+
+ bool _MaxIsTablet()
+ {
+ return [UIDevice currentDevice].userInterfaceIdiom == UIUserInterfaceIdiomPad;
+ }
+
+ bool _MaxIsPhysicalDevice()
+ {
+ return !ALUtils.simulator;
+ }
+
+ int _MaxGetTcfVendorConsentStatus(int vendorIdentifier)
+ {
+ NSNumber *consentStatus = [ALPrivacySettings tcfVendorConsentStatusForIdentifier: vendorIdentifier];
+ return getConsentStatusValue(consentStatus);
+ }
+
+ int _MaxGetAdditionalConsentStatus(int atpIdentifier)
+ {
+ NSNumber *consentStatus = [ALPrivacySettings additionalConsentStatusForIdentifier: atpIdentifier];
+ return getConsentStatusValue(consentStatus);
+ }
+
+ int _MaxGetPurposeConsentStatus(int purposeIdentifier)
+ {
+ NSNumber *consentStatus = [ALPrivacySettings purposeConsentStatusForIdentifier: purposeIdentifier];
+ return getConsentStatusValue(consentStatus);
+ }
+
+ int _MaxGetSpecialFeatureOptInStatus(int specialFeatureIdentifier)
+ {
+ NSNumber *consentStatus = [ALPrivacySettings specialFeatureOptInStatusForIdentifier: specialFeatureIdentifier];
+ return getConsentStatusValue(consentStatus);
+ }
+
+ static const char * cStringCopy(NSString *string)
+ {
+ const char *value = string.UTF8String;
+ return value ? strdup(value) : NULL;
+ }
+
+ void _MaxSetMuted(bool muted)
+ {
+ getSdk().settings.muted = muted;
+ }
+
+ bool _MaxIsMuted()
+ {
+ return getSdk().settings.muted;
+ }
+
+ float _MaxScreenDensity()
+ {
+ return [UIScreen.mainScreen nativeScale];
+ }
+
+ const char * _MaxGetAdValue(const char *adUnitIdentifier, const char *key)
+ {
+ return cStringCopy([getAdManager() adValueForAdUnitIdentifier: NSSTRING(adUnitIdentifier) withKey: NSSTRING(key)]);
+ }
+
+ void _MaxSetVerboseLogging(bool enabled)
+ {
+ getSdk().settings.verboseLoggingEnabled = enabled;
+ }
+
+ bool _MaxIsVerboseLoggingEnabled()
+ {
+ return [getSdk().settings isVerboseLoggingEnabled];
+ }
+
+ void _MaxSetTestDeviceAdvertisingIdentifiers(char **advertisingIdentifiers, int size)
+ {
+ if ( _initializeSdkCalled )
+ {
+ max_unity_log_error(@"Test device advertising IDs must be set before MAX SDK is initialized");
+ return;
+ }
+
+ NSArray *advertisingIdentifiersArray = toStringArray(advertisingIdentifiers, size);
+ getInitConfigurationBuilder().testDeviceAdvertisingIdentifiers = advertisingIdentifiersArray;
+ }
+
+ void _MaxSetCreativeDebuggerEnabled(bool enabled)
+ {
+ getSdk().settings.creativeDebuggerEnabled = enabled;
+ }
+
+ void _MaxSetExceptionHandlerEnabled(bool enabled)
+ {
+ if ( _initializeSdkCalled )
+ {
+ max_unity_log_error(@"Exception handler must be enabled/disabled before MAX SDK is initialized");
+ return;
+ }
+
+ getInitConfigurationBuilder().exceptionHandlerEnabled = enabled;
+ }
+
+ void _MaxSetExtraParameter(const char *key, const char *value)
+ {
+ NSString *stringKey = NSSTRING(key);
+ if ( ![stringKey al_isValidString] )
+ {
+ NSString *message = [NSString stringWithFormat:@"Failed to set extra parameter for nil or empty key: %@", stringKey];
+ max_unity_log_error(message);
+ return;
+ }
+
+ NSString *stringValue = NSSTRING(value);
+ if ( [@"disable_all_logs" isEqualToString: stringKey] )
+ {
+ _disableAllLogs = [@"true" al_isEqualToStringIgnoringCase: stringValue];
+ }
+
+ ALSdkSettings *settings = getSdk().settings;
+ [settings setExtraParameterForKey: stringKey value: stringValue];
+ }
+
+ int * _MaxGetSafeAreaInsets()
+ {
+ UIEdgeInsets safeAreaInsets = UnityGetGLView().safeAreaInsets;
+ static int insets[4] = {(int) safeAreaInsets.left, (int) safeAreaInsets.top, (int) safeAreaInsets.right, (int) safeAreaInsets.bottom};
+ return insets;
+ }
+
+ void _MaxShowCmpForExistingUser()
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxShowCmpForExistingUser");
+ return;
+ }
+
+ [getAdManager() showCMPForExistingUser];
+ }
+
+ bool _MaxHasSupportedCmp()
+ {
+ if ( !_initializeSdkCalled )
+ {
+ max_unity_log_uninitialized_access_error("_MaxHasSupportedCmp");
+ return false;
+ }
+
+ return [getSdk().cmpService hasSupportedCMP];
+ }
+
+ float _MaxGetAdaptiveBannerHeight(const float width)
+ {
+ return [MAUnityAdManager adaptiveBannerHeightForWidth: width];
+ }
+
+ void max_unity_log_uninitialized_access_error(const char *callingMethod)
+ {
+ NSString *message = [NSString stringWithFormat:@"Failed to execute: %s - please ensure the AppLovin MAX Unity Plugin has been initialized by calling 'MaxSdk.InitializeSdk();'!", callingMethod];
+ max_unity_log_error(message);
+ }
+
+ void max_unity_log_error(NSString *message)
+ {
+ if (_disableAllLogs) return;
+
+ NSString *logMessage = [NSString stringWithFormat: @"[%@] %@", TAG, message];
+ NSLog(@"%@", logMessage);
+ }
+}
+
+#pragma clang diagnostic pop
diff --git a/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityPlugin.mm.meta b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityPlugin.mm.meta
new file mode 100644
index 0000000..5ea08ff
--- /dev/null
+++ b/Assets/MaxSdk/AppLovin/Plugins/iOS/MAUnityPlugin.mm.meta
@@ -0,0 +1,109 @@
+fileFormatVersion: 2
+guid: 7e373ed7168b243e6b706e991ab5a643
+labels:
+- al_max
+- al_max_export_path-MaxSdk/AppLovin/Plugins/iOS/MAUnityPlugin.mm
+PluginImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ iconMap: {}
+ executionOrder: {}
+ defineConstraints: []
+ isPreloaded: 0
+ isOverridable: 0
+ isExplicitlyReferenced: 0
+ validateReferences: 1
+ platformData:
+ - first:
+ '': Any
+ second:
+ enabled: 0
+ settings:
+ Exclude Android: 1
+ Exclude Editor: 1
+ Exclude Linux: 1
+ Exclude Linux64: 1
+ Exclude LinuxUniversal: 1
+ Exclude OSXUniversal: 1
+ Exclude Win: 1
+ Exclude Win64: 1
+ Exclude iOS: 0
+ Exclude tvOS: 1
+ - first:
+ Android: Android
+ second:
+ enabled: 0
+ settings:
+ CPU: ARMv7
+ - first:
+ Any:
+ second:
+ enabled: 0
+ settings: {}
+ - first:
+ Editor: Editor
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ DefaultValueInitialized: true
+ OS: AnyOS
+ - first:
+ Facebook: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Facebook: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Linux
+ second:
+ enabled: 0
+ settings:
+ CPU: x86
+ - first:
+ Standalone: Linux64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: OSXUniversal
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ Standalone: Win64
+ second:
+ enabled: 0
+ settings:
+ CPU: AnyCPU
+ - first:
+ iPhone: iOS
+ second:
+ enabled: 1
+ settings:
+ CompileFlags:
+ FrameworkDependencies:
+ - first:
+ tvOS: tvOS
+ second:
+ enabled: 0
+ settings:
+ CompileFlags:
+ FrameworkDependencies:
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/CHANGELOG.md b/Assets/MaxSdk/CHANGELOG.md
new file mode 100644
index 0000000..6153543
--- /dev/null
+++ b/Assets/MaxSdk/CHANGELOG.md
@@ -0,0 +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).
diff --git a/Assets/MaxSdk/CHANGELOG.md.meta b/Assets/MaxSdk/CHANGELOG.md.meta
new file mode 100644
index 0000000..6d06bcb
--- /dev/null
+++ b/Assets/MaxSdk/CHANGELOG.md.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: dfb944c2e1cb479cabf023040a5942d2
+labels:
+- al_max
+- al_max_export_path-MaxSdk/CHANGELOG.md
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation.meta b/Assets/MaxSdk/Mediation.meta
new file mode 100644
index 0000000..103dd6f
--- /dev/null
+++ b/Assets/MaxSdk/Mediation.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: dcf2020c4018447c9b91170c0f62d799
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/BigoAds.meta b/Assets/MaxSdk/Mediation/BigoAds.meta
new file mode 100644
index 0000000..c290a4a
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/BigoAds.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 8e54ef9b3c383471cbff279efe591702
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/BigoAds/Editor.meta b/Assets/MaxSdk/Mediation/BigoAds/Editor.meta
new file mode 100644
index 0000000..39a3140
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/BigoAds/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 1235281c630214a8999b2185ceba6388
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/BigoAds/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml
new file mode 100644
index 0000000..8ede496
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..521ce00
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: fba397d5cac8648fea9b0fe82e201e63
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/BigoAds/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/ByteDance.meta b/Assets/MaxSdk/Mediation/ByteDance.meta
new file mode 100644
index 0000000..3243df3
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/ByteDance.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 43e300dce912446d9b55a6b6d605f863
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/ByteDance/Editor.meta b/Assets/MaxSdk/Mediation/ByteDance/Editor.meta
new file mode 100644
index 0000000..e54a766
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/ByteDance/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: ef8467ffb0e4447b79a8804884a7520a
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/ByteDance/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml
new file mode 100644
index 0000000..0a07212
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+ https://artifact.bytedance.com/repository/pangle
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..5205233
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 0828555cb1ce94702a4af6f3dce3d735
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/ByteDance/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Fyber.meta b/Assets/MaxSdk/Mediation/Fyber.meta
new file mode 100644
index 0000000..913c132
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Fyber.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 61a57c7ea96624f62b6d255abbeea40e
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Fyber/Editor.meta b/Assets/MaxSdk/Mediation/Fyber/Editor.meta
new file mode 100644
index 0000000..315e8c8
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Fyber/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e076e4ef7e2874ba69b108cc7a346c2a
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Fyber/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml
new file mode 100644
index 0000000..36e6e27
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..fd0bbbf
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Fyber/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 5e123cdc08e804dffb2c40c4fbc83caf
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Fyber/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Google.meta b/Assets/MaxSdk/Mediation/Google.meta
new file mode 100644
index 0000000..acc0fab
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Google.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7ed9d3af4a4af44bbae8b9b358d2ab9a
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Google/Editor.meta b/Assets/MaxSdk/Mediation/Google/Editor.meta
new file mode 100644
index 0000000..ea8dd55
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Google/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e8015bd045cea462c8f39c8a05867d08
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Google/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml
new file mode 100644
index 0000000..63452cc
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..36bef72
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Google/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 053b810d3594744e38b6fd0fa378fb57
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Google/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/GoogleAdManager.meta b/Assets/MaxSdk/Mediation/GoogleAdManager.meta
new file mode 100644
index 0000000..b893864
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/GoogleAdManager.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cbf2880cd0e904084bbdb125e06e3ec8
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/GoogleAdManager/Editor.meta b/Assets/MaxSdk/Mediation/GoogleAdManager/Editor.meta
new file mode 100644
index 0000000..7b0912c
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/GoogleAdManager/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 3e1635bfb5b164c389d38eb1e5e6e909
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/GoogleAdManager/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/GoogleAdManager/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/GoogleAdManager/Editor/Dependencies.xml
new file mode 100644
index 0000000..7e46e08
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/GoogleAdManager/Editor/Dependencies.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/GoogleAdManager/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/GoogleAdManager/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..71b0087
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/GoogleAdManager/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: aebbf5e9af66e431fa6078021b2f762f
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/GoogleAdManager/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/MediationAdapters.txt b/Assets/MaxSdk/Mediation/MediationAdapters.txt
new file mode 100644
index 0000000..40f94d0
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/MediationAdapters.txt
@@ -0,0 +1,2 @@
+A placeholder file to make sure that the Mediation folder asset is exported with the base plugin.
+This ensures that the adapters are imported into this directory even when the plugin has been moved to a different folder.
diff --git a/Assets/MaxSdk/Mediation/MediationAdapters.txt.meta b/Assets/MaxSdk/Mediation/MediationAdapters.txt.meta
new file mode 100644
index 0000000..41fb994
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/MediationAdapters.txt.meta
@@ -0,0 +1,6 @@
+fileFormatVersion: 2
+guid: 9a8f83e97729443c86120be3bb24190d
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/MediationAdapters.txt
+timeCreated: 1609308536
diff --git a/Assets/MaxSdk/Mediation/Mintegral.meta b/Assets/MaxSdk/Mediation/Mintegral.meta
new file mode 100644
index 0000000..ba7f671
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Mintegral.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2b675df2ca27e4ee6a573411e6023520
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Mintegral/Editor.meta b/Assets/MaxSdk/Mediation/Mintegral/Editor.meta
new file mode 100644
index 0000000..d189ce0
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Mintegral/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: db1de4066dc4e4290b3879b34fa87de2
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Mintegral/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml
new file mode 100644
index 0000000..dd8b881
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+ https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..415c4ab
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 221b2a20a58a04f2cb4afb0779587206
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Mintegral/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Moloco.meta b/Assets/MaxSdk/Mediation/Moloco.meta
new file mode 100644
index 0000000..1c7f239
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Moloco.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 25d39f128833749aaa3c51b44fb57f07
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Moloco/Editor.meta b/Assets/MaxSdk/Mediation/Moloco/Editor.meta
new file mode 100644
index 0000000..952b969
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Moloco/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 9ecadc3649c184389b90252aa2a2b448
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Moloco/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Moloco/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/Moloco/Editor/Dependencies.xml
new file mode 100644
index 0000000..168927f
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Moloco/Editor/Dependencies.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/Moloco/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/Moloco/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..e066a5f
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Moloco/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 0f5b2cb1209274ba18b234b42d2efe96
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Moloco/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/UnityAds.meta b/Assets/MaxSdk/Mediation/UnityAds.meta
new file mode 100644
index 0000000..cdd0aaf
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/UnityAds.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: b740f89702a474b0ca129c951f48b682
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/UnityAds/Editor.meta b/Assets/MaxSdk/Mediation/UnityAds/Editor.meta
new file mode 100644
index 0000000..b66e846
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/UnityAds/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 30751f2dc322a40e588edfb7c978c9c0
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/UnityAds/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml
new file mode 100644
index 0000000..993a288
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..937df6e
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 9950f1cb0da1e43efbeca604db142076
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/UnityAds/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Vungle.meta b/Assets/MaxSdk/Mediation/Vungle.meta
new file mode 100644
index 0000000..f5d09b9
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Vungle.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 3abedb062e5ab47939dc811a59a7aeaf
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Vungle/Editor.meta b/Assets/MaxSdk/Mediation/Vungle/Editor.meta
new file mode 100644
index 0000000..1ea75cc
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Vungle/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 77b6ea05736a9458f8ef8762ee9b64bb
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Vungle/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml b/Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml
new file mode 100644
index 0000000..4bb990d
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
+
diff --git a/Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml.meta b/Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml.meta
new file mode 100644
index 0000000..7038186
--- /dev/null
+++ b/Assets/MaxSdk/Mediation/Vungle/Editor/Dependencies.xml.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 0d8ad3a6ddc4f44fab2efe607fe14f26
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Mediation/Vungle/Editor/Dependencies.xml
+TextScriptImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Prefabs.meta b/Assets/MaxSdk/Prefabs.meta
new file mode 100644
index 0000000..901c9e9
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 672665b4fb8ab47bf83309f9ad3f523d
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Prefabs/BannerBottom.prefab b/Assets/MaxSdk/Prefabs/BannerBottom.prefab
new file mode 100644
index 0000000..bba7ac6
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs/BannerBottom.prefab
@@ -0,0 +1,402 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3149432657757030138
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5965516886513985157}
+ - component: {fileID: 3167928293195730454}
+ - component: {fileID: 6048907697713132302}
+ m_Layer: 5
+ m_Name: Panel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5965516886513985157
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4118862295226300947}
+ m_Father: {fileID: 3413013686078446151}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 0}
+ m_AnchoredPosition: {x: 0, y: 84}
+ m_SizeDelta: {x: 0, y: 168}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3167928293195730454
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_CullTransparentMesh: 0
+--- !u!114 &6048907697713132302
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0, g: 0, b: 0, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!1 &3829518492429360636
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4118862295226300947}
+ - component: {fileID: 5582763645243389146}
+ - component: {fileID: 1436650035481827287}
+ m_Layer: 5
+ m_Name: Panel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &4118862295226300947
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3829518492429360636}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 5084927109723455527}
+ - {fileID: 8189983859356672495}
+ m_Father: {fileID: 5965516886513985157}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 1}
+ m_AnchorMax: {x: 0.5, y: 1}
+ m_AnchoredPosition: {x: 0, y: -50}
+ m_SizeDelta: {x: 640, y: 100}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5582763645243389146
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3829518492429360636}
+ m_CullTransparentMesh: 0
+--- !u!114 &1436650035481827287
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3829518492429360636}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!1 &4772536868390647122
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8189983859356672495}
+ - component: {fileID: 7216515036998124610}
+ - component: {fileID: 3801875349924125266}
+ m_Layer: 5
+ m_Name: InfoText
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8189983859356672495
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4772536868390647122}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 4118862295226300947}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 3}
+ m_SizeDelta: {x: 0, y: -6}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7216515036998124610
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4772536868390647122}
+ m_CullTransparentMesh: 0
+--- !u!114 &3801875349924125266
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4772536868390647122}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.29411766, g: 0.29411766, b: 0.29411766, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 18
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 1
+ m_MaxSize: 40
+ m_Alignment: 7
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: Actual banner size may vary on mobile devices
+--- !u!1 &7742712311334970126
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3413013686078446151}
+ - component: {fileID: 2511819091764556577}
+ - component: {fileID: 2638196187645015298}
+ - component: {fileID: 633018877563332563}
+ m_Layer: 5
+ m_Name: BannerBottom
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &3413013686078446151
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0, y: 0, z: 0}
+ m_Children:
+ - {fileID: 5965516886513985157}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0, y: 0}
+--- !u!223 &2511819091764556577
+Canvas:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_RenderMode: 0
+ m_Camera: {fileID: 0}
+ m_PlaneDistance: 100
+ m_PixelPerfect: 0
+ m_ReceivesEvents: 1
+ m_OverrideSorting: 0
+ m_OverridePixelPerfect: 0
+ m_SortingBucketNormalizedSize: 0
+ m_AdditionalShaderChannelsFlag: 0
+ m_SortingLayerID: 0
+ m_SortingOrder: 500
+ m_TargetDisplay: 0
+--- !u!114 &2638196187645015298
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_UiScaleMode: 0
+ m_ReferencePixelsPerUnit: 100
+ m_ScaleFactor: 1
+ m_ReferenceResolution: {x: 800, y: 600}
+ m_ScreenMatchMode: 0
+ m_MatchWidthOrHeight: 0
+ m_PhysicalUnit: 3
+ m_FallbackScreenDPI: 96
+ m_DefaultSpriteDPI: 96
+ m_DynamicPixelsPerUnit: 1
+--- !u!114 &633018877563332563
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreReversedGraphics: 1
+ m_BlockingObjects: 0
+ m_BlockingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+--- !u!1 &8436952342056443077
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5084927109723455527}
+ - component: {fileID: 7267203896389005665}
+ - component: {fileID: 5500603550844857255}
+ m_Layer: 5
+ m_Name: Text
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5084927109723455527
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 4118862295226300947}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: -4}
+ m_SizeDelta: {x: 0, y: -8}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7267203896389005665
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_CullTransparentMesh: 0
+--- !u!114 &5500603550844857255
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 26
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 0
+ m_MaxSize: 71
+ m_Alignment: 1
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: MAX Banner Ad
diff --git a/Assets/MaxSdk/Prefabs/BannerBottom.prefab.meta b/Assets/MaxSdk/Prefabs/BannerBottom.prefab.meta
new file mode 100644
index 0000000..153cfcf
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs/BannerBottom.prefab.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 54c062c0526b148efa2ea2e9489b3aa0
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Prefabs/BannerBottom.prefab
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Prefabs/BannerTop.prefab b/Assets/MaxSdk/Prefabs/BannerTop.prefab
new file mode 100644
index 0000000..25d5f49
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs/BannerTop.prefab
@@ -0,0 +1,402 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3149432657757030138
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5965516886513985157}
+ - component: {fileID: 3167928293195730454}
+ - component: {fileID: 6048907697713132302}
+ m_Layer: 5
+ m_Name: Panel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5965516886513985157
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 4118862295226300947}
+ m_Father: {fileID: 3413013686078446151}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 1}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: -50}
+ m_SizeDelta: {x: 0, y: 100}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3167928293195730454
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_CullTransparentMesh: 0
+--- !u!114 &6048907697713132302
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0, g: 0, b: 0, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!1 &3829518492429360636
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 4118862295226300947}
+ - component: {fileID: 5582763645243389146}
+ - component: {fileID: 1436650035481827287}
+ m_Layer: 5
+ m_Name: Panel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &4118862295226300947
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3829518492429360636}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 5084927109723455527}
+ - {fileID: 7429934296583149777}
+ m_Father: {fileID: 5965516886513985157}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 0}
+ m_AnchorMax: {x: 0.5, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 640, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &5582763645243389146
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3829518492429360636}
+ m_CullTransparentMesh: 0
+--- !u!114 &1436650035481827287
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3829518492429360636}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!1 &5625059666589051168
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 7429934296583149777}
+ - component: {fileID: 6576623406533516677}
+ - component: {fileID: 5405475098690228567}
+ m_Layer: 5
+ m_Name: InfoText
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &7429934296583149777
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5625059666589051168}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 4118862295226300947}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 3}
+ m_SizeDelta: {x: 0, y: -6}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &6576623406533516677
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5625059666589051168}
+ m_CullTransparentMesh: 0
+--- !u!114 &5405475098690228567
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5625059666589051168}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.29411766, g: 0.29411766, b: 0.29411766, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 18
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 1
+ m_MaxSize: 40
+ m_Alignment: 7
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: Actual banner size may vary on mobile devices
+--- !u!1 &7742712311334970126
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3413013686078446151}
+ - component: {fileID: 2511819091764556577}
+ - component: {fileID: 2638196187645015298}
+ - component: {fileID: 633018877563332563}
+ m_Layer: 5
+ m_Name: BannerTop
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &3413013686078446151
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0, y: 0, z: 0}
+ m_Children:
+ - {fileID: 5965516886513985157}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0, y: 0}
+--- !u!223 &2511819091764556577
+Canvas:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_RenderMode: 0
+ m_Camera: {fileID: 0}
+ m_PlaneDistance: 100
+ m_PixelPerfect: 0
+ m_ReceivesEvents: 1
+ m_OverrideSorting: 0
+ m_OverridePixelPerfect: 0
+ m_SortingBucketNormalizedSize: 0
+ m_AdditionalShaderChannelsFlag: 0
+ m_SortingLayerID: 0
+ m_SortingOrder: 500
+ m_TargetDisplay: 0
+--- !u!114 &2638196187645015298
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_UiScaleMode: 0
+ m_ReferencePixelsPerUnit: 100
+ m_ScaleFactor: 1
+ m_ReferenceResolution: {x: 800, y: 600}
+ m_ScreenMatchMode: 0
+ m_MatchWidthOrHeight: 0
+ m_PhysicalUnit: 3
+ m_FallbackScreenDPI: 96
+ m_DefaultSpriteDPI: 96
+ m_DynamicPixelsPerUnit: 1
+--- !u!114 &633018877563332563
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreReversedGraphics: 1
+ m_BlockingObjects: 0
+ m_BlockingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+--- !u!1 &8436952342056443077
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5084927109723455527}
+ - component: {fileID: 7267203896389005665}
+ - component: {fileID: 5500603550844857255}
+ m_Layer: 5
+ m_Name: Text
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5084927109723455527
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 4118862295226300947}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: -4}
+ m_SizeDelta: {x: 0, y: -8}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7267203896389005665
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_CullTransparentMesh: 0
+--- !u!114 &5500603550844857255
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 26
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 0
+ m_MaxSize: 71
+ m_Alignment: 1
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: MAX Banner Ad
diff --git a/Assets/MaxSdk/Prefabs/BannerTop.prefab.meta b/Assets/MaxSdk/Prefabs/BannerTop.prefab.meta
new file mode 100644
index 0000000..52ef464
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs/BannerTop.prefab.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 6521750c87fd14f709d09a1e38ffde47
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Prefabs/BannerTop.prefab
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Prefabs/Interstitial.prefab b/Assets/MaxSdk/Prefabs/Interstitial.prefab
new file mode 100644
index 0000000..10c7c7b
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs/Interstitial.prefab
@@ -0,0 +1,523 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &3149432657757030138
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5965516886513985157}
+ - component: {fileID: 3167928293195730454}
+ - component: {fileID: 6048907697713132302}
+ m_Layer: 5
+ m_Name: Panel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5965516886513985157
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 5084927109723455527}
+ - {fileID: 8681486192829137138}
+ m_Father: {fileID: 3413013686078446151}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3167928293195730454
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_CullTransparentMesh: 0
+--- !u!114 &6048907697713132302
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.88235295, g: 0.88235295, b: 0.88235295, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!1 &5668641863723001099
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8681486192829137138}
+ - component: {fileID: 8217482188885705039}
+ - component: {fileID: 3177344817093501532}
+ - component: {fileID: 4415703054918645549}
+ m_Layer: 5
+ m_Name: MaxInterstitialCloseButton
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8681486192829137138
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5668641863723001099}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 763654547095120985}
+ m_Father: {fileID: 5965516886513985157}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 1, y: 1}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: -200, y: -100}
+ m_SizeDelta: {x: 250, y: 100}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &8217482188885705039
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5668641863723001099}
+ m_CullTransparentMesh: 0
+--- !u!114 &3177344817093501532
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5668641863723001099}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!114 &4415703054918645549
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5668641863723001099}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 3
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 3177344817093501532}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!1 &6155160063690407013
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 1672649117655046507}
+ - component: {fileID: 2484422763836779945}
+ - component: {fileID: 1332063313327512547}
+ - component: {fileID: 9160434328933334999}
+ m_Layer: 5
+ m_Name: EventSystem
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &1672649117655046507
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6155160063690407013}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 3413013686078446151}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &2484422763836779945
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6155160063690407013}
+ m_Enabled: 0
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_FirstSelected: {fileID: 0}
+ m_sendNavigationEvents: 1
+ m_DragThreshold: 10
+--- !u!114 &1332063313327512547
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6155160063690407013}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_HorizontalAxis: Horizontal
+ m_VerticalAxis: Vertical
+ m_SubmitButton: Submit
+ m_CancelButton: Cancel
+ m_InputActionsPerSecond: 10
+ m_RepeatDelay: 0.5
+ m_ForceModuleActive: 0
+--- !u!114 &9160434328933334999
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6155160063690407013}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: e0acf281ba86b4929a6942ecd998395b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+--- !u!1 &6462551400748024402
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 763654547095120985}
+ - component: {fileID: 6719360953530842803}
+ - component: {fileID: 4616290306121202327}
+ m_Layer: 5
+ m_Name: Text
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &763654547095120985
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6462551400748024402}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 8681486192829137138}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &6719360953530842803
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6462551400748024402}
+ m_CullTransparentMesh: 0
+--- !u!114 &4616290306121202327
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6462551400748024402}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 40
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 4
+ m_MaxSize: 47
+ m_Alignment: 4
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: Close
+--- !u!1 &7742712311334970126
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3413013686078446151}
+ - component: {fileID: 2511819091764556577}
+ - component: {fileID: 2638196187645015298}
+ - component: {fileID: 633018877563332563}
+ m_Layer: 5
+ m_Name: Interstitial
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &3413013686078446151
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0, y: 0, z: 0}
+ m_Children:
+ - {fileID: 5965516886513985157}
+ - {fileID: 1672649117655046507}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0, y: 0}
+--- !u!223 &2511819091764556577
+Canvas:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_RenderMode: 0
+ m_Camera: {fileID: 0}
+ m_PlaneDistance: 100
+ m_PixelPerfect: 0
+ m_ReceivesEvents: 1
+ m_OverrideSorting: 0
+ m_OverridePixelPerfect: 0
+ m_SortingBucketNormalizedSize: 0
+ m_AdditionalShaderChannelsFlag: 0
+ m_SortingLayerID: 0
+ m_SortingOrder: 999
+ m_TargetDisplay: 0
+--- !u!114 &2638196187645015298
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_UiScaleMode: 0
+ m_ReferencePixelsPerUnit: 100
+ m_ScaleFactor: 1
+ m_ReferenceResolution: {x: 800, y: 600}
+ m_ScreenMatchMode: 0
+ m_MatchWidthOrHeight: 0
+ m_PhysicalUnit: 3
+ m_FallbackScreenDPI: 96
+ m_DefaultSpriteDPI: 96
+ m_DynamicPixelsPerUnit: 1
+--- !u!114 &633018877563332563
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreReversedGraphics: 1
+ m_BlockingObjects: 0
+ m_BlockingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+--- !u!1 &8436952342056443077
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5084927109723455527}
+ - component: {fileID: 7267203896389005665}
+ - component: {fileID: 5500603550844857255}
+ m_Layer: 5
+ m_Name: MaxInterstitialTitle
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5084927109723455527
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 5965516886513985157}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 300}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7267203896389005665
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_CullTransparentMesh: 0
+--- !u!114 &5500603550844857255
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 50
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 0
+ m_MaxSize: 71
+ m_Alignment: 4
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: MAX Interstitial Ad
diff --git a/Assets/MaxSdk/Prefabs/Interstitial.prefab.meta b/Assets/MaxSdk/Prefabs/Interstitial.prefab.meta
new file mode 100644
index 0000000..4fa240b
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs/Interstitial.prefab.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: 3fc0c2627ce8a4490b8e319326f8a535
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Prefabs/Interstitial.prefab
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Prefabs/Rewarded.prefab b/Assets/MaxSdk/Prefabs/Rewarded.prefab
new file mode 100644
index 0000000..dd3457e
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs/Rewarded.prefab
@@ -0,0 +1,795 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!1 &288832541099509301
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 6736833388881778366}
+ - component: {fileID: 738723989743429661}
+ - component: {fileID: 3366016410383180441}
+ m_Layer: 5
+ m_Name: Text
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &6736833388881778366
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 288832541099509301}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 2219183527695013266}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &738723989743429661
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 288832541099509301}
+ m_CullTransparentMesh: 0
+--- !u!114 &3366016410383180441
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 288832541099509301}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 35
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 3
+ m_MaxSize: 48
+ m_Alignment: 4
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: 'Grant Reward
+
+ (5 coins)'
+--- !u!1 &2620135977249264498
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3035265698078104281}
+ - component: {fileID: 7277178523579209441}
+ - component: {fileID: 8085319417655039649}
+ m_Layer: 5
+ m_Name: MaxRewardStatus
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &3035265698078104281
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2620135977249264498}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 3413013686078446151}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: -250}
+ m_SizeDelta: {x: 0, y: 200}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7277178523579209441
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2620135977249264498}
+ m_CullTransparentMesh: 0
+--- !u!114 &8085319417655039649
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 2620135977249264498}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 40
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 10
+ m_MaxSize: 69
+ m_Alignment: 4
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: Reward not yet granted.
+--- !u!1 &3096027301306273604
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 2219183527695013266}
+ - component: {fileID: 3687429596110321440}
+ - component: {fileID: 3757577222601629632}
+ - component: {fileID: 87227930114769318}
+ m_Layer: 5
+ m_Name: MaxRewardButton
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &2219183527695013266
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3096027301306273604}
+ m_LocalRotation: {x: -0, y: -0, z: -0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 6736833388881778366}
+ m_Father: {fileID: 3413013686078446151}
+ m_RootOrder: 2
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0.5, y: 0.5}
+ m_AnchorMax: {x: 0.5, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: -150}
+ m_SizeDelta: {x: 300, y: 100}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3687429596110321440
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3096027301306273604}
+ m_CullTransparentMesh: 0
+--- !u!114 &3757577222601629632
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3096027301306273604}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!114 &87227930114769318
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3096027301306273604}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 3
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 3757577222601629632}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!1 &3149432657757030138
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5965516886513985157}
+ - component: {fileID: 3167928293195730454}
+ - component: {fileID: 6048907697713132302}
+ m_Layer: 5
+ m_Name: Panel
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5965516886513985157
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 5084927109723455527}
+ - {fileID: 8681486192829137138}
+ m_Father: {fileID: 3413013686078446151}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &3167928293195730454
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_CullTransparentMesh: 0
+--- !u!114 &6048907697713132302
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 3149432657757030138}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.88235295, g: 0.88235295, b: 0.88235295, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!1 &4010021198786516228
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3074011789293951898}
+ - component: {fileID: 301935380126337495}
+ - component: {fileID: 535359239537776472}
+ - component: {fileID: 1463999766055390634}
+ m_Layer: 5
+ m_Name: EventSystem
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!4 &3074011789293951898
+Transform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4010021198786516228}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 3413013686078446151}
+ m_RootOrder: 3
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+--- !u!114 &301935380126337495
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4010021198786516228}
+ m_Enabled: 0
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -619905303, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_FirstSelected: {fileID: 0}
+ m_sendNavigationEvents: 1
+ m_DragThreshold: 10
+--- !u!114 &535359239537776472
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4010021198786516228}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1077351063, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_HorizontalAxis: Horizontal
+ m_VerticalAxis: Vertical
+ m_SubmitButton: Submit
+ m_CancelButton: Cancel
+ m_InputActionsPerSecond: 10
+ m_RepeatDelay: 0.5
+ m_ForceModuleActive: 0
+--- !u!114 &1463999766055390634
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 4010021198786516228}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: e0acf281ba86b4929a6942ecd998395b, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+--- !u!1 &5668641863723001099
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 8681486192829137138}
+ - component: {fileID: 8217482188885705039}
+ - component: {fileID: 3177344817093501532}
+ - component: {fileID: 4415703054918645549}
+ m_Layer: 5
+ m_Name: MaxRewardedCloseButton
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &8681486192829137138
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5668641863723001099}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children:
+ - {fileID: 763654547095120985}
+ m_Father: {fileID: 5965516886513985157}
+ m_RootOrder: 1
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 1, y: 1}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: -200, y: -100}
+ m_SizeDelta: {x: 250, y: 100}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &8217482188885705039
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5668641863723001099}
+ m_CullTransparentMesh: 0
+--- !u!114 &3177344817093501532
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5668641863723001099}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: -765806418, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 1, g: 1, b: 1, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_Sprite: {fileID: 10905, guid: 0000000000000000f000000000000000, type: 0}
+ m_Type: 1
+ m_PreserveAspect: 0
+ m_FillCenter: 1
+ m_FillMethod: 4
+ m_FillAmount: 1
+ m_FillClockwise: 1
+ m_FillOrigin: 0
+ m_UseSpriteMesh: 0
+--- !u!114 &4415703054918645549
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 5668641863723001099}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1392445389, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Navigation:
+ m_Mode: 3
+ m_SelectOnUp: {fileID: 0}
+ m_SelectOnDown: {fileID: 0}
+ m_SelectOnLeft: {fileID: 0}
+ m_SelectOnRight: {fileID: 0}
+ m_Transition: 1
+ m_Colors:
+ m_NormalColor: {r: 1, g: 1, b: 1, a: 1}
+ m_HighlightedColor: {r: 0.9607843, g: 0.9607843, b: 0.9607843, a: 1}
+ m_PressedColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 1}
+ m_DisabledColor: {r: 0.78431374, g: 0.78431374, b: 0.78431374, a: 0.5019608}
+ m_ColorMultiplier: 1
+ m_FadeDuration: 0.1
+ m_SpriteState:
+ m_HighlightedSprite: {fileID: 0}
+ m_PressedSprite: {fileID: 0}
+ m_DisabledSprite: {fileID: 0}
+ m_AnimationTriggers:
+ m_NormalTrigger: Normal
+ m_HighlightedTrigger: Highlighted
+ m_PressedTrigger: Pressed
+ m_DisabledTrigger: Disabled
+ m_Interactable: 1
+ m_TargetGraphic: {fileID: 3177344817093501532}
+ m_OnClick:
+ m_PersistentCalls:
+ m_Calls: []
+--- !u!1 &6462551400748024402
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 763654547095120985}
+ - component: {fileID: 6719360953530842803}
+ - component: {fileID: 4616290306121202327}
+ m_Layer: 5
+ m_Name: Text
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &763654547095120985
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6462551400748024402}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 8681486192829137138}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 1, y: 1}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &6719360953530842803
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6462551400748024402}
+ m_CullTransparentMesh: 0
+--- !u!114 &4616290306121202327
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 6462551400748024402}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 40
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 4
+ m_MaxSize: 47
+ m_Alignment: 4
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: Close
+--- !u!1 &7742712311334970126
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 3413013686078446151}
+ - component: {fileID: 2511819091764556577}
+ - component: {fileID: 2638196187645015298}
+ - component: {fileID: 633018877563332563}
+ m_Layer: 5
+ m_Name: Rewarded
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &3413013686078446151
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 0, y: 0, z: 0}
+ m_Children:
+ - {fileID: 5965516886513985157}
+ - {fileID: 3035265698078104281}
+ - {fileID: 2219183527695013266}
+ - {fileID: 3074011789293951898}
+ m_Father: {fileID: 0}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0}
+ m_AnchorMax: {x: 0, y: 0}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 0}
+ m_Pivot: {x: 0, y: 0}
+--- !u!223 &2511819091764556577
+Canvas:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ serializedVersion: 3
+ m_RenderMode: 0
+ m_Camera: {fileID: 0}
+ m_PlaneDistance: 100
+ m_PixelPerfect: 0
+ m_ReceivesEvents: 1
+ m_OverrideSorting: 0
+ m_OverridePixelPerfect: 0
+ m_SortingBucketNormalizedSize: 0
+ m_AdditionalShaderChannelsFlag: 0
+ m_SortingLayerID: 0
+ m_SortingOrder: 999
+ m_TargetDisplay: 0
+--- !u!114 &2638196187645015298
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1980459831, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_UiScaleMode: 0
+ m_ReferencePixelsPerUnit: 100
+ m_ScaleFactor: 1
+ m_ReferenceResolution: {x: 800, y: 600}
+ m_ScreenMatchMode: 0
+ m_MatchWidthOrHeight: 0
+ m_PhysicalUnit: 3
+ m_FallbackScreenDPI: 96
+ m_DefaultSpriteDPI: 96
+ m_DynamicPixelsPerUnit: 1
+--- !u!114 &633018877563332563
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 7742712311334970126}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 1301386320, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_IgnoreReversedGraphics: 1
+ m_BlockingObjects: 0
+ m_BlockingMask:
+ serializedVersion: 2
+ m_Bits: 4294967295
+--- !u!1 &8436952342056443077
+GameObject:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ serializedVersion: 6
+ m_Component:
+ - component: {fileID: 5084927109723455527}
+ - component: {fileID: 7267203896389005665}
+ - component: {fileID: 5500603550844857255}
+ m_Layer: 5
+ m_Name: MaxRewardTitle
+ m_TagString: Untagged
+ m_Icon: {fileID: 0}
+ m_NavMeshLayer: 0
+ m_StaticEditorFlags: 0
+ m_IsActive: 1
+--- !u!224 &5084927109723455527
+RectTransform:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_LocalRotation: {x: 0, y: 0, z: 0, w: 1}
+ m_LocalPosition: {x: 0, y: 0, z: 0}
+ m_LocalScale: {x: 1, y: 1, z: 1}
+ m_Children: []
+ m_Father: {fileID: 5965516886513985157}
+ m_RootOrder: 0
+ m_LocalEulerAnglesHint: {x: 0, y: 0, z: 0}
+ m_AnchorMin: {x: 0, y: 0.5}
+ m_AnchorMax: {x: 1, y: 0.5}
+ m_AnchoredPosition: {x: 0, y: 0}
+ m_SizeDelta: {x: 0, y: 300}
+ m_Pivot: {x: 0.5, y: 0.5}
+--- !u!222 &7267203896389005665
+CanvasRenderer:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_CullTransparentMesh: 0
+--- !u!114 &5500603550844857255
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 8436952342056443077}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 708705254, guid: f70555f144d8491a825f0804e09c671c, type: 3}
+ m_Name:
+ m_EditorClassIdentifier:
+ m_Material: {fileID: 0}
+ m_Color: {r: 0.19607843, g: 0.19607843, b: 0.19607843, a: 1}
+ m_RaycastTarget: 1
+ m_OnCullStateChanged:
+ m_PersistentCalls:
+ m_Calls: []
+ m_FontData:
+ m_Font: {fileID: 10102, guid: 0000000000000000e000000000000000, type: 0}
+ m_FontSize: 50
+ m_FontStyle: 0
+ m_BestFit: 0
+ m_MinSize: 0
+ m_MaxSize: 71
+ m_Alignment: 4
+ m_AlignByGeometry: 0
+ m_RichText: 1
+ m_HorizontalOverflow: 0
+ m_VerticalOverflow: 0
+ m_LineSpacing: 1
+ m_Text: MAX Rewarded Ad
diff --git a/Assets/MaxSdk/Prefabs/Rewarded.prefab.meta b/Assets/MaxSdk/Prefabs/Rewarded.prefab.meta
new file mode 100644
index 0000000..214bea1
--- /dev/null
+++ b/Assets/MaxSdk/Prefabs/Rewarded.prefab.meta
@@ -0,0 +1,10 @@
+fileFormatVersion: 2
+guid: a28c1544d6f094d5eafc8da2343c9119
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Prefabs/Rewarded.prefab
+PrefabImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Resources.meta b/Assets/MaxSdk/Resources.meta
new file mode 100644
index 0000000..37eb399
--- /dev/null
+++ b/Assets/MaxSdk/Resources.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: cbca9b52b4ef44acea8d9a60d020e2ac
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Resources/AppLovinSettings.asset b/Assets/MaxSdk/Resources/AppLovinSettings.asset
new file mode 100644
index 0000000..6f40b36
--- /dev/null
+++ b/Assets/MaxSdk/Resources/AppLovinSettings.asset
@@ -0,0 +1,20 @@
+%YAML 1.1
+%TAG !u! tag:unity3d.com,2011:
+--- !u!114 &11400000
+MonoBehaviour:
+ m_ObjectHideFlags: 0
+ m_CorrespondingSourceObject: {fileID: 0}
+ m_PrefabInstance: {fileID: 0}
+ m_PrefabAsset: {fileID: 0}
+ m_GameObject: {fileID: 0}
+ m_Enabled: 1
+ m_EditorHideFlags: 0
+ m_Script: {fileID: 11500000, guid: ebc0ba1b5ef6b4a6b9dd53d7eadfea16, type: 3}
+ m_Name: AppLovinSettings
+ m_EditorClassIdentifier:
+ qualityServiceEnabled: 1
+ sdkKey:
+ customGradleVersionUrl:
+ customGradleToolsVersion:
+ adMobAndroidAppId:
+ adMobIosAppId:
diff --git a/Assets/MaxSdk/Resources/AppLovinSettings.asset.meta b/Assets/MaxSdk/Resources/AppLovinSettings.asset.meta
new file mode 100644
index 0000000..d343332
--- /dev/null
+++ b/Assets/MaxSdk/Resources/AppLovinSettings.asset.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 510c93902e94f4592985c4ca8f5501ca
+NativeFormatImporter:
+ externalObjects: {}
+ mainObjectFileID: 11400000
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Resources/Images.meta b/Assets/MaxSdk/Resources/Images.meta
new file mode 100644
index 0000000..df7ef8e
--- /dev/null
+++ b/Assets/MaxSdk/Resources/Images.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 2176a004bd57f4a939538db9ed19bffc
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Resources/Images/alert_icon.png b/Assets/MaxSdk/Resources/Images/alert_icon.png
new file mode 100644
index 0000000..1651950
Binary files /dev/null and b/Assets/MaxSdk/Resources/Images/alert_icon.png differ
diff --git a/Assets/MaxSdk/Resources/Images/alert_icon.png.meta b/Assets/MaxSdk/Resources/Images/alert_icon.png.meta
new file mode 100644
index 0000000..634d290
--- /dev/null
+++ b/Assets/MaxSdk/Resources/Images/alert_icon.png.meta
@@ -0,0 +1,94 @@
+fileFormatVersion: 2
+guid: 572fee4574afa4f6dbf2846e0c152fe8
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Resources/Images/alert_icon.png
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 10
+ 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: -100
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ 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
+ 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:
diff --git a/Assets/MaxSdk/Resources/Images/uninstall_icon.png b/Assets/MaxSdk/Resources/Images/uninstall_icon.png
new file mode 100755
index 0000000..3b55354
Binary files /dev/null and b/Assets/MaxSdk/Resources/Images/uninstall_icon.png differ
diff --git a/Assets/MaxSdk/Resources/Images/uninstall_icon.png.meta b/Assets/MaxSdk/Resources/Images/uninstall_icon.png.meta
new file mode 100644
index 0000000..0f39816
--- /dev/null
+++ b/Assets/MaxSdk/Resources/Images/uninstall_icon.png.meta
@@ -0,0 +1,94 @@
+fileFormatVersion: 2
+guid: d24994f48fdd0430692e3d49279cd782
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Resources/Images/uninstall_icon.png
+TextureImporter:
+ internalIDToNameTable: []
+ externalObjects: {}
+ serializedVersion: 10
+ 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: -100
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ 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
+ 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:
diff --git a/Assets/MaxSdk/Resources/Images/warning_icon.png b/Assets/MaxSdk/Resources/Images/warning_icon.png
new file mode 100644
index 0000000..cc9660e
Binary files /dev/null and b/Assets/MaxSdk/Resources/Images/warning_icon.png differ
diff --git a/Assets/MaxSdk/Resources/Images/warning_icon.png.meta b/Assets/MaxSdk/Resources/Images/warning_icon.png.meta
new file mode 100644
index 0000000..ee4941c
--- /dev/null
+++ b/Assets/MaxSdk/Resources/Images/warning_icon.png.meta
@@ -0,0 +1,91 @@
+fileFormatVersion: 2
+guid: b504c956e7ed744b6b0e7f014e1cac5a
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Resources/Images/warning_icon.png
+TextureImporter:
+ fileIDToRecycleName: {}
+ externalObjects: {}
+ serializedVersion: 9
+ 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: -100
+ wrapU: -1
+ wrapV: -1
+ wrapW: -1
+ 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
+ platformSettings:
+ - serializedVersion: 2
+ buildTarget: DefaultTexturePlatform
+ maxTextureSize: 2048
+ resizeAlgorithm: 0
+ textureFormat: -1
+ textureCompression: 1
+ compressionQuality: 50
+ crunchedCompression: 0
+ allowsAlphaSplitting: 0
+ overridden: 0
+ androidETC2FallbackOverride: 0
+ spriteSheet:
+ serializedVersion: 2
+ sprites: []
+ outline: []
+ physicsShape: []
+ bones: []
+ spriteID:
+ vertices: []
+ indices:
+ edges: []
+ weights: []
+ spritePackingTag:
+ pSDRemoveMatte: 0
+ pSDShowRemoveMatteOption: 0
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts.meta b/Assets/MaxSdk/Scripts.meta
new file mode 100644
index 0000000..db8b9af
--- /dev/null
+++ b/Assets/MaxSdk/Scripts.meta
@@ -0,0 +1,8 @@
+fileFormatVersion: 2
+guid: 7a185e76ddff641299882a658e18cbd2
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager.meta b/Assets/MaxSdk/Scripts/IntegrationManager.meta
new file mode 100644
index 0000000..39f65cc
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: e01816bc45c944d03afb95d035caf0e1
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor.meta
new file mode 100644
index 0000000..1d8400a
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor.meta
@@ -0,0 +1,11 @@
+fileFormatVersion: 2
+guid: 0da229e279400497786c39933fe02e52
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor
+folderAsset: yes
+DefaultImporter:
+ externalObjects: {}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinAutoUpdater.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinAutoUpdater.cs
new file mode 100644
index 0000000..d4cdb00
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinAutoUpdater.cs
@@ -0,0 +1,172 @@
+//
+// AppLovinAutoUpdater.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 1/27/20.
+// Copyright © 2020 AppLovin. All rights reserved.
+//
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using UnityEditor;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ ///
+ /// Handles auto updates for AppLovin MAX plugin.
+ ///
+ public static class AppLovinAutoUpdater
+ {
+ public const string KeyAutoUpdateEnabled = "com.applovin.auto_update_enabled";
+ private const string KeyLastUpdateCheckTime = "com.applovin.last_update_check_time_v2"; // Updated to v2 to force adapter version checks in plugin version 3.1.10.
+ private static readonly DateTime EpochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
+ private static readonly int SecondsInADay = (int) TimeSpan.FromDays(1).TotalSeconds;
+
+ // TODO: Make this list dynamic.
+ public static readonly Dictionary MinAdapterVersions = new Dictionary()
+ {
+ {"ADMOB_NETWORK", "android_23.3.0.1_ios_11.9.0.1"},
+ {"BIDMACHINE_NETWORK", "android_3.0.1.1_ios_3.0.0.0.1"},
+ {"CHARTBOOST_NETWORK", "android_9.7.0.3_ios_9.7.0.2"},
+ {"FACEBOOK_MEDIATE", "android_6.17.0.1_ios_6.15.2.1"},
+ {"FYBER_NETWORK", "android_8.3.1.1_ios_8.3.2.1"},
+ {"GOOGLE_AD_MANAGER_NETWORK", "android_23.3.0.1_ios_11.9.0.1"},
+ {"HYPRMX_NETWORK", "android_6.4.2.1_ios_6.4.1.0.1"},
+ {"INMOBI_NETWORK", "android_10.7.7.1_ios_10.7.5.1"},
+ {"IRONSOURCE_NETWORK", "android_8.3.0.0.2_ios_8.3.0.0.1"},
+ {"LINE_NETWORK", "android_2024.8.27.1_ios_2.8.20240827.1"},
+ {"MINTEGRAL_NETWORK", "android_16.8.51.1_ios_7.7.2.0.1"},
+ {"MOBILEFUSE_NETWORK", "android_1.7.6.1_ios_1.7.6.1"},
+ {"MOLOCO_NETWORK", "android_3.1.0.1_ios_3.1.3.1"},
+ {"MYTARGET_NETWORK", "android_5.22.1.1_ios_5.21.7.1"},
+ {"PUBMATIC_NETWORK", "android_3.9.0.2_ios_3.9.0.2"},
+ {"SMAATO_NETWORK", "android_22.7.0.1_ios_22.8.4.1"},
+ {"TIKTOK_NETWORK", "android_6.2.0.5.2_ios_6.2.0.7.2"},
+ {"UNITY_NETWORK", "android_4.12.2.1_ios_4.12.2.1"},
+ {"VERVE_NETWORK", "android_3.0.4.1_ios_3.0.4.1"},
+ {"VUNGLE_NETWORK", "android_7.4.1.1_ios_7.4.1.1"},
+ {"YANDEX_NETWORK", "android_7.4.0.1_ios_2.18.0.1"},
+ };
+
+ ///
+ /// Checks if a new version of the plugin is available and prompts the user to update if one is available.
+ ///
+ public static void Update()
+ {
+ var now = (int) (DateTime.UtcNow - EpochTime).TotalSeconds;
+ if (EditorPrefs.HasKey(KeyLastUpdateCheckTime))
+ {
+ var elapsedTime = now - EditorPrefs.GetInt(KeyLastUpdateCheckTime);
+
+ // Check if we have checked for a new version in the last 24 hrs and skip update if we have.
+ if (elapsedTime < SecondsInADay) return;
+ }
+
+ // Update last checked time.
+ EditorPrefs.SetInt(KeyLastUpdateCheckTime, now);
+
+ // Load the plugin data
+ AppLovinEditorCoroutine.StartCoroutine(AppLovinIntegrationManager.Instance.LoadPluginData(data =>
+ {
+ if (data == null) return;
+
+ ShowPluginUpdateDialogIfNeeded(data);
+ ShowNetworkAdaptersUpdateDialogIfNeeded(data.MediatedNetworks);
+ ShowGoogleNetworkAdaptersUpdateDialogIfNeeded(data.MediatedNetworks);
+ }));
+ }
+
+ private static void ShowPluginUpdateDialogIfNeeded(PluginData data)
+ {
+ // Check if publisher has disabled auto update.
+ if (!EditorPrefs.GetBool(KeyAutoUpdateEnabled, true)) return;
+
+ // Check if the current and latest version are the same or if the publisher is on a newer version (on beta). If so, skip update.
+ var comparison = data.AppLovinMax.CurrentToLatestVersionComparisonResult;
+ if (comparison == MaxSdkUtils.VersionComparisonResult.Equal || comparison == MaxSdkUtils.VersionComparisonResult.Greater) return;
+
+ // A new version of the plugin is available. Show a dialog to the publisher.
+ var option = EditorUtility.DisplayDialogComplex(
+ "AppLovin MAX Plugin Update",
+ "A new version of AppLovin MAX plugin is available for download. Update now?",
+ "Download",
+ "Not Now",
+ "Don't Ask Again");
+
+ 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
+ {
+ // Do nothing
+ MaxSdkLogger.UserDebug("Update postponed.");
+ }
+ else if (option == 2) // Don't Ask Again
+ {
+ MaxSdkLogger.UserDebug("Auto Update disabled. You can enable it again from the AppLovin Integration Manager");
+ EditorPrefs.SetBool(KeyAutoUpdateEnabled, false);
+ }
+ }
+
+ private static void ShowNetworkAdaptersUpdateDialogIfNeeded(Network[] networks)
+ {
+ var networksToUpdate = networks.Where(network => network.RequiresUpdate).ToList();
+
+ // If all networks are above the required version, do nothing.
+ if (networksToUpdate.Count <= 0) return;
+
+ // We found a few adapters that are not compatible with the current SDK, show alert.
+ var message = "The following network adapters are not compatible with the current version of AppLovin MAX Plugin:\n";
+ foreach (var networkName in networksToUpdate)
+ {
+ message += "\n- ";
+ message += networkName.DisplayName + " (Requires " + MinAdapterVersions[networkName.Name] + " or newer)";
+ }
+
+ message += "\n\nPlease update them to the latest versions to avoid any issues.";
+
+ AppLovinIntegrationManager.ShowBuildFailureDialog(message);
+ }
+
+ private static void ShowGoogleNetworkAdaptersUpdateDialogIfNeeded(Network[] networks)
+ {
+ // AdMob and GAM use the same SDKs so their adapters should use the same underlying SDK version.
+ var googleNetwork = networks.FirstOrDefault(network => network.Name.Equals("ADMOB_NETWORK"));
+ var googleAdManagerNetwork = networks.FirstOrDefault(network => network.Name.Equals("GOOGLE_AD_MANAGER_NETWORK"));
+
+ // If both AdMob and GAM are not integrated, do nothing.
+ if (googleNetwork == null || string.IsNullOrEmpty(googleNetwork.CurrentVersions.Unity) ||
+ googleAdManagerNetwork == null || string.IsNullOrEmpty(googleAdManagerNetwork.CurrentVersions.Unity)) return;
+
+ var isAndroidVersionCompatible = GoogleNetworkAdaptersCompatible(googleNetwork.CurrentVersions.Android, googleAdManagerNetwork.CurrentVersions.Android, "19.8.0.0");
+ var isIosVersionCompatible = GoogleNetworkAdaptersCompatible(googleNetwork.CurrentVersions.Ios, googleAdManagerNetwork.CurrentVersions.Ios, "8.0.0.0");
+
+ if (isAndroidVersionCompatible && isIosVersionCompatible) return;
+
+ var message = "You may see unexpected errors if you use different versions of the AdMob and Google Ad Manager adapter SDKs. " +
+ "AdMob and Google Ad Manager share the same SDKs.\n\n" +
+ "You can be sure that you are using the same SDK for both if the first three numbers in each adapter version match.";
+
+ AppLovinIntegrationManager.ShowBuildFailureDialog(message);
+ }
+
+ private static bool GoogleNetworkAdaptersCompatible(string googleVersion, string googleAdManagerVersion, string breakingVersion)
+ {
+ var googleResult = MaxSdkUtils.CompareVersions(googleVersion, breakingVersion);
+ var googleAdManagerResult = MaxSdkUtils.CompareVersions(googleAdManagerVersion, breakingVersion);
+
+ // If one is less than the breaking version and the other is not, they are not compatible.
+ if (googleResult == MaxSdkUtils.VersionComparisonResult.Lesser &&
+ googleAdManagerResult != MaxSdkUtils.VersionComparisonResult.Lesser) return false;
+
+ if (googleAdManagerResult == MaxSdkUtils.VersionComparisonResult.Lesser &&
+ googleResult != MaxSdkUtils.VersionComparisonResult.Lesser) return false;
+
+ return true;
+ }
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinAutoUpdater.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinAutoUpdater.cs.meta
new file mode 100644
index 0000000..0070e74
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinAutoUpdater.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 8bcdd5226273242c5bd9ec79568202e6
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinAutoUpdater.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinCommandLine.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinCommandLine.cs
new file mode 100644
index 0000000..28cb496
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinCommandLine.cs
@@ -0,0 +1,94 @@
+//
+// AppLovinBuildPostProcessor.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 10/30/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+#if UNITY_IOS || UNITY_IPHONE
+
+using System.Diagnostics;
+using System.IO;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ ///
+ /// A helper class to run command line tools.
+ ///
+ /// TODO: Currently only supports shell (Linux). Add support for Windows machines.
+ ///
+ public static class AppLovinCommandLine
+ {
+ ///
+ /// Result obtained by running a command line command.
+ ///
+ public class Result
+ {
+ ///
+ /// Standard output stream from command line.
+ ///
+ public string StandardOutput;
+
+ ///
+ /// Standard error stream from command line.
+ ///
+ public string StandardError;
+
+ ///
+ /// Exit code returned from command line.
+ ///
+ public int ExitCode;
+
+ ///
+ /// The description of the result that can be used for error logging.
+ ///
+ public string Message;
+ }
+
+ ///
+ /// Runs a command line tool using the provided and .
+ ///
+ /// The tool path to run
+ /// The arguments to be passed to the command line tool
+ /// The directory from which to run this command.
+ ///
+ public static Result Run(string toolPath, string arguments, string workingDirectory)
+ {
+ var stdoutFileName = Path.GetTempFileName();
+ var stderrFileName = Path.GetTempFileName();
+
+ var process = new Process();
+ process.StartInfo.UseShellExecute = true;
+ process.StartInfo.CreateNoWindow = false;
+ process.StartInfo.RedirectStandardInput = false;
+ process.StartInfo.RedirectStandardOutput = false;
+ process.StartInfo.RedirectStandardError = false;
+
+ process.StartInfo.WorkingDirectory = workingDirectory;
+ process.StartInfo.FileName = "bash";
+ process.StartInfo.Arguments = string.Format("-l -c '\"{0}\" {1} 1> {2} 2> {3}'", toolPath, arguments, stdoutFileName, stderrFileName);
+ process.Start();
+
+ process.WaitForExit();
+
+ var stdout = File.ReadAllText(stdoutFileName);
+ var stderr = File.ReadAllText(stderrFileName);
+
+ File.Delete(stdoutFileName);
+ File.Delete(stderrFileName);
+
+ var result = new Result();
+ result.StandardOutput = stdout;
+ result.StandardError = stderr;
+ result.ExitCode = process.ExitCode;
+
+ var messagePrefix = result.ExitCode == 0 ? "Command executed successfully" : "Failed to run command";
+ result.Message = string.Format("{0}: '{1} {2}'\nstdout: {3}\nstderr: {4}\nExit code: {5}", messagePrefix, toolPath, arguments, stdout, stderr, process.ExitCode);
+
+ return result;
+ }
+ }
+}
+
+#endif
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinCommandLine.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinCommandLine.cs.meta
new file mode 100644
index 0000000..2c3ff20
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinCommandLine.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 2d0aa55f9a7d2440f871dfb256372a33
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinCommandLine.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinEditorCoroutine.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinEditorCoroutine.cs
new file mode 100644
index 0000000..f5be939
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinEditorCoroutine.cs
@@ -0,0 +1,91 @@
+//
+// AppLovinEditorCoroutine.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 7/25/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+using System.Collections;
+using System.Collections.Generic;
+using UnityEditor;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ ///
+ /// A coroutine that can update based on editor application update.
+ ///
+ public class AppLovinEditorCoroutine
+ {
+ ///
+ /// Keeps track of the coroutine currently running.
+ ///
+ private IEnumerator enumerator;
+
+ ///
+ /// Keeps track of coroutines that have yielded to the current enumerator.
+ ///
+ private readonly List history = new List();
+
+ private AppLovinEditorCoroutine(IEnumerator enumerator)
+ {
+ this.enumerator = enumerator;
+ }
+
+ ///
+ /// Creates and starts a coroutine.
+ ///
+ /// The coroutine to be started
+ /// The coroutine that has been started.
+ public static AppLovinEditorCoroutine StartCoroutine(IEnumerator enumerator)
+ {
+ var coroutine = new AppLovinEditorCoroutine(enumerator);
+ coroutine.Start();
+ return coroutine;
+ }
+
+ private void Start()
+ {
+ EditorApplication.update += OnEditorUpdate;
+ }
+
+ ///
+ /// Stops the coroutine.
+ ///
+ public void Stop()
+ {
+ if (EditorApplication.update == null) return;
+
+ EditorApplication.update -= OnEditorUpdate;
+ }
+
+ private void OnEditorUpdate()
+ {
+ if (enumerator.MoveNext())
+ {
+ // If there is a coroutine to yield for inside the coroutine, add the initial one to history and continue the second one
+ if (enumerator.Current is IEnumerator)
+ {
+ history.Add(enumerator);
+ enumerator = (IEnumerator) enumerator.Current;
+ }
+ }
+ else
+ {
+ // Current coroutine has ended, check if we have more coroutines in history to be run.
+ if (history.Count == 0)
+ {
+ // No more coroutines to run, stop updating.
+ Stop();
+ }
+ // Step out and finish the code in the coroutine that yielded to it
+ else
+ {
+ var index = history.Count - 1;
+ enumerator = history[index];
+ history.RemoveAt(index);
+ }
+ }
+ }
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinEditorCoroutine.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinEditorCoroutine.cs.meta
new file mode 100644
index 0000000..c622bab
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinEditorCoroutine.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 95747c688378548eeb92aeb528ac96ff
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinEditorCoroutine.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInitialize.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInitialize.cs
new file mode 100644
index 0000000..45ce4d4
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInitialize.cs
@@ -0,0 +1,138 @@
+//
+// MaxInitialization.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Thomas So on 5/24/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+using System.Collections.Generic;
+using System.IO;
+using UnityEditor;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ [InitializeOnLoad]
+ public class AppLovinInitialize
+ {
+ private static readonly List ObsoleteNetworks = new List
+ {
+ "AdColony",
+ "Criteo",
+ "Nend",
+ "Snap",
+ "Tapjoy",
+ "VerizonAds",
+ "VoodooAds"
+ };
+
+ private static readonly List ObsoleteFileExportPathsToDelete = new List
+ {
+ // The `MaxSdk/Scripts/Editor` folder contents have been moved into `MaxSdk/Scripts/IntegrationManager/Editor`.
+ "MaxSdk/Scripts/Editor",
+ "MaxSdk/Scripts/Editor.meta",
+
+ // The `EventSystemChecker` has been renamed to `MaxEventSystemChecker`.
+ "MaxSdk/Scripts/EventSystemChecker.cs",
+ "MaxSdk/Scripts/EventSystemChecker.cs.meta",
+
+ // Google AdMob adapter pre/post process scripts. The logic has been migrated to the main plugin.
+ "MaxSdk/Mediation/Google/Editor/MaxGoogleInitialize.cs",
+ "MaxSdk/Mediation/Google/Editor/MaxGoogleInitialize.cs.meta",
+ "MaxSdk/Mediation/Google/Editor/MaxMediationGoogleUtils.cs",
+ "MaxSdk/Mediation/Google/Editor/MaxMediationGoogleUtils.cs.meta",
+ "MaxSdk/Mediation/Google/Editor/PostProcessor.cs",
+ "MaxSdk/Mediation/Google/Editor/PostProcessor.cs.meta",
+ "MaxSdk/Mediation/Google/Editor/PreProcessor.cs",
+ "MaxSdk/Mediation/Google/Editor/PreProcessor.cs.meta",
+ "MaxSdk/Mediation/Google/Editor/MaxSdk.Mediation.Google.Editor.asmdef",
+ "MaxSdk/Mediation/Google/MaxSdk.Mediation.Google.Editor.asmdef.meta",
+ "Plugins/Android/MaxMediationGoogle.androidlib",
+ "Plugins/Android/MaxMediationGoogle.androidlib.meta",
+
+ // Google Ad Manager adapter pre/post process scripts. The logic has been migrated to the main plugin.
+ "MaxSdk/Mediation/GoogleAdManager/Editor/MaxGoogleAdManagerInitialize.cs",
+ "MaxSdk/Mediation/GoogleAdManager/Editor/MaxGoogleAdManagerInitialize.cs.meta",
+ "MaxSdk/Mediation/GoogleAdManager/Editor/PostProcessor.cs",
+ "MaxSdk/Mediation/GoogleAdManager/Editor/PostProcessor.cs.meta",
+ "MaxSdk/Mediation/GoogleAdManager/Editor/MaxSdk.Mediation.GoogleAdManager.Editor.asmdef",
+ "MaxSdk/Mediation/GoogleAdManager/Editor/MaxSdk.Mediation.GoogleAdManager.Editor.asmdef.meta",
+ "Plugins/Android/MaxMediationGoogleAdManager.androidlib",
+ "Plugins/Android/MaxMediationGoogleAdManager.androidlib.meta",
+
+ // The `VariableService` has been removed.
+ "MaxSdk/Scripts/MaxVariableServiceAndroid.cs",
+ "MaxSdk/Scripts/MaxVariableServiceAndroid.cs.meta",
+ "MaxSdk/Scripts/MaxVariableServiceiOS.cs",
+ "MaxSdk/Scripts/MaxVariableServiceiOS.cs.meta",
+ "MaxSdk/Scripts/MaxVariableServiceUnityEditor.cs",
+ "MaxSdk/Scripts/MaxVariableServiceUnityEditor.cs.meta",
+
+ // The `MaxSdk/Scripts/Editor` folder contents have been moved into `MaxSdk/Scripts/IntegrationManager/Editor`.
+ "MaxSdk/Version.md",
+ "MaxSdk/Version.md.meta",
+
+ // TODO: Add MaxTargetingData and MaxUserSegment when the plugin has enough traction.
+ };
+
+ static AppLovinInitialize()
+ {
+ // Don't run obsolete file cleanup logic when entering play mode.
+ if (EditorApplication.isPlayingOrWillChangePlaymode) return;
+
+#if UNITY_IOS
+ // Check that the publisher is targeting iOS 9.0+
+ if (!PlayerSettings.iOS.targetOSVersionString.StartsWith("9.") && !PlayerSettings.iOS.targetOSVersionString.StartsWith("1"))
+ {
+ MaxSdkLogger.UserError("Detected iOS project version less than iOS 9 - The AppLovin MAX SDK WILL NOT WORK ON < iOS9!!!");
+ }
+#endif
+
+ var isPluginInPackageManager = AppLovinIntegrationManager.IsPluginInPackageManager;
+ if (!isPluginInPackageManager)
+ {
+ var changesMade = false;
+ foreach (var obsoleteFileExportPathToDelete in ObsoleteFileExportPathsToDelete)
+ {
+ var pathToDelete = MaxSdkUtils.GetAssetPathForExportPath(obsoleteFileExportPathToDelete);
+ if (CheckExistence(pathToDelete))
+ {
+ MaxSdkLogger.UserDebug("Deleting obsolete file '" + pathToDelete + "' that is no longer needed.");
+ FileUtil.DeleteFileOrDirectory(pathToDelete);
+ changesMade = true;
+ }
+ }
+
+ var pluginParentDir = AppLovinIntegrationManager.PluginParentDirectory;
+ // Check if any obsolete networks are installed
+ foreach (var obsoleteNetwork in ObsoleteNetworks)
+ {
+ var networkDir = Path.Combine(pluginParentDir, "MaxSdk/Mediation/" + obsoleteNetwork);
+ if (CheckExistence(networkDir))
+ {
+ MaxSdkLogger.UserDebug("Deleting obsolete network " + obsoleteNetwork + " from path " + networkDir + "...");
+ FileUtil.DeleteFileOrDirectory(networkDir);
+ FileUtil.DeleteFileOrDirectory(networkDir + ".meta");
+ changesMade = true;
+ }
+ }
+
+ // Refresh UI
+ if (changesMade)
+ {
+ AssetDatabase.Refresh();
+ MaxSdkLogger.UserDebug("Obsolete networks and files removed.");
+ }
+ }
+
+ AppLovinAutoUpdater.Update();
+ }
+
+ private static bool CheckExistence(string location)
+ {
+ return File.Exists(location) ||
+ Directory.Exists(location) ||
+ (location.EndsWith("/*") && Directory.Exists(Path.GetDirectoryName(location)));
+ }
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInitialize.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInitialize.cs.meta
new file mode 100644
index 0000000..fb8a55c
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInitialize.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 6b7dce7fe193a4058bc51d9f4d3a2aed
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInitialize.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManager.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManager.cs
new file mode 100644
index 0000000..f40580f
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManager.cs
@@ -0,0 +1,473 @@
+//
+// MaxIntegrationManager.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 6/1/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+using System;
+using System.Collections;
+using System.IO;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Networking;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ [Serializable]
+ public class PluginData
+ {
+ // ReSharper disable InconsistentNaming - Consistent with JSON data.
+ public Network AppLovinMax;
+ public Network[] MediatedNetworks;
+ public Network[] PartnerMicroSdks;
+ public DynamicLibraryToEmbed[] ThirdPartyDynamicLibrariesToEmbed;
+ }
+
+ [Serializable]
+ public class Network
+ {
+ //
+ // Sample network data:
+ //
+ // {
+ // "Name": "adcolony",
+ // "DisplayName": "AdColony",
+ // "DownloadUrl": "https://bintray.com/applovin/Unity-Mediation-Packages/download_file?file_path=AppLovin-AdColony-Adapters-Android-3.3.10.1-iOS-3.3.7.2.unitypackage",
+ // "PluginFileName": "AppLovin-AdColony-Adapters-Android-3.3.10.1-iOS-3.3.7.2.unitypackage",
+ // "DependenciesFilePath": "MaxSdk/Mediation/AdColony/Editor/Dependencies.xml",
+ // "LatestVersions" : {
+ // "Unity": "android_3.3.10.1_ios_3.3.7.2",
+ // "Android": "3.3.10.1",
+ // "Ios": "3.3.7.2"
+ // }
+ // }
+ //
+
+ // ReSharper disable InconsistentNaming - Consistent with JSON data.
+ public string Name;
+ public string DisplayName;
+ public string DownloadUrl;
+ public string DependenciesFilePath;
+ public PackageInfo[] Packages;
+ public string[] PluginFilePaths;
+ public Versions LatestVersions;
+ public DynamicLibraryToEmbed[] DynamicLibrariesToEmbed;
+
+ [NonSerialized] public Versions CurrentVersions;
+ [NonSerialized] public MaxSdkUtils.VersionComparisonResult CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Lesser;
+ [NonSerialized] public bool RequiresUpdate;
+ }
+
+ [Serializable]
+ public class DynamicLibraryToEmbed
+ {
+ // ReSharper disable InconsistentNaming - Consistent with JSON data.
+ public string PodName;
+ public string[] FrameworkNames;
+
+ // Min and max versions are inclusive, so if the adapter is the min or max version, the xcframework will get embedded.
+ public string MinVersion;
+ public string MaxVersion;
+
+ public DynamicLibraryToEmbed(string podName, string[] frameworkNames, string minVersion, string maxVersion)
+ {
+ PodName = podName;
+ FrameworkNames = frameworkNames;
+ MinVersion = minVersion;
+ MaxVersion = maxVersion;
+ }
+ }
+
+ ///
+ /// A helper data class used to get current versions from Dependency.xml files.
+ ///
+ [Serializable]
+ public class Versions
+ {
+ // ReSharper disable InconsistentNaming - Consistent with JSON data.
+ public string Unity;
+ public string Android;
+ public string Ios;
+
+ public override bool Equals(object value)
+ {
+ var versions = value as Versions;
+
+ return versions != null
+ && Unity.Equals(versions.Unity)
+ && (Android == null || Android.Equals(versions.Android))
+ && (Ios == null || Ios.Equals(versions.Ios));
+ }
+
+ public bool HasEqualSdkVersions(Versions versions)
+ {
+ return versions != null
+ && AdapterSdkVersion(Android).Equals(AdapterSdkVersion(versions.Android))
+ && AdapterSdkVersion(Ios).Equals(AdapterSdkVersion(versions.Ios));
+ }
+
+ public override int GetHashCode()
+ {
+ return new {unity = Unity, android = Android, ios = Ios}.GetHashCode();
+ }
+
+ private static string AdapterSdkVersion(string adapterVersion)
+ {
+ if (string.IsNullOrEmpty(adapterVersion)) return "";
+
+ var index = adapterVersion.LastIndexOf(".", StringComparison.Ordinal);
+ return index > 0 ? adapterVersion.Substring(0, index) : adapterVersion;
+ }
+ }
+
+ ///
+ /// A manager class for MAX integration manager window.
+ ///
+ public class AppLovinIntegrationManager
+ {
+ ///
+ /// Delegate to be called when downloading a plugin with the progress percentage.
+ ///
+ /// The name of the plugin being downloaded.
+ /// Percentage downloaded.
+ /// Whether or not the download is complete.
+ public delegate void DownloadPluginProgressCallback(string pluginName, float progress, bool done);
+
+ ///
+ /// Delegate to be called when a plugin package is imported.
+ ///
+ /// The network data for which the package is imported.
+ public delegate void ImportPackageCompletedCallback(Network network);
+
+ private static readonly AppLovinIntegrationManager instance = new AppLovinIntegrationManager();
+
+ internal static readonly string GradleTemplatePath = Path.Combine("Assets/Plugins/Android", "mainTemplate.gradle");
+ 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 static string _externalDependencyManagerVersion;
+
+ public static DownloadPluginProgressCallback OnDownloadPluginProgressCallback;
+ public static ImportPackageCompletedCallback OnImportPackageCompletedCallback;
+
+ private UnityWebRequest webRequest;
+ private Network importingNetwork;
+
+ ///
+ /// An Instance of the Integration manager.
+ ///
+ public static AppLovinIntegrationManager Instance
+ {
+ get { return instance; }
+ }
+
+ ///
+ /// The parent directory path where the MaxSdk plugin directory is placed.
+ ///
+ public static string PluginParentDirectory
+ {
+ 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).
+ var maxSdkScriptAssetPath = MaxSdkUtils.GetAssetPathForExportPath(MaxSdkAssetExportPath);
+
+ // maxSdkScriptAssetPath will always have AltDirectorySeparatorChar (/) as the path separator. Convert to platform specific path.
+ return maxSdkScriptAssetPath.Replace(MaxSdkAssetExportPath, "")
+ .Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
+ }
+ }
+
+ public static string MediationDirectory
+ {
+ get
+ {
+ var mediationAssetPath = MaxSdkUtils.GetAssetPathForExportPath(MaxSdkMediationExportPath);
+ return mediationAssetPath.Replace(Path.AltDirectorySeparatorChar, Path.DirectorySeparatorChar);
+ }
+ }
+
+ ///
+ /// Whether or not the plugin is in the Unity Package Manager.
+ ///
+ public static bool IsPluginInPackageManager
+ {
+ get { return PluginParentDirectory.StartsWith("Packages"); }
+ }
+
+ ///
+ /// Whether or not gradle build system is enabled.
+ ///
+ public static bool GradleBuildEnabled
+ {
+ get { return GetEditorUserBuildSetting("androidBuildSystem", "").ToString().Equals("Gradle"); }
+ }
+
+ ///
+ /// Whether or not Gradle template is enabled.
+ ///
+ public static bool GradleTemplateEnabled
+ {
+ get { return GradleBuildEnabled && File.Exists(GradleTemplatePath); }
+ }
+
+ ///
+ /// Whether or not the Quality Service settings can be processed which requires Gradle template enabled or Unity IDE newer than version 2018_2.
+ ///
+ public static bool CanProcessAndroidQualityServiceSettings
+ {
+ get { return GradleTemplateEnabled || GradleBuildEnabled; }
+ }
+
+ ///
+ /// The External Dependency Manager version obtained dynamically.
+ ///
+ public static string ExternalDependencyManagerVersion
+ {
+ get
+ {
+ if (MaxSdkUtils.IsValidString(_externalDependencyManagerVersion)) return _externalDependencyManagerVersion;
+
+ try
+ {
+ var versionHandlerVersionNumberType = Type.GetType("Google.VersionHandlerVersionNumber, Google.VersionHandlerImpl");
+ _externalDependencyManagerVersion = versionHandlerVersionNumberType.GetProperty("Value").GetValue(null, null).ToString();
+ }
+#pragma warning disable 0168
+ catch (Exception ignored)
+#pragma warning restore 0168
+ {
+ _externalDependencyManagerVersion = "Failed to get version.";
+ }
+
+ return _externalDependencyManagerVersion;
+ }
+ }
+
+ private AppLovinIntegrationManager()
+ {
+ // Add asset import callbacks.
+ AssetDatabase.importPackageCompleted += packageName =>
+ {
+ if (!IsImportingNetwork(packageName)) return;
+
+ AssetDatabase.Refresh();
+
+ CallImportPackageCompletedCallback(importingNetwork);
+ importingNetwork = null;
+ };
+
+ AssetDatabase.importPackageCancelled += packageName =>
+ {
+ if (!IsImportingNetwork(packageName)) return;
+
+ MaxSdkLogger.UserDebug("Package import cancelled.");
+ importingNetwork = null;
+ };
+
+ AssetDatabase.importPackageFailed += (packageName, errorMessage) =>
+ {
+ if (!IsImportingNetwork(packageName)) return;
+
+ MaxSdkLogger.UserError(errorMessage);
+ importingNetwork = null;
+ };
+ }
+
+ static AppLovinIntegrationManager() { }
+
+ public static PluginData LoadPluginDataSync()
+ {
+ var url = string.Format(PluginDataEndpoint, MaxSdk.Version);
+ using (var unityWebRequest = UnityWebRequest.Get(url))
+ {
+ var operation = unityWebRequest.SendWebRequest();
+
+ // Just wait till www is done
+ while (!operation.isDone) { }
+
+ return CreatePluginDataFromWebResponse(unityWebRequest);
+ }
+ }
+
+ ///
+ /// Loads the plugin data to be display by integration manager window.
+ ///
+ /// Callback to be called once the plugin data download completes.
+ public IEnumerator LoadPluginData(Action callback)
+ {
+ var url = string.Format(PluginDataEndpoint, MaxSdk.Version);
+ using (var unityWebRequest = UnityWebRequest.Get(url))
+ {
+ 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);
+
+ callback(pluginData);
+ }
+ }
+
+ private static PluginData CreatePluginDataFromWebResponse(UnityWebRequest unityWebRequest)
+ {
+#if UNITY_2020_1_OR_NEWER
+ if (unityWebRequest.result != UnityWebRequest.Result.Success)
+#else
+ if (unityWebRequest.isNetworkError || unityWebRequest.isHttpError)
+#endif
+ {
+ MaxSdkLogger.E("Failed to load plugin data. Please check your internet connection.");
+ return null;
+ }
+
+ PluginData pluginData;
+ try
+ {
+ pluginData = JsonUtility.FromJson(unityWebRequest.downloadHandler.text);
+ AppLovinPackageManager.PluginData = pluginData;
+ }
+ catch (Exception exception)
+ {
+ Console.WriteLine(exception);
+ pluginData = null;
+ }
+
+ if (pluginData == null) return null;
+
+ // Get current version of the plugin
+ var appLovinMax = pluginData.AppLovinMax;
+ AppLovinPackageManager.UpdateCurrentVersions(appLovinMax);
+
+ // Get current versions for all the mediation networks.
+ foreach (var network in pluginData.MediatedNetworks)
+ {
+ AppLovinPackageManager.UpdateCurrentVersions(network);
+ }
+
+ foreach (var partnerMicroSdk in pluginData.PartnerMicroSdks)
+ {
+ AppLovinPackageManager.UpdateCurrentVersions(partnerMicroSdk);
+ }
+
+ return pluginData;
+ }
+
+ ///
+ /// Downloads the plugin file for a given network.
+ ///
+ /// Network for which to download the current version.
+ /// Whether or not to show the import window when downloading. Defaults to true.
+ ///
+ 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)
+ {
+ method = UnityWebRequest.kHttpVerbGET,
+ downloadHandler = downloadHandler
+ };
+
+ var operation = webRequest.SendWebRequest();
+ while (!operation.isDone)
+ {
+ 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;
+ }
+
+ ///
+ /// Cancels the plugin download if one is in progress.
+ ///
+ public void CancelDownload()
+ {
+ if (webRequest == null) return;
+
+ webRequest.Abort();
+ }
+
+ ///
+ /// Shows a dialog to the user with the given message and logs the error message to console.
+ ///
+ /// The failure message to be shown to the user.
+ public static void ShowBuildFailureDialog(string message)
+ {
+ var openIntegrationManager = EditorUtility.DisplayDialog("AppLovin MAX", message, "Open Integration Manager", "Dismiss");
+ if (openIntegrationManager)
+ {
+ AppLovinIntegrationManagerWindow.ShowManager();
+ }
+
+ MaxSdkLogger.UserError(message);
+ }
+
+ #region Utility Methods
+
+ ///
+ /// Checks whether or not the given package name is the currently importing package.
+ ///
+ /// The name of the package that needs to be checked.
+ /// true if the importing package matches the given package name.
+ private bool IsImportingNetwork(string packageName)
+ {
+ // Note: The pluginName doesn't have the '.unitypackage' extension included in its name but the pluginFileName does. So using Contains instead of Equals.
+ return importingNetwork != null && GetPluginFileName(importingNetwork).Contains(packageName);
+ }
+
+ private static void CallDownloadPluginProgressCallback(string pluginName, float progress, bool isDone)
+ {
+ if (OnDownloadPluginProgressCallback == null) return;
+
+ OnDownloadPluginProgressCallback(pluginName, progress, isDone);
+ }
+
+ private static void CallImportPackageCompletedCallback(Network network)
+ {
+ if (OnImportPackageCompletedCallback == null) return;
+
+ OnImportPackageCompletedCallback(network);
+ }
+
+ private static object GetEditorUserBuildSetting(string name, object defaultValue)
+ {
+ var editorUserBuildSettingsType = typeof(EditorUserBuildSettings);
+ var property = editorUserBuildSettingsType.GetProperty(name);
+ if (property != null)
+ {
+ var value = property.GetValue(null, null);
+ if (value != null) return value;
+ }
+
+ return defaultValue;
+ }
+
+ private static string GetPluginFileName(Network network)
+ {
+ return network.Name.ToLowerInvariant() + "_" + network.LatestVersions.Unity + ".unitypackage";
+ }
+
+ #endregion
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManager.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManager.cs.meta
new file mode 100644
index 0000000..e9e3603
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManager.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: c5b874c1e65274159bcc0dc15d9458cc
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManager.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerUtils.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerUtils.cs
new file mode 100644
index 0000000..6f50f25
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerUtils.cs
@@ -0,0 +1,80 @@
+using System;
+using System.Linq;
+using UnityEngine;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ public static class AppLovinIntegrationManagerUtils
+ {
+ ///
+ /// Compares AppLovin MAX Unity mediation adapter plugin versions. Returns , ,
+ /// or as the first version is less than, equal to, or greater than the second.
+ ///
+ /// If a version for a specific platform is only present in one of the provided versions, the one that contains it is considered newer.
+ ///
+ /// The first version to be compared.
+ /// The second version to be compared.
+ ///
+ /// if versionA is less than versionB.
+ /// if versionA and versionB are equal.
+ /// if versionA is greater than versionB.
+ ///
+ internal static MaxSdkUtils.VersionComparisonResult CompareUnityMediationVersions(string versionA, string versionB)
+ {
+ if (versionA.Equals(versionB)) return MaxSdkUtils.VersionComparisonResult.Equal;
+
+ // Unity version would be of format: android_w.x.y.z_ios_a.b.c.d
+ // For Android only versions it would be: android_w.x.y.z
+ // For iOS only version it would be: ios_a.b.c.d
+
+ // After splitting into their respective components, the versions would be at the odd indices.
+ var versionAComponents = versionA.Split('_').ToList();
+ var versionBComponents = versionB.Split('_').ToList();
+
+ var androidComparison = MaxSdkUtils.VersionComparisonResult.Equal;
+ if (versionA.Contains("android") && versionB.Contains("android"))
+ {
+ var androidVersionA = versionAComponents[1];
+ var androidVersionB = versionBComponents[1];
+ androidComparison = MaxSdkUtils.CompareVersions(androidVersionA, androidVersionB);
+
+ // Remove the Android version component so that iOS versions can be processed.
+ versionAComponents.RemoveRange(0, 2);
+ versionBComponents.RemoveRange(0, 2);
+ }
+ else if (versionA.Contains("android"))
+ {
+ androidComparison = MaxSdkUtils.VersionComparisonResult.Greater;
+
+ // Remove the Android version component so that iOS versions can be processed.
+ versionAComponents.RemoveRange(0, 2);
+ }
+ else if (versionB.Contains("android"))
+ {
+ androidComparison = MaxSdkUtils.VersionComparisonResult.Lesser;
+
+ // Remove the Android version component so that iOS version can be processed.
+ versionBComponents.RemoveRange(0, 2);
+ }
+
+ var iosComparison = MaxSdkUtils.VersionComparisonResult.Equal;
+ if (versionA.Contains("ios") && versionB.Contains("ios"))
+ {
+ var iosVersionA = versionAComponents[1];
+ var iosVersionB = versionBComponents[1];
+ iosComparison = MaxSdkUtils.CompareVersions(iosVersionA, iosVersionB);
+ }
+ else if (versionA.Contains("ios"))
+ {
+ iosComparison = MaxSdkUtils.VersionComparisonResult.Greater;
+ }
+ else if (versionB.Contains("ios"))
+ {
+ iosComparison = MaxSdkUtils.VersionComparisonResult.Lesser;
+ }
+
+ // If either one of the Android or iOS version is greater, the entire version should be greater.
+ return (androidComparison == MaxSdkUtils.VersionComparisonResult.Greater || iosComparison == MaxSdkUtils.VersionComparisonResult.Greater) ? MaxSdkUtils.VersionComparisonResult.Greater : MaxSdkUtils.VersionComparisonResult.Lesser;
+ }
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerUtils.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerUtils.cs.meta
new file mode 100644
index 0000000..9efcdd9
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerUtils.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 96efb4dba39eb48d2a60afab69786e6a
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerUtils.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerWindow.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerWindow.cs
new file mode 100644
index 0000000..ca72614
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerWindow.cs
@@ -0,0 +1,1075 @@
+//
+// MaxIntegrationManager.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 5/27/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+using System;
+using System.Collections;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ public class AppLovinIntegrationManagerWindow : EditorWindow
+ {
+ private const string WindowTitle = "AppLovin Integration Manager";
+
+ 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 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 WarningIconExportPath = "MaxSdk/Resources/Images/warning_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.";
+
+ 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 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";
+ private const string KeyShowPrivacySettings = "com.applovin.show_privacy_settings";
+ private const string KeyShowOtherSettings = "com.applovin.show_other_settings";
+
+ private const string ExpandButtonText = "+";
+ private const string CollapseButtonText = "-";
+
+ private const string ExternalDependencyManagerPath = "Assets/ExternalDependencyManager";
+
+ private readonly string[] debugUserGeographies = new string[2] {"Not Set", "GDPR"};
+
+ private Vector2 scrollPosition;
+ private static readonly Vector2 WindowMinSize = new Vector2(750, 750);
+ private const float ActionFieldWidth = 60f;
+ 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 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);
+
+ private static readonly Color DarkModeTextColor = new Color(0.29f, 0.6f, 0.8f);
+
+ private GUIStyle titleLabelStyle;
+ private GUIStyle headerLabelStyle;
+ private GUIStyle environmentValueStyle;
+ private GUIStyle wrapTextLabelStyle;
+ private GUIStyle linkLabelStyle;
+ private GUIStyle iconStyle;
+
+ private PluginData pluginData;
+ private bool pluginDataLoadFailed;
+ private bool shouldShowGoogleWarning;
+ private bool networkButtonsEnabled = true;
+
+ private AppLovinEditorCoroutine loadDataCoroutine;
+ private Texture2D uninstallIcon;
+ private Texture2D alertIcon;
+ private Texture2D warningIcon;
+
+ public static void ShowManager()
+ {
+ var manager = GetWindow(utility: true, title: WindowTitle, focus: true);
+ manager.minSize = WindowMinSize;
+ }
+
+ #region Editor Window Lifecyle Methods
+
+ private void Awake()
+ {
+ titleLabelStyle = new GUIStyle(EditorStyles.label)
+ {
+ fontSize = 14,
+ fontStyle = FontStyle.Bold,
+ fixedHeight = 20
+ };
+
+ headerLabelStyle = new GUIStyle(EditorStyles.label)
+ {
+ fontSize = 12,
+ fontStyle = FontStyle.Bold,
+ fixedHeight = 18
+ };
+
+ environmentValueStyle = new GUIStyle(EditorStyles.label)
+ {
+ alignment = TextAnchor.MiddleRight
+ };
+
+ linkLabelStyle = new GUIStyle(EditorStyles.label)
+ {
+ wordWrap = true,
+ normal = {textColor = EditorGUIUtility.isProSkin ? DarkModeTextColor : Color.blue}
+ };
+
+ wrapTextLabelStyle = new GUIStyle(EditorStyles.label)
+ {
+ wordWrap = true
+ };
+
+ iconStyle = new GUIStyle(EditorStyles.miniButton)
+ {
+ fixedWidth = 18,
+ fixedHeight = 18,
+ padding = new RectOffset(1, 1, 1, 1)
+ };
+
+ // Load uninstall icon texture.
+ var uninstallIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(UninstallIconExportPath));
+ // 1. Set the initial size to 1, as Unity 6000 no longer supports a width or height of 0.
+ // 2. The image will be automatically resized once the image asset is loaded.
+ // 3. Set mipChain to false, else the texture has a weird blurry effect.
+ 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 warning icon texture.
+ var warningIconData = File.ReadAllBytes(MaxSdkUtils.GetAssetPathForExportPath(WarningIconExportPath));
+ warningIcon = new Texture2D(1, 1, TextureFormat.RGBA32, false);
+ warningIcon.LoadImage(warningIconData);
+ }
+
+ private void OnEnable()
+ {
+ // Script reloads can cause AppLovinSettings.Instance to be null for one frame,
+ // so we load the Integration Manager on the following frame
+ if (AppLovinSettings.Instance == null)
+ {
+ AppLovinEditorCoroutine.StartCoroutine(WaitForNextFrameForEnable());
+ }
+ else
+ {
+ OnWindowEnabled();
+ }
+ }
+
+ private IEnumerator WaitForNextFrameForEnable()
+ {
+ yield return new WaitForEndOfFrame();
+ OnWindowEnabled();
+ }
+
+ private void OnWindowEnabled()
+ {
+ AppLovinIntegrationManager.OnDownloadPluginProgressCallback = OnDownloadPluginProgress;
+
+ // Plugin downloaded and imported. Update current versions for the imported package.
+ AppLovinIntegrationManager.OnImportPackageCompletedCallback = OnImportPackageCompleted;
+
+ Load();
+ }
+
+ private void OnDisable()
+ {
+ if (loadDataCoroutine != null)
+ {
+ loadDataCoroutine.Stop();
+ loadDataCoroutine = null;
+ }
+
+ AppLovinIntegrationManager.Instance.CancelDownload();
+ EditorUtility.ClearProgressBar();
+
+ // Saves the AppLovinSettings object if it has been changed.
+ AssetDatabase.SaveAssets();
+ }
+
+ 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)
+ {
+ _previousWindowWidth = position.width;
+ CalculateFieldWidth();
+ }
+
+ using (var scrollView = new EditorGUILayout.ScrollViewScope(scrollPosition, false, false))
+ {
+ scrollPosition = scrollView.scrollPosition;
+
+ GUILayout.Space(5);
+
+ // Draw AppLovin MAX plugin details
+ EditorGUILayout.LabelField("AppLovin MAX Plugin Details", titleLabelStyle);
+
+ DrawPluginDetails();
+
+ if (pluginData != null && pluginData.PartnerMicroSdks != null)
+ {
+ DrawCollapsableSection(KeyShowMicroSdkPartners, "AppLovin Micro SDK Partners", DrawPartnerMicroSdks);
+ }
+
+ // Draw mediated networks);
+ EditorGUILayout.BeginHorizontal();
+ var showDetails = DrawExpandCollapseButton(KeyShowMediatedNetworks);
+ EditorGUILayout.LabelField("Mediated Networks", titleLabelStyle);
+ GUILayout.FlexibleSpace();
+ DrawUpgradeAllButton();
+ EditorGUILayout.EndHorizontal();
+ if (showDetails)
+ {
+ 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);
+
+ DrawCollapsableSection(KeyShowPrivacySettings, "Privacy Settings", DrawPrivacySettings);
+
+ DrawCollapsableSection(KeyShowOtherSettings, "Other Settings", DrawOtherSettings);
+
+ // Draw Unity environment details
+ EditorGUILayout.LabelField("Unity Environment Details", titleLabelStyle);
+ DrawUnityEnvironmentDetails();
+
+ // Draw documentation notes
+ EditorGUILayout.LabelField(new GUIContent(DocumentationNote), wrapTextLabelStyle);
+ if (GUILayout.Button(new GUIContent(DocumentationAdaptersLink), linkLabelStyle))
+ {
+ Application.OpenURL(DocumentationAdaptersLink);
+ }
+ }
+
+ if (GUI.changed)
+ {
+ AppLovinSettings.Instance.SaveAsync();
+ AppLovinInternalSettings.Instance.Save();
+ }
+ }
+
+ #endregion
+
+ #region UI Methods
+
+ ///
+ /// Shows failure or loading screen based on whether or not plugin data failed to load.
+ ///
+ private void DrawEmptyPluginData()
+ {
+ GUILayout.Space(5);
+
+ // Plugin data failed to load. Show error and retry button.
+ if (pluginDataLoadFailed)
+ {
+ GUILayout.Space(10);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(5);
+ EditorGUILayout.LabelField("Failed to load plugin data. Please click retry or restart the integration manager.", titleLabelStyle);
+ if (GUILayout.Button("Retry", FieldWidth))
+ {
+ pluginDataLoadFailed = false;
+ Load();
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ GUILayout.Space(10);
+ }
+ // Still loading, show loading label.
+ else
+ {
+ GUILayout.Space(10);
+ GUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+ EditorGUILayout.LabelField("Loading data...", titleLabelStyle);
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+ GUILayout.Space(10);
+ }
+
+ GUILayout.Space(5);
+ }
+
+ ///
+ /// Draws AppLovin MAX plugin details.
+ ///
+ private void DrawPluginDetails()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ using (new EditorGUILayout.VerticalScope("box"))
+ {
+ // Draw plugin version details
+ DrawHeaders("Platform", false);
+
+ // Immediately after downloading and importing a plugin the entire IDE reloads and current versions can be null in that case. Will just show loading text in that case.
+ if (pluginData == null || pluginData.AppLovinMax.CurrentVersions == null)
+ {
+ DrawEmptyPluginData();
+ }
+ else
+ {
+ var appLovinMax = pluginData.AppLovinMax;
+ // Check if a newer version is available to enable the upgrade button.
+ var upgradeButtonEnabled = appLovinMax.CurrentToLatestVersionComparisonResult == MaxSdkUtils.VersionComparisonResult.Lesser;
+ DrawPluginDetailRow("Unity 3D", appLovinMax.CurrentVersions.Unity, appLovinMax.LatestVersions.Unity);
+ DrawPluginDetailRow("Android", appLovinMax.CurrentVersions.Android, appLovinMax.LatestVersions.Android);
+ DrawPluginDetailRow("iOS", appLovinMax.CurrentVersions.Ios, appLovinMax.LatestVersions.Ios);
+
+ // BeginHorizontal combined with FlexibleSpace makes sure that the button is centered horizontally.
+ GUILayout.BeginHorizontal();
+ GUILayout.FlexibleSpace();
+
+ GUI.enabled = upgradeButtonEnabled;
+ if (GUILayout.Button(new GUIContent("Upgrade"), FieldWidth))
+ {
+ AppLovinEditorCoroutine.StartCoroutine(AppLovinPackageManager.AddNetwork(pluginData.AppLovinMax, true));
+ }
+
+ GUI.enabled = true;
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(5);
+ }
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ }
+
+ ///
+ /// Draws the headers for a table.
+ ///
+ private void DrawHeaders(string firstColumnTitle, bool drawAction)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Space(5);
+ EditorGUILayout.LabelField(firstColumnTitle, headerLabelStyle, _networkWidthOption);
+ EditorGUILayout.LabelField("Current Version", headerLabelStyle, _versionWidthOption);
+ GUILayout.Space(3);
+ EditorGUILayout.LabelField("Latest Version", headerLabelStyle, _versionWidthOption);
+ GUILayout.Space(3);
+ if (drawAction)
+ {
+ GUILayout.FlexibleSpace();
+ GUILayout.Button("Actions", headerLabelStyle, FieldWidth);
+ GUILayout.Space(5);
+ }
+ }
+
+ GUILayout.Space(4);
+ }
+
+ ///
+ /// Draws the platform specific version details for AppLovin MAX plugin.
+ ///
+ private void DrawPluginDetailRow(string platform, string currentVersion, string latestVersion)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Space(5);
+ EditorGUILayout.LabelField(new GUIContent(platform), _networkWidthOption);
+ EditorGUILayout.LabelField(new GUIContent(currentVersion), _versionWidthOption);
+ GUILayout.Space(3);
+ EditorGUILayout.LabelField(new GUIContent(latestVersion), _versionWidthOption);
+ GUILayout.Space(3);
+ }
+
+ GUILayout.Space(4);
+ }
+
+ ///
+ /// Draws mediated network details table.
+ ///
+ private void DrawMediatedNetworks()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ using (new EditorGUILayout.VerticalScope("box"))
+ {
+ DrawHeaders("Network", true);
+
+ // Immediately after downloading and importing a plugin the entire IDE reloads and current versions can be null in that case. Will just show loading text in that case.
+ if (pluginData == null || pluginData.AppLovinMax.CurrentVersions == null)
+ {
+ DrawEmptyPluginData();
+ }
+ else
+ {
+ var networks = pluginData.MediatedNetworks;
+ foreach (var network in networks)
+ {
+ DrawNetworkDetailRow(network);
+ }
+
+ GUILayout.Space(10);
+ }
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ }
+
+ private void DrawPartnerMicroSdks()
+ {
+ if (pluginData == null) return;
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ using (new EditorGUILayout.VerticalScope("box"))
+ {
+ DrawHeaders("Network", true);
+
+ var partnerMicroSdks = pluginData.PartnerMicroSdks;
+ foreach (var partnerMicroSdk in partnerMicroSdks)
+ {
+ DrawNetworkDetailRow(partnerMicroSdk);
+ }
+
+ GUILayout.Space(10);
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ }
+
+ ///
+ /// Draws the network specific details for a given network.
+ ///
+ private void DrawNetworkDetailRow(Network network)
+ {
+ string action;
+ var currentVersion = network.CurrentVersions != null ? network.CurrentVersions.Unity : "";
+ var latestVersion = network.LatestVersions.Unity;
+ bool isActionEnabled;
+ bool isInstalled;
+ if (string.IsNullOrEmpty(currentVersion))
+ {
+ action = "Install";
+ currentVersion = "Not Installed";
+ isActionEnabled = true;
+ isInstalled = false;
+ }
+ else
+ {
+ isInstalled = true;
+
+ var comparison = network.CurrentToLatestVersionComparisonResult;
+ // A newer version is available
+ if (comparison == MaxSdkUtils.VersionComparisonResult.Lesser)
+ {
+ action = "Upgrade";
+ isActionEnabled = true;
+ }
+ // Current installed version is newer than latest version from DB (beta version)
+ else if (comparison == MaxSdkUtils.VersionComparisonResult.Greater)
+ {
+ action = "Installed";
+ isActionEnabled = false;
+ }
+ // Already on the latest version
+ else
+ {
+ action = "Installed";
+ isActionEnabled = false;
+ }
+ }
+
+ GUILayout.Space(4);
+ using (new EditorGUILayout.HorizontalScope(GUILayout.ExpandHeight(false)))
+ {
+ GUILayout.Space(5);
+ EditorGUILayout.LabelField(new GUIContent(network.DisplayName), _networkWidthOption);
+ EditorGUILayout.LabelField(new GUIContent(currentVersion), _versionWidthOption);
+ GUILayout.Space(3);
+ 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);
+ }
+ 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 (GUILayout.Button(new GUIContent(action), FieldWidth))
+ {
+ AppLovinEditorCoroutine.StartCoroutine(AppLovinPackageManager.AddNetwork(network, true));
+ }
+
+ GUI.enabled = true;
+ GUILayout.Space(2);
+
+ 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();
+ }
+
+ GUI.enabled = true;
+ GUILayout.Space(5);
+ }
+
+ if (isInstalled)
+ {
+ DrawGoogleAppIdTextBoxIfNeeded(network);
+ }
+ }
+
+ private void DrawGoogleAppIdTextBoxIfNeeded(Network network)
+ {
+ // Custom integration for AdMob where the user can enter the Android and iOS App IDs.
+ if (network.Name.Equals("ADMOB_NETWORK"))
+ {
+ // Show only one set of text boxes if both ADMOB and GAM are installed
+ if (AppLovinPackageManager.IsAdapterInstalled(pluginData, "GOOGLE_AD_MANAGER_NETWORK")) return;
+
+ DrawGoogleAppIdTextBox();
+ }
+ // Custom integration for GAM where the user can enter the Android and iOS App IDs.
+ else if (network.Name.Equals("GOOGLE_AD_MANAGER_NETWORK"))
+ {
+ DrawGoogleAppIdTextBox();
+ }
+ }
+
+ ///
+ /// Draws the text box for GAM or ADMOB to input the App ID
+ ///
+ private void DrawGoogleAppIdTextBox()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(20);
+ 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);
+ }
+
+ GUILayout.EndHorizontal();
+ }
+
+ ///
+ /// Draws the upgrade all button
+ ///
+ private void DrawUpgradeAllButton()
+ {
+ GUI.enabled = NetworksRequireUpgrade();
+ if (GUILayout.Button(new GUIContent("Upgrade All"), UpgradeAllButtonFieldWidth))
+ {
+ AppLovinEditorCoroutine.StartCoroutine(UpgradeAllNetworks());
+ }
+
+ GUI.enabled = true;
+ GUILayout.Space(10);
+ }
+
+#if UNITY_2019_2_OR_NEWER
+ private void DrawPluginMigrationHelper()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ using (new EditorGUILayout.VerticalScope("box"))
+ {
+ GUILayout.Space(5);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ EditorGUILayout.LabelField(new GUIContent("This will migrate the AppLovin MAX Unity Plugin and adapters to the Unity Package Manager."), wrapTextLabelStyle);
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+
+ GUI.enabled = true;
+ GUILayout.Space(3);
+ GUILayout.FlexibleSpace();
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ var migrationText = "Upgrade All Adapters and Migrate to UPM";
+ if (GUILayout.Button(new GUIContent(migrationText)))
+ {
+ if (EditorUtility.DisplayDialog("Migrate to UPM?",
+ "Are you sure you want to migrate the SDK and adapters to UPM? This action will move both the MAX SDK and its adapters.", "Yes", "No"))
+ {
+ var deleteExternalDependencyManager = false;
+ if (Directory.Exists(ExternalDependencyManagerPath))
+ {
+ deleteExternalDependencyManager = EditorUtility.DisplayDialog("External Dependency Manager Detected",
+ "Our plugin includes the External Dependency Manager via the Unity Package Manager. Would you like us to automatically remove the existing External Dependency Manager folder, or would you prefer to manage it manually?", "Remove Automatically", "Manage Manually");
+ }
+
+ AppLovinPluginMigrationHelper.MigrateToUnityPackageManager(pluginData, deleteExternalDependencyManager);
+ }
+ }
+
+ GUILayout.Space(10);
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(5);
+ EditorGUILayout.HelpBox("Ensure all changes are committed before migration.", MessageType.Warning);
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ }
+#endif
+
+ private void DrawQualityServiceSettings()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ using (new EditorGUILayout.VerticalScope("box"))
+ {
+ GUILayout.Space(4);
+ if (!AppLovinIntegrationManager.CanProcessAndroidQualityServiceSettings)
+ {
+ GUILayout.Space(4);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ EditorGUILayout.HelpBox(QualityServiceRequiresGradleBuildErrorMsg, MessageType.Warning);
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(4);
+ }
+
+ 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.
+ if (GUILayout.Button(new GUIContent(AppLovinSdkKeyLink), linkLabelStyle))
+ {
+ Application.OpenURL(AppLovinSdkKeyLink);
+ }
+
+ GUILayout.FlexibleSpace();
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(4);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ AppLovinSettings.Instance.QualityServiceEnabled = GUILayout.Toggle(AppLovinSettings.Instance.QualityServiceEnabled, " Enable MAX Ad Review");
+ GUILayout.EndHorizontal();
+ GUILayout.Space(4);
+
+ GUILayout.Space(4);
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ }
+
+ private string DrawTextField(string fieldTitle, string text, GUILayoutOption labelWidth, GUILayoutOption textFieldWidthOption = null, bool isTextFieldEditable = true, string tooltip = "")
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ var guiContent = MaxSdkUtils.IsValidString(tooltip) ? new GUIContent(fieldTitle, tooltip) : new GUIContent(fieldTitle);
+ EditorGUILayout.LabelField(guiContent, labelWidth);
+ GUILayout.Space(4);
+ if (isTextFieldEditable)
+ {
+ text = (textFieldWidthOption == null) ? GUILayout.TextField(text) : GUILayout.TextField(text, textFieldWidthOption);
+ }
+ else
+ {
+ if (textFieldWidthOption == null)
+ {
+ GUILayout.Label(text);
+ }
+ else
+ {
+ GUILayout.Label(text, textFieldWidthOption);
+ }
+ }
+
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+ GUILayout.Space(4);
+
+ return text;
+ }
+
+ private void DrawPrivacySettings()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ using (new EditorGUILayout.VerticalScope("box"))
+ {
+ DrawConsentFlowSettings();
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ }
+
+ private void DrawConsentFlowSettings()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ AppLovinInternalSettings.Instance.ConsentFlowEnabled = GUILayout.Toggle(AppLovinInternalSettings.Instance.ConsentFlowEnabled, " Enable MAX Terms and Privacy Policy Flow");
+ GUILayout.EndHorizontal();
+ GUILayout.Space(6);
+ GUILayout.Space(4);
+ EditorGUILayout.HelpBox("This flow automatically includes Google UMP.", MessageType.Info);
+
+ GUI.enabled = true;
+
+ if (!AppLovinInternalSettings.Instance.ConsentFlowEnabled) return;
+
+ GUILayout.Space(6);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ EditorGUILayout.LabelField("Click the link below to access the guide on creating the GDPR form within AdMob's dashboard.");
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ if (GUILayout.Button(new GUIContent(DocumentationTermsAndPrivacyPolicyFlow), linkLabelStyle))
+ {
+ Application.OpenURL(DocumentationTermsAndPrivacyPolicyFlow);
+ }
+
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+
+ 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);
+
+ GUILayout.Space(4);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR = GUILayout.Toggle(AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR, " Show Terms and Privacy Policy Flow when in GDPR Regions");
+ GUILayout.EndHorizontal();
+
+ GUILayout.Space(4);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ EditorGUILayout.LabelField("iOS specific settings:", headerLabelStyle);
+ GUILayout.EndHorizontal();
+
+ var isEditableTextField = AppLovinInternalSettings.Instance.OverrideDefaultUserTrackingUsageDescriptions;
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ AppLovinInternalSettings.Instance.OverrideDefaultUserTrackingUsageDescriptions = GUILayout.Toggle(AppLovinInternalSettings.Instance.OverrideDefaultUserTrackingUsageDescriptions, " Override Default User Tracking Usage Description");
+ GUILayout.EndHorizontal();
+ GUILayout.Space(4);
+
+ GUILayout.Space(4);
+ AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn = DrawTextField("User Tracking Usage Description", AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn, GUILayout.Width(PrivacySettingLabelWidth), _privacySettingFieldWidthOption, isEditableTextField);
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ AppLovinInternalSettings.Instance.UserTrackingUsageLocalizationEnabled = GUILayout.Toggle(AppLovinInternalSettings.Instance.UserTrackingUsageLocalizationEnabled, " Localize User Tracking Usage Description");
+ GUILayout.EndHorizontal();
+ GUILayout.Space(4);
+
+ 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);
+
+ GUILayout.Space(4);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ EditorGUILayout.HelpBox("If you have your own implementation of InfoPlist.strings localization implementation, please use that instead. Using both at the same time may cause conflicts.", MessageType.Info);
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+ }
+
+ GUILayout.Space(4);
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ GUILayout.Button("Click the link below for more information about User Tracking Usage Description: ", wrapTextLabelStyle);
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ if (GUILayout.Button(new GUIContent(UserTrackingUsageDescriptionDocsLink), linkLabelStyle))
+ {
+ Application.OpenURL(UserTrackingUsageDescriptionDocsLink);
+ }
+
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+ GUILayout.Space(8);
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ EditorGUILayout.LabelField("Testing:", headerLabelStyle);
+ GUILayout.EndHorizontal();
+
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(4);
+ EditorGUILayout.LabelField("Debug User Geography");
+ AppLovinInternalSettings.Instance.DebugUserGeography = (MaxSdkBase.ConsentFlowUserGeography) EditorGUILayout.Popup((int) AppLovinInternalSettings.Instance.DebugUserGeography, debugUserGeographies, _privacySettingFieldWidthOption);
+ GUILayout.Space(4);
+ GUILayout.EndHorizontal();
+
+ EditorGUILayout.HelpBox("Debug User Geography is only enabled in debug mode", MessageType.Info);
+ }
+
+ private void DrawOtherSettings()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ using (new EditorGUILayout.VerticalScope("box"))
+ {
+ GUILayout.Space(5);
+ var autoUpdateEnabled = DrawOtherSettingsToggle(EditorPrefs.GetBool(AppLovinAutoUpdater.KeyAutoUpdateEnabled, true), " Enable Auto Update", "Checks for AppLovin MAX plugin updates and notifies you when an update is available.");
+ EditorPrefs.SetBool(AppLovinAutoUpdater.KeyAutoUpdateEnabled, autoUpdateEnabled);
+ GUILayout.Space(5);
+ 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);
+ EditorGUILayout.HelpBox("This will overwrite the gradle build tools version in your base gradle template.", MessageType.Info);
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ }
+
+ private bool DrawOtherSettingsToggle(bool value, string text, string tooltip = "")
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Space(4);
+ var content = MaxSdkUtils.IsValidString(tooltip) ? new GUIContent(text, tooltip) : new GUIContent(text);
+ var toggleValue = GUILayout.Toggle(value, content);
+ GUILayout.Space(4);
+
+ return toggleValue;
+ }
+ }
+
+ private void DrawUnityEnvironmentDetails()
+ {
+ GUILayout.BeginHorizontal();
+ GUILayout.Space(10);
+ using (new EditorGUILayout.VerticalScope("box"))
+ {
+ DrawUnityEnvironmentDetailRow("Unity Version", Application.unityVersion);
+ GUILayout.Space(5);
+ DrawUnityEnvironmentDetailRow("Platform", Application.platform.ToString());
+ GUILayout.Space(5);
+ DrawUnityEnvironmentDetailRow("External Dependency Manager Version", AppLovinIntegrationManager.ExternalDependencyManagerVersion);
+ GUILayout.Space(5);
+ DrawUnityEnvironmentDetailRow("Gradle Template Enabled", AppLovinIntegrationManager.GradleTemplateEnabled.ToString());
+ }
+
+ GUILayout.Space(5);
+ GUILayout.EndHorizontal();
+ }
+
+ private void DrawUnityEnvironmentDetailRow(string key, string value)
+ {
+ using (new EditorGUILayout.HorizontalScope())
+ {
+ GUILayout.Space(5);
+ EditorGUILayout.LabelField(key, GUILayout.Width(250));
+ GUILayout.FlexibleSpace();
+ EditorGUILayout.LabelField(value, environmentValueStyle);
+ GUILayout.Space(5);
+ }
+ }
+
+ private void DrawCollapsableSection(string keyShowDetails, string label, Action drawContent)
+ {
+ EditorGUILayout.BeginHorizontal();
+ var showDetails = DrawExpandCollapseButton(keyShowDetails);
+
+ EditorGUILayout.LabelField(label, titleLabelStyle);
+ GUILayout.FlexibleSpace();
+ EditorGUILayout.EndHorizontal();
+ if (showDetails)
+ {
+ drawContent();
+ }
+ }
+
+ private bool DrawExpandCollapseButton(string keyShowDetails)
+ {
+ var showDetails = EditorPrefs.GetBool(keyShowDetails, true);
+ var buttonText = showDetails ? CollapseButtonText : ExpandButtonText;
+ if (GUILayout.Button(buttonText, CollapseButtonWidthOption))
+ {
+ EditorPrefs.SetBool(keyShowDetails, !showDetails);
+ }
+
+ return showDetails;
+ }
+
+ ///
+ /// Calculates the fields width based on the width of the window.
+ ///
+ private void CalculateFieldWidth()
+ {
+ 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);
+
+ var versionLabelWidth = Math.Max(VersionFieldMinWidth, availableWidth * VersionFieldWidthPercentage);
+ _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);
+ }
+
+ #endregion
+
+ #region Utility Methods
+
+ ///
+ /// Loads the plugin data to be displayed by this window.
+ ///
+ private void Load()
+ {
+ loadDataCoroutine = AppLovinEditorCoroutine.StartCoroutine(AppLovinIntegrationManager.Instance.LoadPluginData(data =>
+ {
+ if (data == null)
+ {
+ pluginDataLoadFailed = true;
+ }
+ else
+ {
+ pluginData = data;
+ pluginDataLoadFailed = false;
+
+ UpdateShouldShowGoogleWarningIfNeeded();
+ }
+
+ CalculateFieldWidth();
+ Repaint();
+ }));
+ }
+
+ ///
+ /// Callback method that will be called with progress updates when the plugin is being downloaded.
+ ///
+ public static void OnDownloadPluginProgress(string pluginName, float progress, bool done)
+ {
+ // 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();
+ }
+ }
+ }
+
+ private void OnImportPackageCompleted(Network network)
+ {
+ AppLovinPackageManager.UpdateCurrentVersions(network);
+
+ UpdateShouldShowGoogleWarningIfNeeded();
+ }
+
+ private void UpdateShouldShowGoogleWarningIfNeeded()
+ {
+ if (pluginData == null)
+ {
+ shouldShowGoogleWarning = false;
+ return;
+ }
+
+ var networks = pluginData.MediatedNetworks;
+ var googleNetwork = networks.FirstOrDefault(foundNetwork => foundNetwork.Name.Equals("ADMOB_NETWORK"));
+ var googleAdManagerNetwork = networks.FirstOrDefault(foundNetwork => foundNetwork.Name.Equals("GOOGLE_AD_MANAGER_NETWORK"));
+
+ if (googleNetwork != null && googleAdManagerNetwork != null &&
+ MaxSdkUtils.IsValidString(googleNetwork.CurrentVersions.Unity) && MaxSdkUtils.IsValidString(googleAdManagerNetwork.CurrentVersions.Unity) &&
+ !googleNetwork.CurrentVersions.HasEqualSdkVersions(googleAdManagerNetwork.CurrentVersions))
+ {
+ shouldShowGoogleWarning = true;
+ }
+ else
+ {
+ shouldShowGoogleWarning = false;
+ }
+ }
+
+ ///
+ /// Upgrades all outdated networks
+ ///
+ private IEnumerator UpgradeAllNetworks()
+ {
+ networkButtonsEnabled = false;
+ EditorApplication.LockReloadAssemblies();
+ var networks = pluginData.MediatedNetworks;
+ foreach (var network in networks)
+ {
+ var comparison = network.CurrentToLatestVersionComparisonResult;
+ // A newer version is available
+ if (MaxSdkUtils.IsValidString(network.CurrentVersions.Unity) && comparison == MaxSdkUtils.VersionComparisonResult.Lesser)
+ {
+ yield return AppLovinPackageManager.AddNetwork(network, false);
+ }
+ }
+
+ EditorApplication.UnlockReloadAssemblies();
+ networkButtonsEnabled = true;
+
+ // The pluginData becomes stale after the networks have been updated, and we should re-load it.
+ Load();
+ }
+
+ ///
+ /// Returns whether any network adapter needs to be upgraded
+ ///
+ private bool NetworksRequireUpgrade()
+ {
+ if (pluginData == null || pluginData.AppLovinMax.CurrentVersions == null) return false;
+
+ var networks = pluginData.MediatedNetworks;
+ return networks.Any(network => MaxSdkUtils.IsValidString(network.CurrentVersions.Unity) && network.CurrentToLatestVersionComparisonResult == MaxSdkUtils.VersionComparisonResult.Lesser);
+ }
+
+ #endregion
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerWindow.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerWindow.cs.meta
new file mode 100644
index 0000000..d7996fd
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerWindow.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 84c61b4c05e114cec91df447d1b585f8
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinIntegrationManagerWindow.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInternalSettings.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInternalSettings.cs
new file mode 100644
index 0000000..6bcd4dd
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInternalSettings.cs
@@ -0,0 +1,307 @@
+//
+// AppLovinInternalSettigns.cs
+// AppLovin User Engagement Unity Plugin
+//
+// Created by Santosh Bagadi on 9/15/22.
+// Copyright © 2022 AppLovin. All rights reserved.
+//
+
+using System;
+using System.Collections.Generic;
+using System.IO;
+using UnityEngine;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ ///
+ /// A representing the AppLovin internal settings that can be set in the Integration Manager Window.
+ ///
+ /// The scriptable object asset is saved under ProjectSettings as AppLovinInternalSettings.json.
+ ///
+ public class AppLovinInternalSettings : ScriptableObject
+ {
+ 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";
+ private const string DefaultUserTrackingDescriptionEs = "Esto utiliza la información del dispositivo para anuncios y contenido más personalizados";
+ private const string DefaultUserTrackingDescriptionFr = "Cela permet d'utiliser les informations du téléphone pour afficher des contenus publicitaires plus pertinents.";
+ private const string DefaultUserTrackingDescriptionJa = "これはユーザーデータをもとに、より関連性の高い広告コンテンツをお客様に提供します";
+ private const string DefaultUserTrackingDescriptionKo = "보다 개인화된 광고 및 콘텐츠를 위해 기기 정보를 사용합니다.";
+ private const string DefaultUserTrackingDescriptionZhHans = "我们使用设备信息来提供个性化的广告和内容。";
+ private const string DefaultUserTrackingDescriptionZhHant = "我們使用設備信息來提供個性化的廣告和內容。";
+
+ [SerializeField] private bool consentFlowEnabled;
+ [SerializeField] private string consentFlowPrivacyPolicyUrl = string.Empty;
+ [SerializeField] private string consentFlowTermsOfServiceUrl = string.Empty;
+ [SerializeField] private bool shouldShowTermsAndPrivacyPolicyAlertInGDPR;
+ [SerializeField] private bool overrideDefaultUserTrackingUsageDescriptions;
+ [SerializeField] private MaxSdkBase.ConsentFlowUserGeography debugUserGeography;
+ [SerializeField] private string userTrackingUsageDescriptionEn = string.Empty;
+ [SerializeField] private bool userTrackingUsageLocalizationEnabled;
+ [SerializeField] private string userTrackingUsageDescriptionDe = string.Empty;
+ [SerializeField] private string userTrackingUsageDescriptionEs = string.Empty;
+ [SerializeField] private string userTrackingUsageDescriptionFr = string.Empty;
+ [SerializeField] private string userTrackingUsageDescriptionJa = string.Empty;
+ [SerializeField] private string userTrackingUsageDescriptionKo = string.Empty;
+ [SerializeField] private string userTrackingUsageDescriptionZhHans = string.Empty;
+ [SerializeField] private string userTrackingUsageDescriptionZhHant = string.Empty;
+
+ private const string SettingsFilePath = "ProjectSettings/AppLovinInternalSettings.json";
+
+ public static AppLovinInternalSettings Instance
+ {
+ get
+ {
+ if (_instance != null) return _instance;
+
+ _instance = CreateInstance();
+
+ var projectRootPath = Path.GetDirectoryName(Application.dataPath);
+ var settingsFilePath = Path.Combine(projectRootPath, SettingsFilePath);
+ if (!File.Exists(settingsFilePath))
+ {
+ _instance.Save();
+ return _instance;
+ }
+
+ var settingsJson = File.ReadAllText(settingsFilePath);
+ if (string.IsNullOrEmpty(settingsJson))
+ {
+ _instance.Save();
+ return _instance;
+ }
+
+ JsonUtility.FromJsonOverwrite(settingsJson, _instance);
+ return _instance;
+ }
+ }
+
+ public void Save()
+ {
+ var settingsJson = JsonUtility.ToJson(_instance);
+ try
+ {
+ var projectRootPath = Path.GetDirectoryName(Application.dataPath);
+ var settingsFilePath = Path.Combine(projectRootPath, SettingsFilePath);
+ File.WriteAllText(settingsFilePath, settingsJson);
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to save internal settings.");
+ Console.WriteLine(exception);
+ }
+ }
+
+ ///
+ /// Whether or not AppLovin Consent Flow is enabled.
+ ///
+ public bool ConsentFlowEnabled
+ {
+ get { return consentFlowEnabled; }
+ set
+ {
+ var previousValue = consentFlowEnabled;
+ consentFlowEnabled = value;
+
+ if (value)
+ {
+ // If the value didn't change, we don't need to update anything.
+ if (previousValue) return;
+
+ UserTrackingUsageDescriptionEn = DefaultUserTrackingDescriptionEn;
+ UserTrackingUsageLocalizationEnabled = true;
+ }
+ else
+ {
+ ConsentFlowPrivacyPolicyUrl = string.Empty;
+ ConsentFlowTermsOfServiceUrl = string.Empty;
+ UserTrackingUsageDescriptionEn = string.Empty;
+ UserTrackingUsageLocalizationEnabled = false;
+ OverrideDefaultUserTrackingUsageDescriptions = false;
+ }
+ }
+ }
+
+ ///
+ /// A URL pointing to the Privacy Policy for the app to be shown when prompting the user for consent.
+ ///
+ public string ConsentFlowPrivacyPolicyUrl
+ {
+ get { return consentFlowPrivacyPolicyUrl; }
+ set { consentFlowPrivacyPolicyUrl = value; }
+ }
+
+ ///
+ /// An optional URL pointing to the Terms of Service for the app to be shown when prompting the user for consent.
+ ///
+ public string ConsentFlowTermsOfServiceUrl
+ {
+ get { return consentFlowTermsOfServiceUrl; }
+ set { consentFlowTermsOfServiceUrl = value; }
+ }
+
+ ///
+ /// Whether or not to show the Terms and Privacy Policy alert in GDPR regions prior to presenting the CMP prompt.
+ ///
+ public bool ShouldShowTermsAndPrivacyPolicyAlertInGDPR
+ {
+ get { return shouldShowTermsAndPrivacyPolicyAlertInGDPR; }
+ set { shouldShowTermsAndPrivacyPolicyAlertInGDPR = value; }
+ }
+
+ ///
+ /// A User Tracking Usage Description in English to be shown to users when requesting permission to use data for tracking.
+ /// For more information see Apple's documentation.
+ ///
+ public string UserTrackingUsageDescriptionEn
+ {
+ get { return userTrackingUsageDescriptionEn; }
+ set { userTrackingUsageDescriptionEn = value; }
+ }
+
+ ///
+ /// An optional string to set debug user geography
+ ///
+ public MaxSdkBase.ConsentFlowUserGeography DebugUserGeography
+ {
+ get { return debugUserGeography; }
+ set { debugUserGeography = value; }
+ }
+
+ public bool OverrideDefaultUserTrackingUsageDescriptions
+ {
+ get { return overrideDefaultUserTrackingUsageDescriptions; }
+ set
+ {
+ var previousValue = overrideDefaultUserTrackingUsageDescriptions;
+ overrideDefaultUserTrackingUsageDescriptions = value;
+
+ if (!value)
+ {
+ if (!previousValue) return;
+
+ UserTrackingUsageDescriptionEn = DefaultUserTrackingDescriptionEn;
+ UserTrackingUsageDescriptionDe = DefaultUserTrackingDescriptionDe;
+ UserTrackingUsageDescriptionEs = DefaultUserTrackingDescriptionEs;
+ UserTrackingUsageDescriptionFr = DefaultUserTrackingDescriptionFr;
+ UserTrackingUsageDescriptionJa = DefaultUserTrackingDescriptionJa;
+ UserTrackingUsageDescriptionKo = DefaultUserTrackingDescriptionKo;
+ UserTrackingUsageDescriptionZhHans = DefaultUserTrackingDescriptionZhHans;
+ UserTrackingUsageDescriptionZhHant = DefaultUserTrackingDescriptionZhHant;
+ }
+ }
+ }
+
+ ///
+ /// Whether or not to localize User Tracking Usage Description.
+ /// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
+ ///
+ public bool UserTrackingUsageLocalizationEnabled
+ {
+ get { return userTrackingUsageLocalizationEnabled; }
+ set
+ {
+ var previousValue = userTrackingUsageLocalizationEnabled;
+ userTrackingUsageLocalizationEnabled = value;
+
+ if (value)
+ {
+ // If the value didn't change, don't do anything
+ if (previousValue) return;
+
+ // Don't set the default values if they are being overriden.
+ if (OverrideDefaultUserTrackingUsageDescriptions) return;
+
+ UserTrackingUsageDescriptionDe = DefaultUserTrackingDescriptionDe;
+ UserTrackingUsageDescriptionEs = DefaultUserTrackingDescriptionEs;
+ UserTrackingUsageDescriptionFr = DefaultUserTrackingDescriptionFr;
+ UserTrackingUsageDescriptionJa = DefaultUserTrackingDescriptionJa;
+ UserTrackingUsageDescriptionKo = DefaultUserTrackingDescriptionKo;
+ UserTrackingUsageDescriptionZhHans = DefaultUserTrackingDescriptionZhHans;
+ UserTrackingUsageDescriptionZhHant = DefaultUserTrackingDescriptionZhHant;
+ }
+ else
+ {
+ UserTrackingUsageDescriptionDe = string.Empty;
+ UserTrackingUsageDescriptionEs = string.Empty;
+ UserTrackingUsageDescriptionFr = string.Empty;
+ UserTrackingUsageDescriptionJa = string.Empty;
+ UserTrackingUsageDescriptionKo = string.Empty;
+ UserTrackingUsageDescriptionZhHans = string.Empty;
+ UserTrackingUsageDescriptionZhHant = string.Empty;
+ }
+ }
+ }
+
+ ///
+ /// A User Tracking Usage Description in German to be shown to users when requesting permission to use data for tracking.
+ /// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
+ ///
+ public string UserTrackingUsageDescriptionDe
+ {
+ get { return userTrackingUsageDescriptionDe; }
+ set { userTrackingUsageDescriptionDe = value; }
+ }
+
+ ///
+ /// A User Tracking Usage Description in Spanish to be shown to users when requesting permission to use data for tracking.
+ /// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
+ ///
+ public string UserTrackingUsageDescriptionEs
+ {
+ get { return userTrackingUsageDescriptionEs; }
+ set { userTrackingUsageDescriptionEs = value; }
+ }
+
+ ///
+ /// A User Tracking Usage Description in French to be shown to users when requesting permission to use data for tracking.
+ /// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
+ ///
+ public string UserTrackingUsageDescriptionFr
+ {
+ get { return userTrackingUsageDescriptionFr; }
+ set { userTrackingUsageDescriptionFr = value; }
+ }
+
+ ///
+ /// A User Tracking Usage Description in Japanese to be shown to users when requesting permission to use data for tracking.
+ /// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
+ ///
+ public string UserTrackingUsageDescriptionJa
+ {
+ get { return userTrackingUsageDescriptionJa; }
+ set { userTrackingUsageDescriptionJa = value; }
+ }
+
+ ///
+ /// A User Tracking Usage Description in Korean to be shown to users when requesting permission to use data for tracking.
+ /// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
+ ///
+ public string UserTrackingUsageDescriptionKo
+ {
+ get { return userTrackingUsageDescriptionKo; }
+ set { userTrackingUsageDescriptionKo = value; }
+ }
+
+ ///
+ /// A User Tracking Usage Description in Chinese (Simplified) to be shown to users when requesting permission to use data for tracking.
+ /// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
+ ///
+ public string UserTrackingUsageDescriptionZhHans
+ {
+ get { return userTrackingUsageDescriptionZhHans; }
+ set { userTrackingUsageDescriptionZhHans = value; }
+ }
+
+ ///
+ /// A User Tracking Usage Description in Chinese (Traditional) to be shown to users when requesting permission to use data for tracking.
+ /// For more information see Apple's documentation: https://developer.apple.com/documentation/bundleresources/information_property_list/nsusertrackingusagedescription
+ ///
+ public string UserTrackingUsageDescriptionZhHant
+ {
+ get { return userTrackingUsageDescriptionZhHant; }
+ set { userTrackingUsageDescriptionZhHant = value; }
+ }
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInternalSettings.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInternalSettings.cs.meta
new file mode 100644
index 0000000..a863905
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInternalSettings.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 65c51e21887ae42c2839962fb9585e9f
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinInternalSettings.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinMenuItems.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinMenuItems.cs
new file mode 100644
index 0000000..568ed4f
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinMenuItems.cs
@@ -0,0 +1,60 @@
+//
+// MaxMenuItems.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 5/27/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+using UnityEditor;
+using UnityEngine;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ public static class AppLovinMenuItems
+ {
+ /**
+ * The special characters at the end represent a shortcut for this action.
+ *
+ * % - ctrl on Windows, cmd on macOS
+ * # - shift
+ * & - alt
+ *
+ * So, (shift + cmd/ctrl + i) will launch the integration manager
+ */
+ [MenuItem("AppLovin/Integration Manager %#i")]
+ private static void IntegrationManager()
+ {
+ ShowIntegrationManager();
+ }
+
+ [MenuItem("AppLovin/Documentation")]
+ private static void Documentation()
+ {
+ Application.OpenURL("https://developers.applovin.com/en/unity/overview/integration");
+ }
+
+ [MenuItem("AppLovin/Contact Us")]
+ private static void ContactUs()
+ {
+ Application.OpenURL("https://www.applovin.com/contact/");
+ }
+
+ [MenuItem("AppLovin/About")]
+ private static void About()
+ {
+ Application.OpenURL("https://www.applovin.com/about/");
+ }
+
+ [MenuItem("Assets/AppLovin Integration Manager")]
+ private static void AssetsIntegrationManager()
+ {
+ ShowIntegrationManager();
+ }
+
+ private static void ShowIntegrationManager()
+ {
+ AppLovinIntegrationManagerWindow.ShowManager();
+ }
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinMenuItems.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinMenuItems.cs.meta
new file mode 100644
index 0000000..30fb834
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinMenuItems.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 02c2d277874f649d18a59d382420bf65
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinMenuItems.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPackageManager.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPackageManager.cs
new file mode 100644
index 0000000..0b2247c
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPackageManager.cs
@@ -0,0 +1,626 @@
+using System;
+using System.Collections;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+#if !UNITY_2020_1_OR_NEWER
+using System.Reflection;
+#endif
+using System.Xml.Linq;
+using UnityEditor;
+using UnityEditor.PackageManager;
+using UnityEngine;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ [Serializable]
+ public class PackageInfo
+ {
+ // ReSharper disable InconsistentNaming - For JSON Deserialization
+ public string Name;
+ public string Version;
+ }
+
+ public interface IPackageManagerClient
+ {
+ IEnumerator AddNetwork(Network network, bool showImport);
+ void RemoveNetwork(Network network);
+ }
+
+ public static class AppLovinPackageManager
+ {
+ 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
+ }
+ }
+
+ internal static PluginData PluginData { get; set; }
+
+ ///
+ /// Checks whether or not an adapter with the given version or newer exists.
+ ///
+ /// The name of the network (the root adapter folder name in "MaxSdk/Mediation/" folder.
+ /// The min iOS adapter version to check for. Can be null if we want to check for any version.
+ /// The min android adapter version to check for. Can be null if we want to check for any version.
+ /// true if an adapter with the min version is installed.
+ internal static bool IsAdapterInstalled(string adapterName, string iosVersion = null, string androidVersion = null)
+ {
+ var dependencyFilePathList = GetAssetPathListForExportPath("MaxSdk/Mediation/" + adapterName + "/Editor/Dependencies.xml");
+ if (dependencyFilePathList.Count <= 0) return false;
+
+ var currentVersion = GetCurrentVersions(dependencyFilePathList);
+ if (iosVersion != null)
+ {
+ var iosVersionComparison = MaxSdkUtils.CompareVersions(currentVersion.Ios, iosVersion);
+ if (iosVersionComparison == MaxSdkUtils.VersionComparisonResult.Lesser)
+ {
+ return false;
+ }
+ }
+
+ if (androidVersion != null)
+ {
+ var androidVersionComparison = MaxSdkUtils.CompareVersions(currentVersion.Android, androidVersion);
+ if (androidVersionComparison == MaxSdkUtils.VersionComparisonResult.Lesser)
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ ///
+ /// Checks whether an adapter is installed using the plugin data.
+ ///
+ /// The plugin data to check for the adapter
+ /// The name of the network.
+ /// Whether an adapter is installed in the plugin data
+ internal static bool IsAdapterInstalled(PluginData pluginData, string adapterName)
+ {
+ var network = pluginData.MediatedNetworks.Where(mediatedNetwork => mediatedNetwork.Name.Equals(adapterName)).ToList().FirstOrDefault();
+ var networkVersion = network != null ? network.CurrentVersions : null;
+ var currentVersion = networkVersion != null ? networkVersion.Unity : "";
+
+ return MaxSdkUtils.IsValidString(currentVersion);
+ }
+
+ ///
+ /// Gets the mediation networks that are currently installed in the project. If using UPM, checks
+ /// for networks in Packages folder and Mediation folder in case a custom adapter was added to the project.
+ ///
+ /// A list of the installed mediation network names.
+ internal static List GetInstalledMediationNetworks()
+ {
+ var installedNetworks = new List();
+ var installedNetworksInAssets = AppLovinAssetsPackageManager.GetInstalledMediationNetworks();
+ installedNetworks.AddRange(installedNetworksInAssets);
+
+#if UNITY_2019_2_OR_NEWER
+ var installedNetworksInPackages = AppLovinUpmPackageManager.GetInstalledMediationNetworks();
+ installedNetworks.AddRange(installedNetworksInPackages);
+#endif
+
+ if (IsAmazonAppLovinAdapterInstalled())
+ {
+ installedNetworks.Add("AmazonAdMarketplace");
+ }
+
+ return installedNetworks;
+ }
+
+ ///
+ /// Adds a network to the project.
+ ///
+ /// The network to add.
+ /// Whether to show the import window (only for non UPM)
+ internal static IEnumerator AddNetwork(Network network, bool showImport)
+ {
+ yield return PackageManagerClient.AddNetwork(network, showImport);
+
+ AppLovinEditorCoroutine.StartCoroutine(RefreshAssetsAtEndOfFrame(network));
+ }
+
+ ///
+ /// Removes a network from the project.
+ ///
+ /// The network to remove.
+ internal static void RemoveNetwork(Network network)
+ {
+ PackageManagerClient.RemoveNetwork(network);
+
+ AppLovinEditorCoroutine.StartCoroutine(RefreshAssetsAtEndOfFrame(network));
+ }
+
+ #region Utility
+
+ ///
+ /// Gets the list of all asset paths for a given MAX plugin export path.
+ ///
+ /// The actual exported path of the asset.
+ /// The exported path of the MAX plugin asset or an empty list if the asset is not found.
+ private static List GetAssetPathListForExportPath(string exportPath)
+ {
+ var assetLabelToFind = "l:al_max_export_path-" + exportPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
+ var assetGuids = AssetDatabase.FindAssets(assetLabelToFind);
+
+ var assetPaths = new List();
+ foreach (var assetGuid in assetGuids)
+ {
+ assetPaths.Add(AssetDatabase.GUIDToAssetPath(assetGuid));
+ }
+
+ return assetPaths.Count <= 0 ? new List() : assetPaths;
+ }
+
+ ///
+ /// Updates the CurrentVersion fields for a given network data object.
+ ///
+ /// Network for which to update the current versions.
+ 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;
+
+ // If AppLovin mediation plugin, get the version from MaxSdk and the latest and current version comparison.
+ if (network.Name.Equals("APPLOVIN_NETWORK"))
+ {
+ network.CurrentVersions.Unity = MaxSdk.Version;
+
+ var unityVersionComparison = MaxSdkUtils.CompareVersions(network.CurrentVersions.Unity, network.LatestVersions.Unity);
+ var androidVersionComparison = MaxSdkUtils.CompareVersions(network.CurrentVersions.Android, network.LatestVersions.Android);
+ var iosVersionComparison = MaxSdkUtils.CompareVersions(network.CurrentVersions.Ios, network.LatestVersions.Ios);
+
+ // Overall version is same if all the current and latest (from db) versions are same.
+ if (unityVersionComparison == MaxSdkUtils.VersionComparisonResult.Equal &&
+ androidVersionComparison == MaxSdkUtils.VersionComparisonResult.Equal &&
+ iosVersionComparison == MaxSdkUtils.VersionComparisonResult.Equal)
+ {
+ network.CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Equal;
+ }
+ // One of the installed versions is newer than the latest versions which means that the publisher is on a beta version.
+ else if (unityVersionComparison == MaxSdkUtils.VersionComparisonResult.Greater ||
+ androidVersionComparison == MaxSdkUtils.VersionComparisonResult.Greater ||
+ iosVersionComparison == MaxSdkUtils.VersionComparisonResult.Greater)
+ {
+ network.CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Greater;
+ }
+ // We have a new version available if all Android, iOS and Unity has a newer version available in db.
+ else
+ {
+ network.CurrentToLatestVersionComparisonResult = MaxSdkUtils.VersionComparisonResult.Lesser;
+ }
+ }
+ // For all other mediation adapters, get the version comparison using their Unity versions.
+ else
+ {
+ // If adapter is indeed installed, compare the current (installed) and the latest (from db) versions, so that we can determine if the publisher is on an older, current or a newer version of the adapter.
+ // If the publisher is on a newer version of the adapter than the db version, that means they are on a beta version.
+ if (MaxSdkUtils.IsValidString(currentVersions.Unity))
+ {
+ network.CurrentToLatestVersionComparisonResult = AppLovinIntegrationManagerUtils.CompareUnityMediationVersions(currentVersions.Unity, network.LatestVersions.Unity);
+ }
+
+ if (MaxSdkUtils.IsValidString(network.CurrentVersions.Unity) && AppLovinAutoUpdater.MinAdapterVersions.ContainsKey(network.Name))
+ {
+ var comparisonResult = AppLovinIntegrationManagerUtils.CompareUnityMediationVersions(network.CurrentVersions.Unity, AppLovinAutoUpdater.MinAdapterVersions[network.Name]);
+ // Requires update if current version is lower than the min required version.
+ network.RequiresUpdate = comparisonResult < 0;
+ }
+ else
+ {
+ // Reset value so that the Integration manager can hide the alert icon once adapter is updated.
+ network.RequiresUpdate = false;
+ }
+ }
+ }
+
+#if UNITY_2019_2_OR_NEWER
+ ///
+ /// Checks whether a network has duplicate adapters installed in both the Assets folder and via UPM.
+ ///
+ /// The list of paths to the dependencies.xml files
+ /// True if there are adapters in both the Assets folder and installed via UPM
+ private static bool HasDuplicateAdapters(List dependencyPaths)
+ {
+ var inPackagesFolder = dependencyPaths.Any(path => path.Contains("Packages"));
+ var inAssetsFolder = dependencyPaths.Any(path => path.Contains("Assets"));
+
+ return inPackagesFolder && inAssetsFolder;
+ }
+
+ ///
+ /// Displays a prompt informing the user that duplicate adapters were detected
+ /// and allows them to choose which version to keep.
+ ///
+ /// The network that has duplicate adapters installed.
+ private static void ShowDeleteDuplicateAdapterPrompt(Network network)
+ {
+ var keepAssetsAdapter = EditorUtility.DisplayDialog("Duplicate Adapters Detected",
+ "The " + network.DisplayName + " adapter is installed in both the Assets folder and via UPM. Please choose which version to keep.",
+ "Keep Assets Folder Version",
+ "Keep UPM Version");
+ DeleteDuplicateAdapter(network, keepAssetsAdapter);
+ }
+
+ ///
+ /// Removes a duplicate adapter by either deleting it from the Assets folder
+ /// or uninstalling it from the Unity Package Manager (UPM).
+ ///
+ /// The network for which the duplicate adapter is being removed.
+ /// If true, retains the adapter in the Assets folder and removes the UPM version;
+ /// otherwise, deletes the adapter from the Assets folder.
+ internal static void DeleteDuplicateAdapter(Network network, bool keepAssetsAdapter)
+ {
+ if (keepAssetsAdapter)
+ {
+ var appLovinManifest = AppLovinUpmManifest.Load();
+ AppLovinUpmPackageManager.RemovePackages(network, appLovinManifest);
+ appLovinManifest.Save();
+ }
+ else
+ {
+ foreach (var pluginFilePath in network.PluginFilePaths)
+ {
+ var filePath = Path.Combine(AppLovinIntegrationManager.MediationDirectory, pluginFilePath.Replace("MaxSdk/Mediation/", ""));
+ FileUtil.DeleteFileOrDirectory(filePath);
+ FileUtil.DeleteFileOrDirectory(filePath + ".meta");
+ }
+ }
+
+ AppLovinUpmPackageManager.ResolvePackageManager();
+ }
+#endif
+
+ ///
+ /// Gets the current versions for a given network's dependency file paths. UPM will have multiple paths
+ /// for each network - one each for iOS and Android.
+ ///
+ /// A list of dependency file paths to extract current versions from.
+ /// Current versions of a given network's dependency files.
+ private static Versions GetCurrentVersions(List dependencyPaths)
+ {
+ var currentVersions = new Versions();
+ foreach (var dependencyPath in dependencyPaths)
+ {
+ GetCurrentVersion(currentVersions, dependencyPath);
+ }
+
+ if (currentVersions.Android != null && currentVersions.Ios != null)
+ {
+ currentVersions.Unity = "android_" + currentVersions.Android + "_ios_" + currentVersions.Ios;
+ }
+ else if (currentVersions.Android != null)
+ {
+ currentVersions.Unity = "android_" + currentVersions.Android;
+ }
+ else if (currentVersions.Ios != null)
+ {
+ currentVersions.Unity = "ios_" + currentVersions.Ios;
+ }
+
+ return currentVersions;
+ }
+
+ ///
+ /// Extracts the current version of a network from its dependency.xml file.
+ ///
+ /// The Versions object we are using.
+ /// The path to the dependency.xml file.
+ private static void GetCurrentVersion(Versions currentVersions, string dependencyPath)
+ {
+ XDocument dependency;
+ try
+ {
+ dependency = XDocument.Load(dependencyPath);
+ }
+#pragma warning disable 0168
+ catch (IOException exception)
+#pragma warning restore 0168
+ {
+ // Couldn't find the dependencies file. The plugin is not installed.
+ return;
+ }
+
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ //
+ string androidVersion = null;
+ string iosVersion = null;
+ var dependenciesElement = dependency.Element("dependencies");
+ if (dependenciesElement != null)
+ {
+ var androidPackages = dependenciesElement.Element("androidPackages");
+ if (androidPackages != null)
+ {
+ var adapterPackage = androidPackages.Descendants().FirstOrDefault(element => element.Name.LocalName.Equals("androidPackage")
+ && element.FirstAttribute.Name.LocalName.Equals("spec")
+ && element.FirstAttribute.Value.StartsWith("com.applovin"));
+ if (adapterPackage != null)
+ {
+ androidVersion = adapterPackage.FirstAttribute.Value.Split(':').Last();
+ // Hack alert: Some Android versions might have square brackets to force a specific version. Remove them if they are detected.
+ if (androidVersion.StartsWith("["))
+ {
+ androidVersion = androidVersion.Trim('[', ']');
+ }
+ }
+ }
+
+ var iosPods = dependenciesElement.Element("iosPods");
+ if (iosPods != null)
+ {
+ var adapterPod = iosPods.Descendants().FirstOrDefault(element => element.Name.LocalName.Equals("iosPod")
+ && element.FirstAttribute.Name.LocalName.Equals("name")
+ && element.FirstAttribute.Value.StartsWith("AppLovin"));
+ if (adapterPod != null)
+ {
+ iosVersion = adapterPod.Attributes().First(attribute => attribute.Name.LocalName.Equals("version")).Value;
+ }
+ }
+ }
+
+ if (androidVersion != null)
+ {
+ currentVersions.Android = androidVersion;
+ }
+
+ if (iosVersion != null)
+ {
+ currentVersions.Ios = iosVersion;
+ }
+ }
+
+ ///
+ /// Check for the Amazon AppLovin adapter in the project.
+ ///
+ /// Whether the AppLovin Adapter is installed through the Amazon SDK.
+ private static bool IsAmazonAppLovinAdapterInstalled()
+ {
+ string[] dependenciesFiles = AssetDatabase.FindAssets("t:TextAsset Dependencies", new[] {"Assets"})
+ .Select(AssetDatabase.GUIDToAssetPath)
+ .ToArray();
+
+ // Use regex to search for Amazon and then AppLovin in the file paths of the dependencies.xml files.
+ return dependenciesFiles.Any(filePath => filePath.Contains(AppLovinMediationAmazonAdapterDependenciesPath));
+ }
+
+ ///
+ /// Refresh assets and update current versions after a slight delay to allow for Client.Resolve to finish.
+ ///
+ /// The network that was just installed/removed.
+ private static IEnumerator RefreshAssetsAtEndOfFrame(Network network)
+ {
+ yield return new WaitForEndOfFrame();
+ UpdateCurrentVersions(network);
+ AssetDatabase.Refresh();
+ }
+
+ #endregion
+ }
+
+#if UNITY_2019_2_OR_NEWER
+ public class AppLovinUpmPackageManager : IPackageManagerClient
+ {
+ public const string PackageNamePrefixAppLovin = "com.applovin.mediation.ads";
+ private const string PackageNamePrefixNetwork = "com.applovin.mediation.adapters";
+ private const string PackageNamePrefixDsp = "com.applovin.mediation.dsp";
+
+ private const float TimeoutFetchPackageCollectionSeconds = 10f;
+
+#if !UNITY_2020_1_OR_NEWER
+ private static Type packageManagerClientType;
+ private static MethodInfo packageManagerResolveMethod;
+#endif
+
+ public static List GetInstalledMediationNetworks()
+ {
+ // Return empty list if we failed to get the package list
+ var packageCollection = GetPackageCollectionSync(TimeoutFetchPackageCollectionSeconds);
+ if (packageCollection == null)
+ {
+ return new List();
+ }
+
+ return packageCollection.Where(package => package.name.StartsWith(PackageNamePrefixNetwork) || package.name.StartsWith(PackageNamePrefixDsp))
+ .SelectMany(package => package.keywords)
+ .Where(keyword => keyword.StartsWith("dir:"))
+ .Select(keyword => keyword.Replace("dir:", ""))
+ .Distinct()
+ .ToList();
+ }
+
+ public IEnumerator AddNetwork(Network network, bool showImport)
+ {
+ var appLovinManifest = AppLovinUpmManifest.Load();
+ AddPackages(network, appLovinManifest);
+ appLovinManifest.Save();
+
+ // Remove any versions of the adapter in the Assets folder
+ AppLovinPackageManager.DeleteDuplicateAdapter(network, false);
+ ResolvePackageManager();
+
+ yield break;
+ }
+
+ public void RemoveNetwork(Network network)
+ {
+ var appLovinManifest = AppLovinUpmManifest.Load();
+ RemovePackages(network, appLovinManifest);
+ appLovinManifest.Save();
+ ResolvePackageManager();
+ }
+
+ ///
+ /// Adds a network's packages to the package manager removes any beta version that exists
+ ///
+ /// The network to add.
+ /// The AppLovinUpmManifest instance to edit
+ internal static void AddPackages(Network network, AppLovinUpmManifest appLovinManifest)
+ {
+ foreach (var packageInfo in network.Packages)
+ {
+ appLovinManifest.AddPackageDependency(packageInfo.Name, packageInfo.Version);
+ RemoveBetaPackage(packageInfo.Name, appLovinManifest);
+ }
+ }
+
+ ///
+ /// Removes a network's packages from the package manager
+ ///
+ /// The network to add.
+ /// The AppLovinUpmManifest instance to edit
+ internal static void RemovePackages(Network network, AppLovinUpmManifest appLovinManifest)
+ {
+ foreach (var packageInfo in network.Packages)
+ {
+ appLovinManifest.RemovePackageDependency(packageInfo.Name);
+ RemoveBetaPackage(packageInfo.Name, appLovinManifest);
+ }
+ }
+
+ ///
+ /// Removes the beta version of a package name
+ ///
+ /// The name of the package to remove a beta for
+ /// The AppLovinUpmManifest instance to edit
+ private static void RemoveBetaPackage(string packageName, AppLovinUpmManifest appLovinManifest)
+ {
+ var prefix = "";
+ if (packageName.Contains(PackageNamePrefixNetwork))
+ {
+ prefix = PackageNamePrefixNetwork;
+ }
+ else if (packageName.Contains(PackageNamePrefixDsp))
+ {
+ prefix = PackageNamePrefixDsp;
+ }
+ else if (packageName.Contains(PackageNamePrefixAppLovin))
+ {
+ prefix = PackageNamePrefixAppLovin;
+ }
+ else
+ {
+ return;
+ }
+
+ var betaPackageName = packageName.Replace(prefix, prefix + ".beta");
+ appLovinManifest.RemovePackageDependency(betaPackageName);
+ }
+
+ ///
+ /// Resolves the Unity Package Manager so any changes made to the manifest.json file are reflected in the Unity Editor.
+ ///
+ internal static void ResolvePackageManager()
+ {
+#if UNITY_2020_1_OR_NEWER
+ Client.Resolve();
+#else
+ packageManagerClientType = packageManagerClientType ?? typeof(Client);
+ if (packageManagerClientType != null)
+ {
+ packageManagerResolveMethod = packageManagerResolveMethod ?? packageManagerClientType.GetMethod("Resolve", BindingFlags.NonPublic | BindingFlags.Static);
+ }
+
+ if (packageManagerResolveMethod != null)
+ {
+ packageManagerResolveMethod.Invoke(null, null);
+ }
+#endif
+ }
+
+ ///
+ /// Gets the PackageCollection from the Unity Package Manager synchronously.
+ ///
+ /// How long to wait before exiting with a timeout error
+ ///
+ private static PackageCollection GetPackageCollectionSync(float timeoutSeconds = -1)
+ {
+ var request = Client.List();
+
+ // Just wait till the request is complete
+ var now = DateTime.Now;
+ while (!request.IsCompleted)
+ {
+ // Wait indefinitely if there is no timeout set.
+ if (timeoutSeconds < 0) continue;
+
+ var delta = DateTime.Now - now;
+ if (delta.TotalSeconds > timeoutSeconds)
+ {
+ MaxSdkLogger.UserError("Failed to list UPM packages: Timeout");
+ break;
+ }
+ }
+
+ if (!request.IsCompleted)
+ {
+ return null;
+ }
+
+ if (request.Status >= StatusCode.Failure)
+ {
+ MaxSdkLogger.UserError("Failed to list packages: " + request.Error.message);
+ return null;
+ }
+
+ return (request.Status == StatusCode.Success) ? request.Result : null;
+ }
+ }
+
+#endif
+
+ public class AppLovinAssetsPackageManager : IPackageManagerClient
+ {
+ public static List GetInstalledMediationNetworks()
+ {
+ var maxMediationDirectory = AppLovinIntegrationManager.MediationDirectory;
+ if (!Directory.Exists(maxMediationDirectory)) return new List();
+
+ var mediationNetworkDirectories = Directory.GetDirectories(maxMediationDirectory);
+ return mediationNetworkDirectories.Select(Path.GetFileName).ToList();
+ }
+
+ public IEnumerator AddNetwork(Network network, bool showImport)
+ {
+ yield return AppLovinIntegrationManager.Instance.DownloadPlugin(network, showImport);
+ }
+
+ public void RemoveNetwork(Network network)
+ {
+ foreach (var pluginFilePath in network.PluginFilePaths)
+ {
+ var filePath = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, pluginFilePath);
+ FileUtil.DeleteFileOrDirectory(filePath);
+ FileUtil.DeleteFileOrDirectory(filePath + ".meta");
+ }
+ }
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPackageManager.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPackageManager.cs.meta
new file mode 100644
index 0000000..5a508fa
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPackageManager.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 69faa9dfd9aac483daa24261a3e11206
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPackageManager.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPluginMigrationHelper.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPluginMigrationHelper.cs
new file mode 100644
index 0000000..287b1bb
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPluginMigrationHelper.cs
@@ -0,0 +1,168 @@
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using UnityEditor;
+using UnityEngine;
+
+#if UNITY_2019_2_OR_NEWER
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ ///
+ /// Moves our SDK Unity Plugin from under the Assets folder to the Unity Package Manager.
+ ///
+ public static class AppLovinPluginMigrationHelper
+ {
+ private const string ApplovinRegistryName = "AppLovin MAX Unity";
+ private const string ApplovinRegistryUrl = "https://unity.packages.applovin.com/";
+ private static readonly List AppLovinRegistryScopes = new List() {"com.applovin.mediation.ads", "com.applovin.mediation.adapters", "com.applovin.mediation.dsp"};
+
+ private const string OpenUpmRegistryName = "package.openupm.com";
+ private const string OpenUpmRegistryUrl = "https://package.openupm.com";
+ private static readonly List OpenUpmRegistryScopes = new List() {"com.google.external-dependency-manager"};
+
+ private static List betaNetworkPluginFilePaths = new List();
+
+ ///
+ /// Attempts to move the Unity plugin to UPM by adding the AppLovin scoped registry and dependencies to the manifest.
+ ///
+ /// The Unity Plugin data for our sdk and mediation adapters.
+ /// Whether to delete the EDM folder under "Assets"
+ internal static void MigrateToUnityPackageManager(PluginData pluginData, bool deleteExternalDependencyManager)
+ {
+ MaxSdkLogger.UserDebug("Moving AppLovin Unity Plugin to package manager");
+
+ if (deleteExternalDependencyManager)
+ {
+ DeleteExternalDependencyManager();
+ }
+
+ var appLovinManifest = AppLovinUpmManifest.Load();
+
+ MigrateAdapters(pluginData, appLovinManifest);
+ MigratePlugin(pluginData, appLovinManifest);
+
+ appLovinManifest.Save();
+ AppLovinUpmPackageManager.ResolvePackageManager();
+ DeletePluginFiles();
+ }
+
+ ///
+ /// Add all currently installed networks to the manifest.
+ ///
+ internal static void MigrateAdapters(PluginData pluginData, AppLovinUpmManifest appLovinManifest)
+ {
+ var allNetworks = pluginData.MediatedNetworks.Concat(pluginData.PartnerMicroSdks).ToArray();
+ betaNetworkPluginFilePaths.Clear();
+
+ // Add every currently installed network and separate it by android and iOS.
+ foreach (var network in allNetworks)
+ {
+ var currentVersion = network.CurrentVersions != null ? network.CurrentVersions.Unity : "";
+ if (string.IsNullOrEmpty(currentVersion)) continue;
+
+ if (currentVersion.Contains("beta"))
+ {
+ betaNetworkPluginFilePaths.AddRange(network.PluginFilePaths);
+ continue;
+ }
+
+ AppLovinUpmPackageManager.AddPackages(network, appLovinManifest);
+ }
+ }
+
+ ///
+ /// Add the AppLovin scoped registry to the manifest if it doesn't exist. Otherwise update it.
+ ///
+ private static void MigratePlugin(PluginData pluginData, AppLovinUpmManifest appLovinManifest)
+ {
+ appLovinManifest.AddOrUpdateRegistry(ApplovinRegistryName, ApplovinRegistryUrl, AppLovinRegistryScopes);
+ appLovinManifest.AddOrUpdateRegistry(OpenUpmRegistryName, OpenUpmRegistryUrl, OpenUpmRegistryScopes);
+
+ var appLovinVersion = pluginData.AppLovinMax.LatestVersions.Unity;
+ appLovinManifest.AddPackageDependency(AppLovinUpmPackageManager.PackageNamePrefixAppLovin, appLovinVersion);
+ }
+
+ #region Utility
+
+ ///
+ /// Delete the external dependency manager folder from the project.
+ ///
+ private static void DeleteExternalDependencyManager()
+ {
+ var externalDependencyManagerPath = Path.Combine(Application.dataPath, "ExternalDependencyManager");
+ FileUtil.DeleteFileOrDirectory(externalDependencyManagerPath);
+ FileUtil.DeleteFileOrDirectory(externalDependencyManagerPath + ".meta");
+ }
+
+ ///
+ /// Deletes all the files in the plugin directory except the AppLovinSettings.asset file.
+ ///
+ private static void DeletePluginFiles()
+ {
+ if (AppLovinIntegrationManager.IsPluginInPackageManager) return;
+
+ var pluginPath = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, "MaxSdk");
+ var appLovinSettingsPath = Path.Combine(pluginPath, "Resources/AppLovinSettings.asset");
+
+ var appLovinResourcesDirectory = Path.Combine(pluginPath, "Resources");
+ var appLovinSettingsTempPath = Path.Combine(Path.GetTempPath(), "AppLovinSettings.asset");
+ var appLovinMediationTempPath = Path.Combine(Path.GetTempPath(), "Mediation");
+
+ // Ensure there aren't any errors when moving the files due to the directories/files already existing.
+ FileUtil.DeleteFileOrDirectory(appLovinSettingsTempPath);
+ FileUtil.DeleteFileOrDirectory(appLovinMediationTempPath);
+
+ var mediationAssetsDir = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, "MaxSdk/Mediation");
+
+ // Move the AppLovinSettings.asset file and any beta adapters to a temporary directory.
+ File.Move(appLovinSettingsPath, appLovinSettingsTempPath);
+ var adapterSaved = MoveBetaAdaptersIfNeeded(mediationAssetsDir, appLovinMediationTempPath);
+
+ // Move the meta file if the adapter was saved to save the asset labels
+ if (adapterSaved)
+ {
+ File.Move(mediationAssetsDir, appLovinMediationTempPath + ".meta");
+ }
+
+ // Delete the plugin directory and then move the AppLovinSettings.asset and beta adapters back.
+ FileUtil.DeleteFileOrDirectory(pluginPath);
+ Directory.CreateDirectory(appLovinResourcesDirectory);
+ File.Move(appLovinSettingsTempPath, appLovinSettingsPath);
+ MoveBetaAdaptersIfNeeded(appLovinMediationTempPath, mediationAssetsDir);
+ if (adapterSaved)
+ {
+ File.Move(appLovinMediationTempPath + ".meta", mediationAssetsDir);
+ }
+
+ FileUtil.DeleteFileOrDirectory(appLovinMediationTempPath);
+ }
+
+ ///
+ /// Moves the beta adapters from a source mediation directory to a destination mediation directory.
+ ///
+ /// The directory containing the beta adapters to be moved.
+ /// The target directory where the beta adapters should be moved.
+ private static bool MoveBetaAdaptersIfNeeded(string sourceMediationDirectory, string destinationMediationDirectory)
+ {
+ if (betaNetworkPluginFilePaths.Count == 0 || !Directory.Exists(sourceMediationDirectory)) return false;
+
+ var movedAdapter = false;
+ Directory.CreateDirectory(destinationMediationDirectory);
+ foreach (var pluginFilePath in betaNetworkPluginFilePaths)
+ {
+ var sourceDirectory = Path.Combine(sourceMediationDirectory, Path.GetFileName(pluginFilePath));
+ var destinationDirectory = Path.Combine(destinationMediationDirectory, Path.GetFileName(pluginFilePath));
+ if (Directory.Exists(sourceDirectory))
+ {
+ Directory.Move(sourceDirectory, destinationDirectory);
+ movedAdapter = true;
+ }
+ }
+
+ return movedAdapter;
+ }
+
+ #endregion
+ }
+}
+#endif
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPluginMigrationHelper.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPluginMigrationHelper.cs.meta
new file mode 100644
index 0000000..dc78f48
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPluginMigrationHelper.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 11288612632de49b99708cdee436692c
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPluginMigrationHelper.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroid.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroid.cs
new file mode 100644
index 0000000..08967ef
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroid.cs
@@ -0,0 +1,497 @@
+//
+// MaxPostProcessBuildAndroid.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 4/10/20.
+// Copyright © 2020 AppLovin. All rights reserved.
+//
+
+#if UNITY_ANDROID
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using System.Xml.Linq;
+using AppLovinMax.ThirdParty.MiniJson;
+using UnityEditor;
+using UnityEditor.Android;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ ///
+ /// A post processor used to update the Android project once it is generated.
+ ///
+ 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";
+
+ private const string KeyMetaDataAppLovinVerboseLoggingOn = "applovin.sdk.verbose_logging";
+ private const string KeyMetaDataGoogleApplicationId = "com.google.android.gms.ads.APPLICATION_ID";
+ private const string KeyMetaDataGoogleOptimizeInitialization = "com.google.android.gms.ads.flag.OPTIMIZE_INITIALIZATION";
+ private const string KeyMetaDataGoogleOptimizeAdLoading = "com.google.android.gms.ads.flag.OPTIMIZE_AD_LOADING";
+
+ private const string KeyMetaDataMobileFuseAutoInit = "com.mobilefuse.sdk.disable_auto_init";
+ private const string KeyMetaDataMyTargetAutoInit = "com.my.target.autoInitMode";
+
+ private const string KeyMetaDataAppLovinSdkKey = "applovin.sdk.key";
+
+ private const string AppLovinSettingsFileName = "applovin_settings.json";
+
+ private const string KeySdkKey = "sdk_key";
+ private const string KeyConsentFlowSettings = "consent_flow_settings";
+ private const string KeyConsentFlowEnabled = "consent_flow_enabled";
+ private const string KeyConsentFlowTermsOfService = "consent_flow_terms_of_service";
+ private const string KeyConsentFlowPrivacyPolicy = "consent_flow_privacy_policy";
+ private const string KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR = "consent_flow_show_terms_and_privacy_policy_alert_in_gdpr";
+ private const string KeyConsentFlowDebugUserGeography = "consent_flow_debug_user_geography";
+
+ private const string KeyRenderOutsideSafeArea = "render_outside_safe_area";
+
+#if UNITY_2022_3_OR_NEWER
+ // To match "'com.android.library' version '7.3.1'" line in build.gradle
+ private static readonly Regex TokenGradleVersionLibrary = new Regex(".*id ['\"]com\\.android\\.library['\"] version");
+ private static readonly Regex TokenGradleVersion = new Regex(".*id ['\"]com\\.android\\.application['\"] version");
+#else
+ // To match "classpath 'com.android.tools.build:gradle:4.0.1'" line in build.gradle
+ private static readonly Regex TokenGradleVersion = new Regex(".*classpath ['\"]com\\.android\\.tools\\.build:gradle:.*");
+#endif
+
+ // To match "distributionUrl=..." in gradle-wrapper.properties file
+ private static readonly Regex TokenDistributionUrl = new Regex(".*distributionUrl.*");
+
+ private static readonly XNamespace AndroidNamespace = "http://schemas.android.com/apk/res/android";
+
+ 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);
+
+ var gradlePropertiesUpdated = new List();
+
+ // If the gradle properties file already exists, make sure to add any previous properties.
+ if (File.Exists(gradlePropertiesPath))
+ {
+ 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
+ // Disable dexing using artifact transform (it causes issues for ExoPlayer with Gradle plugin 3.5.0+)
+ gradlePropertiesUpdated.Add(PropertyDexingArtifactTransform + DisableProperty);
+#endif
+
+ try
+ {
+ File.WriteAllText(gradlePropertiesPath, string.Join("\n", gradlePropertiesUpdated.ToArray()) + "\n");
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to enable AndroidX and Jetifier. gradle.properties file write failed.");
+ Console.WriteLine(exception);
+ }
+
+ ProcessAndroidManifest(path);
+ AddSdkSettings(path);
+ }
+
+ public int callbackOrder
+ {
+ get { return AppLovinPreProcess.CallbackOrder; }
+ }
+
+ private static void ProcessAndroidManifest(string path)
+ {
+ var manifestPath = Path.Combine(path, "src/main/AndroidManifest.xml");
+ XDocument manifest;
+ try
+ {
+ manifest = XDocument.Load(manifestPath);
+ }
+#pragma warning disable 0168
+ catch (IOException exception)
+#pragma warning restore 0168
+ {
+ MaxSdkLogger.UserWarning("[AppLovin MAX] AndroidManifest.xml is missing.");
+ return;
+ }
+
+ // Get the `manifest` element.
+ var elementManifest = manifest.Element("manifest");
+ if (elementManifest == null)
+ {
+ MaxSdkLogger.UserWarning("[AppLovin MAX] AndroidManifest.xml is invalid.");
+ return;
+ }
+
+ var elementApplication = elementManifest.Element("application");
+ if (elementApplication == null)
+ {
+ MaxSdkLogger.UserWarning("[AppLovin MAX] AndroidManifest.xml is invalid.");
+ return;
+ }
+
+ var metaDataElements = elementApplication.Descendants().Where(element => element.Name.LocalName.Equals("meta-data"));
+
+ EnableVerboseLoggingIfNeeded(elementApplication);
+ AddGoogleApplicationIdIfNeeded(elementApplication, metaDataElements);
+ AddGoogleOptimizationFlagsIfNeeded(elementApplication, metaDataElements);
+ DisableAutoInitIfNeeded(elementApplication, metaDataElements);
+ RemoveSdkKeyIfNeeded(metaDataElements);
+
+ // Save the updated manifest file.
+ manifest.Save(manifestPath);
+ }
+
+ private static void EnableVerboseLoggingIfNeeded(XElement elementApplication)
+ {
+ var enabled = EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled, false);
+
+ var descendants = elementApplication.Descendants();
+ var verboseLoggingMetaData = descendants.FirstOrDefault(descendant => descendant.FirstAttribute != null &&
+ descendant.FirstAttribute.Name.LocalName.Equals("name") &&
+ descendant.FirstAttribute.Value.Equals(KeyMetaDataAppLovinVerboseLoggingOn) &&
+ descendant.LastAttribute != null &&
+ descendant.LastAttribute.Name.LocalName.Equals("value"));
+
+ // check if applovin.sdk.verbose_logging meta data exists.
+ if (verboseLoggingMetaData != null)
+ {
+ if (enabled)
+ {
+ // update applovin.sdk.verbose_logging meta data value.
+ verboseLoggingMetaData.LastAttribute.Value = enabled.ToString();
+ }
+ else
+ {
+ // remove applovin.sdk.verbose_logging meta data.
+ verboseLoggingMetaData.Remove();
+ }
+ }
+ else
+ {
+ if (enabled)
+ {
+ // add applovin.sdk.verbose_logging meta data if it does not exist.
+ var metaData = CreateMetaDataElement(KeyMetaDataAppLovinVerboseLoggingOn, enabled.ToString());
+ elementApplication.Add(metaData);
+ }
+ }
+ }
+
+ private static void AddGoogleApplicationIdIfNeeded(XElement elementApplication, IEnumerable metaDataElements)
+ {
+ if (!AppLovinPackageManager.IsAdapterInstalled("Google") && !AppLovinPackageManager.IsAdapterInstalled("GoogleAdManager")) return;
+
+ var googleApplicationIdMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataGoogleApplicationId);
+ var appId = AppLovinSettings.Instance.AdMobAndroidAppId;
+ // Log error if the App ID is not set.
+ if (string.IsNullOrEmpty(appId) || !appId.StartsWith("ca-app-pub-"))
+ {
+ MaxSdkLogger.UserError("Google App ID is not set. Please enter a valid app ID within the AppLovin Integration Manager window.");
+ return;
+ }
+
+ // Check if the Google App ID meta data already exists. Update if it already exists.
+ if (googleApplicationIdMetaData != null)
+ {
+ googleApplicationIdMetaData.SetAttributeValue(AndroidNamespace + "value", appId);
+ }
+ // Meta data doesn't exist, add it.
+ else
+ {
+ elementApplication.Add(CreateMetaDataElement(KeyMetaDataGoogleApplicationId, appId));
+ }
+ }
+
+ private static void AddGoogleOptimizationFlagsIfNeeded(XElement elementApplication, IEnumerable metaDataElements)
+ {
+ if (!AppLovinPackageManager.IsAdapterInstalled("Google") && !AppLovinPackageManager.IsAdapterInstalled("GoogleAdManager")) return;
+
+ var googleOptimizeInitializationMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataGoogleOptimizeInitialization);
+ // If meta data doesn't exist, add it
+ if (googleOptimizeInitializationMetaData == null)
+ {
+ elementApplication.Add(CreateMetaDataElement(KeyMetaDataGoogleOptimizeInitialization, true));
+ }
+
+ var googleOptimizeAdLoadingMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataGoogleOptimizeAdLoading);
+ // If meta data doesn't exist, add it
+ if (googleOptimizeAdLoadingMetaData == null)
+ {
+ elementApplication.Add(CreateMetaDataElement(KeyMetaDataGoogleOptimizeAdLoading, true));
+ }
+ }
+
+ private static void DisableAutoInitIfNeeded(XElement elementApplication, IEnumerable metaDataElements)
+ {
+ if (AppLovinPackageManager.IsAdapterInstalled("MobileFuse"))
+ {
+ var mobileFuseMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataMobileFuseAutoInit);
+ // If MobileFuse meta data doesn't exist, add it
+ if (mobileFuseMetaData == null)
+ {
+ elementApplication.Add(CreateMetaDataElement(KeyMetaDataMobileFuseAutoInit, true));
+ }
+ }
+
+ if (AppLovinPackageManager.IsAdapterInstalled("MyTarget"))
+ {
+ var myTargetMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataMyTargetAutoInit);
+ // If MyTarget meta data doesn't exist, add it
+ if (myTargetMetaData == null)
+ {
+ elementApplication.Add(CreateMetaDataElement(KeyMetaDataMyTargetAutoInit, 0));
+ }
+ }
+ }
+
+ private static void RemoveSdkKeyIfNeeded(IEnumerable metaDataElements)
+ {
+ var sdkKeyMetaData = GetMetaDataElement(metaDataElements, KeyMetaDataAppLovinSdkKey);
+ if (sdkKeyMetaData == null) return;
+
+ sdkKeyMetaData.Remove();
+ }
+
+ private static void UpdateGradleVersionsIfNeeded(string gradleWrapperPropertiesPath, string rootGradleBuildFilePath)
+ {
+ var customGradleVersionUrl = AppLovinSettings.Instance.CustomGradleVersionUrl;
+ var customGradleToolsVersion = AppLovinSettings.Instance.CustomGradleToolsVersion;
+
+ if (MaxSdkUtils.IsValidString(customGradleVersionUrl))
+ {
+ var newDistributionUrl = string.Format("distributionUrl={0}", customGradleVersionUrl);
+ if (ReplaceStringInFile(gradleWrapperPropertiesPath, TokenDistributionUrl, newDistributionUrl))
+ {
+ MaxSdkLogger.D("Distribution url set to " + newDistributionUrl);
+ }
+ else
+ {
+ MaxSdkLogger.E("Failed to set distribution URL");
+ }
+ }
+
+ if (MaxSdkUtils.IsValidString(customGradleToolsVersion))
+ {
+#if UNITY_2022_3_OR_NEWER
+ // Unity 2022.3+ requires Gradle Plugin version 7.1.2+.
+ if (MaxSdkUtils.CompareVersions(customGradleToolsVersion, "7.1.2") == MaxSdkUtils.VersionComparisonResult.Lesser)
+ {
+ MaxSdkLogger.E("Failed to set gradle plugin version. Unity 2022.3+ requires gradle plugin version 7.1.2+");
+ return;
+ }
+
+ var newGradleVersionLibraryLine = AppLovinProcessGradleBuildFile.GetFormattedBuildScriptLine(string.Format("id 'com.android.library' version '{0}' apply false", customGradleToolsVersion));
+ if (ReplaceStringInFile(rootGradleBuildFilePath, TokenGradleVersionLibrary, newGradleVersionLibraryLine))
+ {
+ MaxSdkLogger.D("Gradle library version set to " + newGradleVersionLibraryLine);
+ }
+ else
+ {
+ MaxSdkLogger.E("Failed to set gradle library version");
+ }
+
+ var newGradleVersionLine = AppLovinProcessGradleBuildFile.GetFormattedBuildScriptLine(string.Format("id 'com.android.application' version '{0}' apply false", customGradleToolsVersion));
+#else
+ var newGradleVersionLine = AppLovinProcessGradleBuildFile.GetFormattedBuildScriptLine(string.Format("classpath 'com.android.tools.build:gradle:{0}'", customGradleToolsVersion));
+#endif
+ if (ReplaceStringInFile(rootGradleBuildFilePath, TokenGradleVersion, newGradleVersionLine))
+ {
+ MaxSdkLogger.D("Gradle version set to " + newGradleVersionLine);
+ }
+ else
+ {
+ MaxSdkLogger.E("Failed to set gradle plugin version");
+ }
+ }
+ }
+
+ private static void AddSdkSettings(string path)
+ {
+ var appLovinSdkSettings = new Dictionary();
+ var rawResourceDirectory = Path.Combine(path, "src/main/res/raw");
+
+ // Add the SDK key to the SDK settings.
+ appLovinSdkSettings[KeySdkKey] = AppLovinSettings.Instance.SdkKey;
+ appLovinSdkSettings[KeyRenderOutsideSafeArea] = PlayerSettings.Android.renderOutsideSafeArea;
+
+ // Add the Terms and Privacy Policy flow settings if needed.
+ EnableConsentFlowIfNeeded(rawResourceDirectory, appLovinSdkSettings);
+
+ WriteAppLovinSettings(rawResourceDirectory, appLovinSdkSettings);
+ }
+
+ private static void EnableConsentFlowIfNeeded(string rawResourceDirectory, Dictionary applovinSdkSettings)
+ {
+ // Check if consent flow is enabled. No need to create the applovin_consent_flow_settings.json if consent flow is disabled.
+ var consentFlowEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
+ if (!consentFlowEnabled)
+ {
+ RemoveAppLovinSettingsRawResourceFileIfNeeded(rawResourceDirectory);
+ return;
+ }
+
+ var privacyPolicyUrl = AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl;
+ if (string.IsNullOrEmpty(privacyPolicyUrl))
+ {
+ AppLovinIntegrationManager.ShowBuildFailureDialog("You cannot use the AppLovin SDK's consent flow without defining a Privacy Policy URL in the AppLovin Integration Manager.");
+
+ // No need to update the applovin_consent_flow_settings.json here. Default consent flow state will be determined on the SDK side.
+ return;
+ }
+
+ var consentFlowSettings = new Dictionary();
+ consentFlowSettings[KeyConsentFlowEnabled] = consentFlowEnabled;
+ consentFlowSettings[KeyConsentFlowPrivacyPolicy] = privacyPolicyUrl;
+
+ var termsOfServiceUrl = AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl;
+ if (MaxSdkUtils.IsValidString(termsOfServiceUrl))
+ {
+ consentFlowSettings[KeyConsentFlowTermsOfService] = termsOfServiceUrl;
+ }
+
+ consentFlowSettings[KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR] = AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR;
+
+ var debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
+ if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
+ {
+ consentFlowSettings[KeyConsentFlowDebugUserGeography] = "gdpr";
+ }
+
+ applovinSdkSettings[KeyConsentFlowSettings] = consentFlowSettings;
+ }
+
+ private static void WriteAppLovinSettingsRawResourceFile(string applovinSdkSettingsJson, string rawResourceDirectory)
+ {
+ if (!Directory.Exists(rawResourceDirectory))
+ {
+ Directory.CreateDirectory(rawResourceDirectory);
+ }
+
+ var consentFlowSettingsFilePath = Path.Combine(rawResourceDirectory, AppLovinSettingsFileName);
+ try
+ {
+ File.WriteAllText(consentFlowSettingsFilePath, applovinSdkSettingsJson + "\n");
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("applovin_settings.json file write failed due to: " + exception.Message);
+ Console.WriteLine(exception);
+ }
+ }
+
+ ///
+ /// Removes the applovin_settings json file from the build if it exists.
+ ///
+ /// The raw resource directory that holds the json file
+ private static void RemoveAppLovinSettingsRawResourceFileIfNeeded(string rawResourceDirectory)
+ {
+ var consentFlowSettingsFilePath = Path.Combine(rawResourceDirectory, AppLovinSettingsFileName);
+ if (!File.Exists(consentFlowSettingsFilePath)) return;
+
+ try
+ {
+ File.Delete(consentFlowSettingsFilePath);
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Deleting applovin_settings.json failed due to: " + exception.Message);
+ Console.WriteLine(exception);
+ }
+ }
+
+ private static void WriteAppLovinSettings(string rawResourceDirectory, Dictionary applovinSdkSettings)
+ {
+ var applovinSdkSettingsJson = Json.Serialize(applovinSdkSettings);
+ WriteAppLovinSettingsRawResourceFile(applovinSdkSettingsJson, rawResourceDirectory);
+ }
+
+ ///
+ /// Creates and returns a meta-data element with the given name and value.
+ ///
+ private static XElement CreateMetaDataElement(string name, object value)
+ {
+ var metaData = new XElement("meta-data");
+ metaData.Add(new XAttribute(AndroidNamespace + "name", name));
+ metaData.Add(new XAttribute(AndroidNamespace + "value", value));
+
+ return metaData;
+ }
+
+ ///
+ /// Looks through all the given meta-data elements to check if the required one exists. Returns null if it doesn't exist.
+ ///
+ private static XElement GetMetaDataElement(IEnumerable metaDataElements, string metaDataName)
+ {
+ foreach (var metaDataElement in metaDataElements)
+ {
+ var attributes = metaDataElement.Attributes();
+ if (attributes.Any(attribute => attribute.Name.Namespace.Equals(AndroidNamespace)
+ && attribute.Name.LocalName.Equals("name")
+ && attribute.Value.Equals(metaDataName)))
+ {
+ return metaDataElement;
+ }
+ }
+
+ return null;
+ }
+
+ ///
+ /// Finds the first line that contains regexToMatch and replaces the whole line with replacement
+ ///
+ /// Path to the file you want to replace a line in
+ /// Regex to search for in the line you want to replace
+ /// String that you want as the new line
+ /// Returns whether the string was successfully replaced or not
+ private static bool ReplaceStringInFile(string path, Regex regexToMatch, string replacement)
+ {
+ if (!File.Exists(path)) return false;
+
+ var lines = File.ReadAllLines(path);
+ for (var i = 0; i < lines.Length; i++)
+ {
+ if (regexToMatch.IsMatch(lines[i]))
+ {
+ lines[i] = replacement;
+ File.WriteAllLines(path, lines);
+ return true;
+ }
+ }
+
+ return false;
+ }
+ }
+}
+
+#endif
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroid.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroid.cs.meta
new file mode 100644
index 0000000..2877140
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroid.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: c01ea79d0cb2a43c093e2fd07201df9e
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroid.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroidGradle.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroidGradle.cs
new file mode 100644
index 0000000..eaf241f
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroidGradle.cs
@@ -0,0 +1,78 @@
+//
+// AppLovinBuildPostProcessor.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 8/29/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+#if UNITY_ANDROID
+
+using System.IO;
+using UnityEditor.Android;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ ///
+ /// Adds Quality Service plugin to the Gradle project once the project has been exported. See for more details.
+ ///
+ public class AppLovinPostProcessGradleProject : AppLovinProcessGradleBuildFile, IPostGenerateGradleAndroidProject
+ {
+ public void OnPostGenerateGradleAndroidProject(string path)
+ {
+ 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);
+ }
+
+ if (failedToAddPlugin)
+ {
+ MaxSdkLogger.UserWarning("Failed to add AppLovin Quality Service plugin to the gradle project.");
+ return;
+ }
+
+ // 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))
+ {
+ MaxSdkLogger.UserWarning("Couldn't find build.gradle file. Failed to add AppLovin Quality Service plugin to the gradle project.");
+ return;
+ }
+
+ AddAppLovinQualityServicePlugin(applicationGradleBuildFilePath);
+ }
+
+ public int callbackOrder
+ {
+ get { return CallbackOrder; }
+ }
+ }
+}
+
+#endif
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroidGradle.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroidGradle.cs.meta
new file mode 100644
index 0000000..c167f0c
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroidGradle.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: f75e54e2eb78f427ca8643c97684387b
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessAndroidGradle.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessiOS.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessiOS.cs
new file mode 100644
index 0000000..3b71a29
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessiOS.cs
@@ -0,0 +1,731 @@
+//
+// MaxIntegrationManager.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 8/29/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+#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 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;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ [Serializable]
+ public class SkAdNetworkData
+ {
+ [SerializeField] public string[] SkAdNetworkIds;
+ }
+
+ public class AppLovinPostProcessiOS
+ {
+ 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;
+
+ private const string TargetUnityIphonePodfileLine = "target 'Unity-iPhone' do";
+ private const string UseFrameworksPodfileLine = "use_frameworks!";
+ private const string UseFrameworksDynamicPodfileLine = "use_frameworks! :linkage => :dynamic";
+ private const string UseFrameworksStaticPodfileLine = "use_frameworks! :linkage => :static";
+
+ private const string ResourcesDirectoryName = "Resources";
+ private const string AppLovinMaxResourcesDirectoryName = "AppLovinMAXResources";
+ private const string AppLovinAdvertisingAttributionEndpoint = "https://postbacks-app.com";
+
+ private const string AppLovinSettingsPlistFileName = "AppLovin-Settings.plist";
+
+ private const string KeySdkKey = "SdkKey";
+
+ private const string AppLovinVerboseLoggingOnKey = "AppLovinVerboseLoggingOn";
+
+ private const string KeyConsentFlowInfo = "ConsentFlowInfo";
+ private const string KeyConsentFlowEnabled = "ConsentFlowEnabled";
+ private const string KeyConsentFlowTermsOfService = "ConsentFlowTermsOfService";
+ private const string KeyConsentFlowPrivacyPolicy = "ConsentFlowPrivacyPolicy";
+ private const string KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR = "ConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR";
+ private const string KeyConsentFlowDebugUserGeography = "ConsentFlowDebugUserGeography";
+
+ private const string KeyAppLovinSdkKeyToRemove = "AppLovinSdkKey";
+
+ private static readonly Regex PodfilePodLineRegex = new Regex("pod \'([^\']*)\'");
+
+ ///
+ /// Adds AppLovin Quality Service to the iOS project once the project has been exported.
+ ///
+ /// 1. Downloads the Quality Service ruby script.
+ /// 2. Runs the script using Ruby which integrates AppLovin Quality Service to the project.
+ ///
+ [PostProcessBuild(AppLovinPreProcess.CallbackOrder)] // We want to run Quality Service script last.
+ public static void OnPostProcessBuild(BuildTarget buildTarget, string buildPath)
+ {
+ if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
+
+ var sdkKey = AppLovinSettings.Instance.SdkKey;
+ if (string.IsNullOrEmpty(sdkKey))
+ {
+ MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. SDK Key is empty. Please enter the AppLovin SDK Key in the Integration Manager.");
+ return;
+ }
+
+ var outputFilePath = Path.Combine(buildPath, OutputFileName);
+
+ // Check if Quality Service is already installed.
+ if (File.Exists(outputFilePath) && Directory.Exists(Path.Combine(buildPath, "AppLovinQualityService")))
+ {
+ // TODO: Check if there is a way to validate if the SDK key matches the script. Else the pub can't use append when/if they change the SDK Key.
+ 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"))
+ {
+ unityWebRequest.method = UnityWebRequest.kHttpVerbPOST;
+ unityWebRequest.downloadHandler = downloadHandler;
+ unityWebRequest.uploadHandler = uploadHandler;
+ var operation = unityWebRequest.SendWebRequest();
+
+ // Wait for the download to complete or the request to timeout.
+ while (!operation.isDone) { }
+
+#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;
+ }
+
+ // 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)]
+ public static void MaxPostProcessPbxProject(BuildTarget buildTarget, string buildPath)
+ {
+ var projectPath = PBXProject.GetPBXProjectPath(buildPath);
+ 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);
+ LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn, "en", buildPath, project, unityMainTargetGuid);
+ LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEs, "es", buildPath, project, unityMainTargetGuid);
+ LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionFr, "fr", buildPath, project, unityMainTargetGuid);
+ LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionJa, "ja", buildPath, project, unityMainTargetGuid);
+ LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionKo, "ko", buildPath, project, unityMainTargetGuid);
+ LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHans, "zh-Hans", buildPath, project, unityMainTargetGuid);
+ LocalizeUserTrackingDescriptionIfNeeded(AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionZhHant, "zh-Hant", buildPath, project, unityMainTargetGuid);
+
+ AddSwiftSupport(buildPath, project, unityFrameworkTargetGuid, unityMainTargetGuid);
+ AddYandexSettingsIfNeeded(project, unityMainTargetGuid);
+
+ project.WriteToFile(projectPath);
+ }
+
+ private static void EmbedDynamicLibrariesIfNeeded(string buildPath, PBXProject project, string targetGuid)
+ {
+ // Check that the Pods directory exists (it might not if a publisher is building with Generate Podfile setting disabled in EDM).
+ var podsDirectory = Path.Combine(buildPath, "Pods");
+ if (!Directory.Exists(podsDirectory) || !ShouldEmbedDynamicLibraries(buildPath)) return;
+
+ 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
+ }
+
+ ///
+ /// |-----------------------------------------------------------------------------------------------------------------------------------------------------|
+ /// | embed | use_frameworks! (:linkage => :dynamic) | use_frameworks! :linkage => :static | `use_frameworks!` line not present |
+ /// |---------------------------|------------------------------------------|---------------------------------------|--------------------------------------|
+ /// | Unity-iPhone present | Do not embed dynamic libraries | Embed dynamic libraries | Do not embed dynamic libraries |
+ /// | Unity-iPhone not present | Embed dynamic libraries | Embed dynamic libraries | Embed dynamic libraries |
+ /// |-----------------------------------------------------------------------------------------------------------------------------------------------------|
+ ///
+ /// An iOS build path
+ /// Whether or not the dynamic libraries should be embedded.
+ private static bool ShouldEmbedDynamicLibraries(string buildPath)
+ {
+ var podfilePath = Path.Combine(buildPath, "Podfile");
+ if (!File.Exists(podfilePath)) return false;
+
+ // If the Podfile doesn't have a `Unity-iPhone` target, we should embed the dynamic libraries.
+ var lines = File.ReadAllLines(podfilePath);
+ var containsUnityIphoneTarget = lines.Any(line => line.Contains(TargetUnityIphonePodfileLine));
+ if (!containsUnityIphoneTarget) return true;
+
+ // If the Podfile does not have a `use_frameworks! :linkage => static` line, we should not embed the dynamic libraries.
+ var useFrameworksStaticLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksStaticPodfileLine));
+ if (useFrameworksStaticLineIndex == -1) return false;
+
+ // If more than one of the `use_frameworks!` lines are present, CocoaPods will use the last one.
+ var useFrameworksLineIndex = Array.FindIndex(lines, line => line.Trim() == UseFrameworksPodfileLine); // Check for exact line to avoid matching `use_frameworks! :linkage => static/dynamic`
+ var useFrameworksDynamicLineIndex = Array.FindIndex(lines, line => line.Contains(UseFrameworksDynamicPodfileLine));
+
+ // Check if `use_frameworks! :linkage => :static` is the last line of the three. If it is, we should embed the dynamic libraries.
+ return useFrameworksLineIndex < useFrameworksStaticLineIndex && useFrameworksDynamicLineIndex < useFrameworksStaticLineIndex;
+ }
+
+ private static List GetDynamicLibraryPathsToEmbed(string podsDirectory, string buildPath)
+ {
+ var podfilePath = Path.Combine(buildPath, "Podfile");
+ var dynamicLibraryFrameworksToEmbed = GetDynamicLibraryFrameworksToEmbed(podfilePath);
+
+ return GetDynamicLibraryPathsInProjectToEmbed(podsDirectory, dynamicLibraryFrameworksToEmbed);
+ }
+
+ private static List GetDynamicLibraryFrameworksToEmbed(string podfilePath)
+ {
+ var dynamicLibrariesToEmbed = GetDynamicLibrariesToEmbed();
+
+ var podsInUnityIphoneTarget = GetPodNamesInUnityIphoneTarget(podfilePath);
+ var dynamicLibrariesToIgnore = dynamicLibrariesToEmbed.Where(dynamicLibraryToEmbed => podsInUnityIphoneTarget.Contains(dynamicLibraryToEmbed.PodName)).ToList();
+
+ // Determine frameworks to embed based on the dynamic libraries to embed and ignore
+ var dynamicLibraryFrameworksToIgnore = dynamicLibrariesToIgnore.SelectMany(library => library.FrameworkNames).Distinct().ToList();
+ return dynamicLibrariesToEmbed.SelectMany(library => library.FrameworkNames).Except(dynamicLibraryFrameworksToIgnore).Distinct().ToList();
+ }
+
+ private static List GetDynamicLibrariesToEmbed()
+ {
+ var pluginData = AppLovinIntegrationManager.LoadPluginDataSync();
+ if (pluginData == null)
+ {
+ MaxSdkLogger.E("Failed to load plugin data. Dynamic libraries will not be embedded.");
+ return null;
+ }
+
+ // Get the dynamic libraries to embed for each network
+ var librariesToAdd = pluginData.MediatedNetworks
+ .Where(network => network.DynamicLibrariesToEmbed != null)
+ .SelectMany(network => network.DynamicLibrariesToEmbed
+ .Where(libraryToEmbed => IsRequiredNetworkVersionInstalled(libraryToEmbed, network)))
+ .ToList();
+
+ // Get the dynamic libraries to embed for AppLovin MAX
+ if (pluginData.AppLovinMax.DynamicLibrariesToEmbed != null)
+ {
+ librariesToAdd.AddRange(pluginData.AppLovinMax.DynamicLibrariesToEmbed);
+ }
+
+ // Get the dynamic libraries to embed for third parties
+ if (pluginData.ThirdPartyDynamicLibrariesToEmbed != null)
+ {
+ // TODO: Add version check for third party dynamic libraries.
+ librariesToAdd.AddRange(pluginData.ThirdPartyDynamicLibrariesToEmbed);
+ }
+
+ return librariesToAdd;
+ }
+
+ private static List GetPodNamesInUnityIphoneTarget(string podfilePath)
+ {
+ var lines = File.ReadAllLines(podfilePath);
+ var podNamesInUnityIphone = new List();
+
+ var insideUnityIphoneTarget = false;
+ foreach (var line in lines)
+ {
+ // Loop until we find the `target 'Unity-iPhone'` line
+ if (insideUnityIphoneTarget)
+ {
+ if (line.Trim() == "end") break;
+
+ if (PodfilePodLineRegex.IsMatch(line))
+ {
+ var podName = PodfilePodLineRegex.Match(line).Groups[1].Value;
+ podNamesInUnityIphone.Add(podName);
+ }
+ }
+ else if (line.Contains(TargetUnityIphonePodfileLine))
+ {
+ insideUnityIphoneTarget = true;
+ }
+ }
+
+ return podNamesInUnityIphone;
+ }
+
+ private static bool IsRequiredNetworkVersionInstalled(DynamicLibraryToEmbed libraryToEmbed, Network network)
+ {
+ 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;
+ }
+
+ private static List GetDynamicLibraryPathsInProjectToEmbed(string podsDirectory, List dynamicLibrariesToEmbed)
+ {
+ var dynamicLibraryPathsPresentInProject = new List();
+ foreach (var dynamicLibraryToSearch in dynamicLibrariesToEmbed)
+ {
+ // both .framework and .xcframework are directories, not files
+ var directories = Directory.GetDirectories(podsDirectory, dynamicLibraryToSearch, SearchOption.AllDirectories);
+ if (directories.Length <= 0) continue;
+
+ var dynamicLibraryAbsolutePath = directories[0];
+ var relativePath = GetDynamicLibraryRelativePath(dynamicLibraryAbsolutePath);
+ dynamicLibraryPathsPresentInProject.Add(relativePath);
+ }
+
+ return dynamicLibraryPathsPresentInProject;
+ }
+
+ private static string GetDynamicLibraryRelativePath(string dynamicLibraryAbsolutePath)
+ {
+ var index = dynamicLibraryAbsolutePath.LastIndexOf("Pods", StringComparison.Ordinal);
+ return dynamicLibraryAbsolutePath.Substring(index);
+ }
+
+ private static void LocalizeUserTrackingDescriptionIfNeeded(string localizedUserTrackingDescription, string localeCode, string buildPath, PBXProject project, string targetGuid)
+ {
+ var resourcesDirectoryPath = Path.Combine(buildPath, AppLovinMaxResourcesDirectoryName);
+ var localeSpecificDirectoryName = localeCode + ".lproj";
+ var localeSpecificDirectoryPath = Path.Combine(resourcesDirectoryPath, localeSpecificDirectoryName);
+ var infoPlistStringsFilePath = Path.Combine(localeSpecificDirectoryPath, "InfoPlist.strings");
+
+ // Check if localization has been disabled between builds, and remove them as needed.
+ if (ShouldRemoveLocalization(localizedUserTrackingDescription))
+ {
+ if (!File.Exists(infoPlistStringsFilePath)) return;
+
+ File.Delete(infoPlistStringsFilePath);
+ return;
+ }
+
+ // Log an error if we detect a localization file for this language in the `Resources` directory
+ var legacyResourcedDirectoryPath = Path.Combine(buildPath, ResourcesDirectoryName);
+ var localeSpecificLegacyDirectoryPath = Path.Combine(legacyResourcedDirectoryPath, localeSpecificDirectoryName);
+ if (Directory.Exists(localeSpecificLegacyDirectoryPath))
+ {
+ MaxSdkLogger.UserError("Detected existing localization resource for \"" + localeCode + "\" locale. Skipping localization for User Tracking Usage Description. Please disable localization in AppLovin Integration manager and add the localizations to your existing resource.");
+ return;
+ }
+
+ // Create intermediate directories as needed.
+ if (!Directory.Exists(resourcesDirectoryPath))
+ {
+ Directory.CreateDirectory(resourcesDirectoryPath);
+ }
+
+ if (!Directory.Exists(localeSpecificDirectoryPath))
+ {
+ Directory.CreateDirectory(localeSpecificDirectoryPath);
+ }
+
+ var localizedDescriptionLine = "\"NSUserTrackingUsageDescription\" = \"" + localizedUserTrackingDescription + "\";\n";
+ // File already exists, update it in case the value changed between builds.
+ if (File.Exists(infoPlistStringsFilePath))
+ {
+ var output = new List();
+ var lines = File.ReadAllLines(infoPlistStringsFilePath);
+ var keyUpdated = false;
+ foreach (var line in lines)
+ {
+ if (line.Contains("NSUserTrackingUsageDescription"))
+ {
+ output.Add(localizedDescriptionLine);
+ keyUpdated = true;
+ }
+ else
+ {
+ output.Add(line);
+ }
+ }
+
+ if (!keyUpdated)
+ {
+ output.Add(localizedDescriptionLine);
+ }
+
+ File.WriteAllText(infoPlistStringsFilePath, string.Join("\n", output.ToArray()) + "\n");
+ }
+ // File doesn't exist, create one.
+ else
+ {
+ File.WriteAllText(infoPlistStringsFilePath, "/* Localized versions of Info.plist keys - Generated by AL MAX plugin */\n" + localizedDescriptionLine);
+ }
+
+ var localeSpecificDirectoryRelativePath = Path.Combine(AppLovinMaxResourcesDirectoryName, localeSpecificDirectoryName);
+ var guid = project.AddFolderReference(localeSpecificDirectoryRelativePath, localeSpecificDirectoryRelativePath);
+ project.AddFileToBuild(targetGuid, guid);
+ }
+
+ private static bool ShouldRemoveLocalization(string localizedUserTrackingDescription)
+ {
+ if (string.IsNullOrEmpty(localizedUserTrackingDescription)) return true;
+
+ var internalSettings = AppLovinInternalSettings.Instance;
+ return !internalSettings.ConsentFlowEnabled || !internalSettings.UserTrackingUsageLocalizationEnabled;
+ }
+
+ private static void AddSwiftSupport(string buildPath, PBXProject project, string unityFrameworkTargetGuid, string unityMainTargetGuid)
+ {
+ var swiftFileRelativePath = "Classes/MAXSwiftSupport.swift";
+ var swiftFilePath = Path.Combine(buildPath, swiftFileRelativePath);
+
+ // Add Swift file
+ CreateSwiftFile(swiftFilePath);
+ var swiftFileGuid = project.AddFile(swiftFileRelativePath, swiftFileRelativePath);
+ project.AddFileToBuild(unityFrameworkTargetGuid, swiftFileGuid);
+
+ // Add Swift version property if needed
+ var swiftVersion = project.GetBuildPropertyForAnyConfig(unityFrameworkTargetGuid, "SWIFT_VERSION");
+ if (string.IsNullOrEmpty(swiftVersion))
+ {
+ project.SetBuildProperty(unityFrameworkTargetGuid, "SWIFT_VERSION", "5.0");
+ }
+
+ // Some publishers may configure these settings in their own post-processing scripts.
+ // Only set them if they haven't already been defined to avoid overwriting publisher-defined values.
+ var enableModules = project.GetBuildPropertyForAnyConfig(unityFrameworkTargetGuid, "CLANG_ENABLE_MODULES");
+ if (string.IsNullOrEmpty(enableModules))
+ {
+ project.SetBuildProperty(unityFrameworkTargetGuid, "CLANG_ENABLE_MODULES", "YES");
+ }
+
+ var alwaysEmbedSwiftLibraries = project.GetBuildPropertyForAnyConfig(unityMainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES");
+ if (string.IsNullOrEmpty(alwaysEmbedSwiftLibraries))
+ {
+ project.SetBuildProperty(unityMainTargetGuid, "ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES", "YES");
+ }
+ }
+
+ private static void CreateSwiftFile(string swiftFilePath)
+ {
+ if (File.Exists(swiftFilePath)) return;
+
+ // Create a file to write to.
+ using (var writer = File.CreateText(swiftFilePath))
+ {
+ writer.WriteLine("//\n// MAXSwiftSupport.swift\n//");
+ writer.WriteLine("\nimport Foundation\n");
+ writer.WriteLine("// This file ensures the project includes Swift support.");
+ writer.WriteLine("// It is automatically generated by the MAX Unity Plugin.");
+ writer.Close();
+ }
+ }
+
+ [PostProcessBuild(AppLovinPreProcess.CallbackOrder)]
+ public static void MaxPostProcessPlist(BuildTarget buildTarget, string path)
+ {
+ var plistPath = Path.Combine(path, "Info.plist");
+ var plist = new PlistDocument();
+ plist.ReadFromFile(plistPath);
+
+ RemoveAttributionReportEndpointIfNeeded(plist);
+
+ EnableVerboseLoggingIfNeeded(plist);
+ AddGoogleApplicationIdIfNeeded(plist);
+
+ AddSdkSettings(plist, path);
+ AddSkAdNetworksInfoIfNeeded(plist);
+ RemoveSdkKeyIfNeeded(plist);
+
+ plist.WriteToFile(plistPath);
+ }
+
+ private static void RemoveAttributionReportEndpointIfNeeded(PlistDocument plist)
+ {
+ PlistElement attributionReportEndPoint;
+ plist.root.values.TryGetValue("NSAdvertisingAttributionReportEndpoint", out attributionReportEndPoint);
+
+ // We no longer support this feature. Check if we had previously set the attribution endpoint and un-set it.
+ if (attributionReportEndPoint == null || !AppLovinAdvertisingAttributionEndpoint.Equals(attributionReportEndPoint.AsString())) return;
+
+ MaxSdkLogger.UserWarning("Global SKAdNetwork postback forwarding is no longer supported by AppLovin. Removing AppLovin Advertising Attribution Endpoint from Info.plist.");
+ plist.root.values.Remove("NSAdvertisingAttributionReportEndpoint");
+ }
+
+ private static void EnableVerboseLoggingIfNeeded(PlistDocument plist)
+ {
+ if (!EditorPrefs.HasKey(MaxSdkLogger.KeyVerboseLoggingEnabled)) return;
+
+ var enabled = EditorPrefs.GetBool(MaxSdkLogger.KeyVerboseLoggingEnabled);
+ if (enabled)
+ {
+ plist.root.SetBoolean(AppLovinVerboseLoggingOnKey, true);
+ }
+ else
+ {
+ plist.root.values.Remove(AppLovinVerboseLoggingOnKey);
+ }
+ }
+
+ private static void AddGoogleApplicationIdIfNeeded(PlistDocument plist)
+ {
+ if (!AppLovinPackageManager.IsAdapterInstalled("Google") && !AppLovinPackageManager.IsAdapterInstalled("GoogleAdManager")) return;
+
+ const string googleApplicationIdentifier = "GADApplicationIdentifier";
+ var appId = AppLovinSettings.Instance.AdMobIosAppId;
+ // Log error if the App ID is not set.
+ if (string.IsNullOrEmpty(appId) || !appId.StartsWith("ca-app-pub-"))
+ {
+ MaxSdkLogger.UserError("[AppLovin MAX] Google App ID is not set. Please enter a valid app ID within the AppLovin Integration Manager window.");
+ return;
+ }
+
+ plist.root.SetString(googleApplicationIdentifier, appId);
+ }
+
+ private static void AddYandexSettingsIfNeeded(PBXProject project, string unityMainTargetGuid)
+ {
+ if (!AppLovinPackageManager.IsAdapterInstalled("Yandex")) return;
+
+ if (MaxSdkUtils.CompareVersions(PlayerSettings.iOS.targetOSVersionString, "12.0") == MaxSdkUtils.VersionComparisonResult.Lesser)
+ {
+ MaxSdkLogger.UserWarning("Your iOS target version is under the minimum required version by Yandex. Please update it to 12.0 or newer in your ProjectSettings and rebuild your project.");
+ return;
+ }
+
+ project.SetBuildProperty(unityMainTargetGuid, "GENERATE_INFOPLIST_FILE", "NO");
+ }
+
+ private static void AddSdkSettings(PlistDocument infoPlist, string buildPath)
+ {
+ var sdkSettingsPlistPath = Path.Combine(buildPath, AppLovinSettingsPlistFileName);
+ var sdkSettingsPlist = new PlistDocument();
+ if (File.Exists(sdkSettingsPlistPath))
+ {
+ sdkSettingsPlist.ReadFromFile(sdkSettingsPlistPath);
+ }
+
+ // Add the SDK key to the SDK settings plist.
+ sdkSettingsPlist.root.SetString(KeySdkKey, AppLovinSettings.Instance.SdkKey);
+
+ // Add consent flow settings if needed.
+ EnableConsentFlowIfNeeded(sdkSettingsPlist, infoPlist);
+
+ sdkSettingsPlist.WriteToFile(sdkSettingsPlistPath);
+
+ var projectPath = PBXProject.GetPBXProjectPath(buildPath);
+ 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);
+ project.WriteToFile(projectPath);
+ }
+
+ private static void EnableConsentFlowIfNeeded(PlistDocument applovinSettingsPlist, PlistDocument infoPlist)
+ {
+ var consentFlowEnabled = AppLovinInternalSettings.Instance.ConsentFlowEnabled;
+ if (!consentFlowEnabled) return;
+
+ var userTrackingUsageDescription = AppLovinInternalSettings.Instance.UserTrackingUsageDescriptionEn;
+ var privacyPolicyUrl = AppLovinInternalSettings.Instance.ConsentFlowPrivacyPolicyUrl;
+ if (string.IsNullOrEmpty(userTrackingUsageDescription) || string.IsNullOrEmpty(privacyPolicyUrl))
+ {
+ AppLovinIntegrationManager.ShowBuildFailureDialog("You cannot use the AppLovin SDK's consent flow without defining a Privacy Policy URL and the `User Tracking Usage Description` in the AppLovin Integration Manager. \n\n" +
+ "Both values must be included to enable the SDK's consent flow.");
+
+ // No need to update the info.plist here. Default consent flow state will be determined on the SDK side.
+ return;
+ }
+
+ var consentFlowInfoRoot = applovinSettingsPlist.root.CreateDict(KeyConsentFlowInfo);
+ consentFlowInfoRoot.SetBoolean(KeyConsentFlowEnabled, consentFlowEnabled);
+ consentFlowInfoRoot.SetString(KeyConsentFlowPrivacyPolicy, privacyPolicyUrl);
+
+ var termsOfServiceUrl = AppLovinInternalSettings.Instance.ConsentFlowTermsOfServiceUrl;
+ if (MaxSdkUtils.IsValidString(termsOfServiceUrl))
+ {
+ consentFlowInfoRoot.SetString(KeyConsentFlowTermsOfService, termsOfServiceUrl);
+ }
+
+ var shouldShowTermsAndPrivacyPolicyAlertInGdpr = AppLovinInternalSettings.Instance.ShouldShowTermsAndPrivacyPolicyAlertInGDPR;
+ consentFlowInfoRoot.SetBoolean(KeyConsentFlowShowTermsAndPrivacyPolicyAlertInGDPR, shouldShowTermsAndPrivacyPolicyAlertInGdpr);
+
+ var debugUserGeography = AppLovinInternalSettings.Instance.DebugUserGeography;
+ if (debugUserGeography == MaxSdkBase.ConsentFlowUserGeography.Gdpr)
+ {
+ consentFlowInfoRoot.SetString(KeyConsentFlowDebugUserGeography, "gdpr");
+ }
+
+ infoPlist.root.SetString("NSUserTrackingUsageDescription", userTrackingUsageDescription);
+ }
+
+ private static void AddSkAdNetworksInfoIfNeeded(PlistDocument plist)
+ {
+ var skAdNetworkData = GetSkAdNetworkData();
+ var skAdNetworkIds = skAdNetworkData.SkAdNetworkIds;
+ // Check if we have a valid list of SKAdNetworkIds that need to be added.
+ if (skAdNetworkIds == null || skAdNetworkIds.Length < 1) return;
+
+ //
+ // Add the SKAdNetworkItems to the plist. It should look like following:
+ //
+ // SKAdNetworkItems
+ //
+ //
+ // SKAdNetworkIdentifier
+ // ABC123XYZ.skadnetwork
+ //
+ //
+ // SKAdNetworkIdentifier
+ // 123QWE456.skadnetwork
+ //
+ //
+ // SKAdNetworkIdentifier
+ // 987XYZ123.skadnetwork
+ //
+ //
+ //
+ PlistElement skAdNetworkItems;
+ plist.root.values.TryGetValue("SKAdNetworkItems", out skAdNetworkItems);
+ var existingSkAdNetworkIds = new HashSet();
+ // Check if SKAdNetworkItems array is already in the Plist document and collect all the IDs that are already present.
+ if (skAdNetworkItems != null && skAdNetworkItems.GetType() == typeof(PlistElementArray))
+ {
+ var plistElementDictionaries = skAdNetworkItems.AsArray().values.Where(plistElement => plistElement.GetType() == typeof(PlistElementDict));
+ foreach (var plistElement in plistElementDictionaries)
+ {
+ PlistElement existingId;
+ plistElement.AsDict().values.TryGetValue("SKAdNetworkIdentifier", out existingId);
+ if (existingId == null || existingId.GetType() != typeof(PlistElementString) || string.IsNullOrEmpty(existingId.AsString())) continue;
+
+ existingSkAdNetworkIds.Add(existingId.AsString());
+ }
+ }
+ // Else, create an array of SKAdNetworkItems into which we will add our IDs.
+ else
+ {
+ skAdNetworkItems = plist.root.CreateArray("SKAdNetworkItems");
+ }
+
+ foreach (var skAdNetworkId in skAdNetworkIds)
+ {
+ // Skip adding IDs that are already in the array.
+ if (existingSkAdNetworkIds.Contains(skAdNetworkId)) continue;
+
+ var skAdNetworkItemDict = skAdNetworkItems.AsArray().AddDict();
+ skAdNetworkItemDict.SetString("SKAdNetworkIdentifier", skAdNetworkId);
+ }
+ }
+
+ private static SkAdNetworkData GetSkAdNetworkData()
+ {
+ // Get the list of installed ad networks to be passed up
+ var installedNetworks = AppLovinPackageManager.GetInstalledMediationNetworks();
+ var uriBuilder = new UriBuilder("https://unity.applovin.com/max/1.0/skadnetwork_ids");
+ var adNetworks = string.Join(",", installedNetworks.ToArray());
+ if (MaxSdkUtils.IsValidString(adNetworks))
+ {
+ uriBuilder.Query += string.Format("ad_networks={0}", adNetworks);
+ }
+
+ using (var unityWebRequest = UnityWebRequest.Get(uriBuilder.ToString()))
+ {
+ var operation = unityWebRequest.SendWebRequest();
+ // Wait for the download to complete or the request to timeout.
+ while (!operation.isDone) { }
+
+#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();
+ }
+
+ try
+ {
+ return JsonUtility.FromJson(unityWebRequest.downloadHandler.text);
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to parse data '" + unityWebRequest.downloadHandler.text + "' with exception: " + exception);
+ return new SkAdNetworkData();
+ }
+ }
+ }
+
+ private static void RemoveSdkKeyIfNeeded(PlistDocument plist)
+ {
+ if (!plist.root.values.ContainsKey(KeyAppLovinSdkKeyToRemove)) return;
+
+ plist.root.values.Remove(KeyAppLovinSdkKeyToRemove);
+ }
+ }
+}
+
+#endif
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessiOS.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessiOS.cs.meta
new file mode 100644
index 0000000..8182708
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessiOS.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: d5d209f90444f4a90830b48b5f3f3ff4
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPostProcessiOS.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcess.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcess.cs
new file mode 100644
index 0000000..3ed102a
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcess.cs
@@ -0,0 +1,182 @@
+//
+// AppLovinPreProcess.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Jonathan Liu on 10/19/2023.
+// Copyright © 2023 AppLovin. All rights reserved.
+//
+
+using System;
+using System.IO;
+using System.Linq;
+using System.Xml;
+using System.Xml.Linq;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ public abstract class AppLovinPreProcess
+ {
+ // Use a slightly lower value than max value so pubs have the option to run a post process script after ours.
+ internal const int CallbackOrder = int.MaxValue - 10;
+ private const string AppLovinDependenciesFileExportPath = "MaxSdk/AppLovin/Editor/Dependencies.xml";
+ private const string ElementNameDependencies = "dependencies";
+
+ private static readonly XmlWriterSettings DependenciesFileXmlWriterSettings = new XmlWriterSettings
+ {
+ Indent = true,
+ IndentChars = " ",
+ NewLineChars = "\n",
+ NewLineHandling = NewLineHandling.Replace
+ };
+
+ protected static string AppLovinDependenciesFilePath
+ {
+ get { return AppLovinIntegrationManager.IsPluginInPackageManager ? Path.Combine("Assets", AppLovinDependenciesFileExportPath) : MaxSdkUtils.GetAssetPathForExportPath(AppLovinDependenciesFileExportPath); }
+ }
+
+ ///
+ /// Gets the AppLovin Dependencies.xml file. If `createIfNotExists` is true, a new file will be created if one does not exist.
+ ///
+ /// The path to the AppLovin Dependencies.xml file
+ /// Whether to create a new Dependencies.xml file if one does not exist
+ ///
+ protected static XDocument GetAppLovinDependenciesFile(string path, bool createIfNotExists = false)
+ {
+ try
+ {
+ if (File.Exists(path))
+ {
+ return XDocument.Load(path);
+ }
+
+ if (createIfNotExists)
+ {
+ return new XDocument(new XDeclaration("1.0", "utf-8", "yes"),
+ new XElement(ElementNameDependencies));
+ }
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.E("Unable to load Dependencies file due to exception: " + exception.Message);
+ }
+
+ return null;
+ }
+
+ ///
+ /// Updates a dependency if it exists, otherwise adds a new dependency.
+ ///
+ /// The dependencies document we are writing to
+ /// The parent tag that we want to search for the dependency. For example, to add a new dependency to Android, pass in "androidPackages"
+ /// The element we are looking to update/add. For example, to add a new dependency to Android, pass in "androidPackage"
+ /// The attribute name we want in the dependency. For example, to add something to the spec attribute, pass in "spec"
+ /// The attribute value prefix we are looking to replace. For example, "com.google.android.ump:user-messaging-platform"
+ /// The new dependency we want to add.
+ protected static void AddOrUpdateDependency(
+ XDocument dependenciesDocument,
+ string parentTag,
+ string elementTag,
+ string matchAttribute,
+ string matchValuePrefix,
+ XElement newDependency)
+ {
+ var parentElement = dependenciesDocument.Root.Element(parentTag);
+ if (parentElement == null)
+ {
+ parentElement = new XElement(parentTag);
+ dependenciesDocument.Root.Add(parentElement);
+ }
+
+ // Check if a dependency exists that matches the attributes name and value
+ var existingElement = parentElement.Elements(elementTag)
+ .FirstOrDefault(element =>
+ {
+ var attr = element.Attribute(matchAttribute);
+ return attr != null && attr.Value.StartsWith(matchValuePrefix, StringComparison.OrdinalIgnoreCase);
+ });
+
+ if (existingElement != null)
+ {
+ foreach (var attr in newDependency.Attributes())
+ {
+ existingElement.SetAttributeValue(attr.Name, attr.Value);
+ }
+ }
+ else
+ {
+ parentElement.Add(newDependency);
+ }
+ }
+
+ ///
+ /// Removes a dependency from an xml file.
+ ///
+ /// The xml file to remove a dependency from
+ /// The parent tag that we want to search for the dependency to remove. For example: "androidPackages"
+ /// The element we are looking to remove. For example: "androidPackage"
+ /// The attribute name we want to remove. For example: "spec"
+ /// The attribute value prefix we are looking to replace. For example: "com.google.android.ump:user-messaging-platform"
+ /// True if the dependency was removed successfully, otherwise return false.
+ protected static bool RemoveDependency(
+ XDocument doc,
+ string parentTag,
+ string elementTag,
+ string matchAttribute,
+ string matchValuePrefix)
+ {
+ var root = doc.Root;
+ if (root == null) return false;
+
+ var parentElement = root.Element(parentTag);
+ if (parentElement == null) return false;
+
+ XElement toRemove = null;
+ foreach (var e in parentElement.Elements(elementTag))
+ {
+ var attr = e.Attribute(matchAttribute);
+ if (attr != null && attr.Value.StartsWith(matchValuePrefix))
+ {
+ toRemove = e;
+ break;
+ }
+ }
+
+ if (toRemove == null) return false;
+
+ toRemove.Remove();
+ return true;
+ }
+
+ ///
+ /// Saves an xml file.
+ ///
+ /// The document to save
+ /// The path to the document to save
+ /// Returns true if the file was saved successfully
+ protected static bool SaveDependenciesFile(XDocument doc, string path)
+ {
+ try
+ {
+ // Ensure directory exists before saving the file
+ var directory = Path.GetDirectoryName(path);
+ if (MaxSdkUtils.IsValidString(directory))
+ {
+ // Does nothing if directory already exists
+ Directory.CreateDirectory(directory);
+ }
+
+ using (var xmlWriter = XmlWriter.Create(path, DependenciesFileXmlWriterSettings))
+ {
+ doc.Save(xmlWriter);
+ return true;
+ }
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.E("Unable to save Dependencies file due to exception: " + exception.Message);
+ }
+
+ return false;
+ }
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcess.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcess.cs.meta
new file mode 100644
index 0000000..f5e00db
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcess.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 0e6254f24e89548b3a7644fa7bf25f9d
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcess.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessAndroid.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessAndroid.cs
new file mode 100644
index 0000000..209a4bf
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessAndroid.cs
@@ -0,0 +1,115 @@
+//
+// AppLovinBuildPreProcessor.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 8/27/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+#if UNITY_ANDROID
+
+using System.Xml.Linq;
+using UnityEditor.Build;
+using UnityEditor.Build.Reporting;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ ///
+ /// Adds the AppLovin Quality Service plugin to the gradle template file. See for more details.
+ ///
+ public class AppLovinPreProcessAndroid : AppLovinProcessGradleBuildFile, IPreprocessBuildWithReport
+ {
+ private const string ElementNameAndroidPackages = "androidPackages";
+ 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";
+
+ public void OnPreprocessBuild(BuildReport report)
+ {
+ PreprocessAppLovinQualityServicePlugin();
+ AddGoogleCmpDependencyIfNeeded();
+ }
+
+ private static void PreprocessAppLovinQualityServicePlugin()
+ {
+ // 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()
+ {
+ if (AppLovinInternalSettings.Instance.ConsentFlowEnabled)
+ {
+ var umpPackage = new XElement(ElementNameAndroidPackage,
+ new XAttribute(AttributeNameSpec, UmpDependencyPackage + UmpDependencyVersion));
+ var success = AddOrUpdateAndroidDependency(UmpDependencyPackage, umpPackage );
+ if (!success)
+ {
+ MaxSdkLogger.UserWarning("Google CMP will not function. Unable to add user-messaging-platform dependency.");
+ }
+ }
+ else
+ {
+ RemoveAndroidDependency(UmpDependencyPackage);
+ }
+ }
+
+ ///
+ /// Adds or updates an Android dependency in the AppLovin Dependencies.xml file.
+ ///
+ /// The package that we are trying to update
+ /// The new dependency to add if it doesn't exist
+ /// Returns true if the file was successfully edited
+ private static bool AddOrUpdateAndroidDependency(string package, XElement newDependency)
+ {
+ var dependenciesFilePath = AppLovinDependenciesFilePath;
+ var dependenciesDocument = GetAppLovinDependenciesFile(dependenciesFilePath, AppLovinIntegrationManager.IsPluginInPackageManager);
+ if (dependenciesDocument == null) return false;
+
+ AddOrUpdateDependency(dependenciesDocument,
+ ElementNameAndroidPackages,
+ ElementNameAndroidPackage,
+ AttributeNameSpec,
+ package,
+ newDependency);
+ return SaveDependenciesFile(dependenciesDocument, dependenciesFilePath);
+ }
+
+ ///
+ /// Removed an android dependency from the AppLovin Dependencies.xml file.
+ ///
+ /// The package to remove
+ private static void RemoveAndroidDependency(string package)
+ {
+ var dependenciesFilePath = AppLovinDependenciesFilePath;
+ var dependenciesDocument = GetAppLovinDependenciesFile(dependenciesFilePath);
+ if (dependenciesDocument == null) return;
+
+ var removed = RemoveDependency(dependenciesDocument,
+ ElementNameAndroidPackages,
+ ElementNameAndroidPackage,
+ AttributeNameSpec,
+ package);
+
+ if (!removed) return;
+
+ SaveDependenciesFile(dependenciesDocument, dependenciesFilePath);
+ }
+
+ public int callbackOrder
+ {
+ get { return CallbackOrder; }
+ }
+ }
+}
+
+#endif
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessAndroid.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessAndroid.cs.meta
new file mode 100644
index 0000000..4dd79e8
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessAndroid.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 8ccaf444d0d4f4cadb5debe7c41b20c4
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessAndroid.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessiOS.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessiOS.cs
new file mode 100644
index 0000000..e99ea7b
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessiOS.cs
@@ -0,0 +1,99 @@
+//
+// AppLovinBuildPreProcessiOS.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Jonathan Liu on 10/17/2023.
+// Copyright © 2023 AppLovin. All rights reserved.
+//
+
+#if UNITY_IOS
+
+using System.Xml.Linq;
+using UnityEditor.Build;
+using UnityEditor.Build.Reporting;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ public class AppLovinPreProcessiOS : AppLovinPreProcess, IPreprocessBuildWithReport
+ {
+ public void OnPreprocessBuild(BuildReport report)
+ {
+ AddGoogleCmpDependencyIfNeeded();
+ }
+
+ private const string ElementNameIosPods = "iosPods";
+ private const string ElementNameIosPod = "iosPod";
+ private const string AttributeNameName = "name";
+ private const string AttributeNameVersion = "version";
+ private const string UmpDependencyPod = "GoogleUserMessagingPlatform";
+ private const string UmpDependencyVersion = "~> 2.1";
+
+ private static void AddGoogleCmpDependencyIfNeeded()
+ {
+ if (AppLovinInternalSettings.Instance.ConsentFlowEnabled)
+ {
+ var umpDependency = new XElement(ElementNameIosPod,
+ new XAttribute(AttributeNameName, UmpDependencyPod),
+ new XAttribute(AttributeNameVersion, UmpDependencyVersion));
+ var success = AddOrUpdateIosDependency(UmpDependencyPod, umpDependency);
+ if (!success)
+ {
+ MaxSdkLogger.UserWarning("Google CMP will not function. Unable to add GoogleUserMessagingPlatform dependency.");
+ }
+ }
+ else
+ {
+ RemoveIosDependency(UmpDependencyPod);
+ }
+ }
+
+ ///
+ /// Adds or updates an iOS pod in the AppLovin Dependencies.xml file.
+ ///
+ /// The pod that we are trying to update
+ /// The new dependency to add if it doesn't exist
+ /// Returns true if the file was successfully edited
+ private static bool AddOrUpdateIosDependency(string pod, XElement newDependency)
+ {
+ var dependenciesFilePath = AppLovinDependenciesFilePath;
+ var dependenciesDocument = GetAppLovinDependenciesFile(dependenciesFilePath, AppLovinIntegrationManager.IsPluginInPackageManager);
+ if (dependenciesDocument == null) return false;
+
+ AddOrUpdateDependency(dependenciesDocument,
+ ElementNameIosPods,
+ ElementNameIosPod,
+ AttributeNameName,
+ pod,
+ newDependency);
+ return SaveDependenciesFile(dependenciesDocument, dependenciesFilePath);
+ }
+
+ ///
+ /// Removed an iOS pod from the AppLovin Dependencies.xml file.
+ ///
+ /// The pod to remove
+ private static void RemoveIosDependency(string pod)
+ {
+ var dependenciesFilePath = AppLovinDependenciesFilePath;
+ var dependenciesDocument = GetAppLovinDependenciesFile(dependenciesFilePath);
+ if (dependenciesDocument == null) return;
+
+ var removed = RemoveDependency(dependenciesDocument,
+ ElementNameIosPods,
+ ElementNameIosPod,
+ AttributeNameName,
+ pod);
+
+ if (!removed) return;
+
+ SaveDependenciesFile(dependenciesDocument, dependenciesFilePath);
+ }
+
+ public int callbackOrder
+ {
+ get { return CallbackOrder; }
+ }
+ }
+}
+
+#endif
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessiOS.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessiOS.cs.meta
new file mode 100644
index 0000000..33bdffb
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessiOS.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: ee45537a5833240d7afcfac4a38df1b9
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinPreProcessiOS.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinProcessGradleBuildFile.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinProcessGradleBuildFile.cs
new file mode 100644
index 0000000..464bd04
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinProcessGradleBuildFile.cs
@@ -0,0 +1,634 @@
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 9/3/19.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+#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 UnityEngine;
+using UnityEngine.Networking;
+using Debug = UnityEngine.Debug;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ [Serializable]
+ public class AppLovinQualityServiceData
+ {
+ // ReSharper disable once InconsistentNaming - Need to keep name for response data
+ public string api_key;
+ }
+
+ ///
+ /// Adds or updates the AppLovin Quality Service plugin to the provided build.gradle file.
+ /// If the gradle file already has the plugin, the API key is updated.
+ ///
+ public abstract class AppLovinProcessGradleBuildFile : AppLovinPreProcess
+ {
+ private static readonly Regex TokenBuildScriptRepositories = new Regex(".*repositories.*");
+ private static readonly Regex TokenBuildScriptDependencies = new Regex(".*classpath \'com.android.tools.build:gradle.*");
+ private static readonly Regex TokenApplicationPlugin = new Regex(".*apply plugin: \'com.android.application\'.*");
+ private static readonly Regex TokenApiKey = new Regex(".*apiKey.*");
+ private static readonly Regex TokenAppLovinPlugin = new Regex(".*apply plugin:.+?(?=applovin-quality-service).*");
+
+ private const string PluginsMatcher = "plugins";
+ private const string PluginManagementMatcher = "pluginManagement";
+ private const string QualityServicePluginRoot = " id 'com.applovin.quality' version '+' apply false // NOTE: Requires version 4.8.3+ for Gradle version 7.2+";
+
+ private const string BuildScriptMatcher = "buildscript";
+ private const string QualityServiceMavenRepo = "maven { url 'https://artifacts.applovin.com/android'; content { includeGroupByRegex 'com.applovin.*' } }";
+ private const string QualityServiceDependencyClassPath = "classpath 'com.applovin.quality:AppLovinQualityServiceGradlePlugin:+'";
+ private const string QualityServiceApplyPlugin = "apply plugin: 'applovin-quality-service'";
+ private const string QualityServicePlugin = "applovin {";
+ private const string QualityServiceApiKey = " apiKey '{0}'";
+ private const string QualityServiceBintrayMavenRepo = "https://applovin.bintray.com/Quality-Service";
+ private const string QualityServiceNoRegexMavenRepo = "maven { url 'https://artifacts.applovin.com/android' }";
+
+ // Legacy plugin detection variables
+ private const string QualityServiceDependencyClassPathV3 = "classpath 'com.applovin.quality:AppLovinQualityServiceGradlePlugin:3.+'";
+ private static readonly Regex TokenSafeDkLegacyApplyPlugin = new Regex(".*apply plugin:.+?(?=safedk).*");
+ private const string SafeDkLegacyPlugin = "safedk {";
+ private const string SafeDkLegacyMavenRepo = "http://download.safedk.com";
+ private const string SafeDkLegacyDependencyClassPath = "com.safedk:SafeDKGradlePlugin:";
+
+ ///
+ /// 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.
+ ///
+ /// Gradle's required structure for including plugins varies by version:
+ /// - Older versions of Gradle require the plugin to be added to the dependencies block.
+ /// Example:
+ /// dependencies {
+ /// classpath 'com.android.tools.build:gradle:4.0.1'
+ /// classpath 'com.applovin.quality:AppLovinQualityServiceGradlePlugin:+'
+ /// }
+ ///
+ /// - Newer versions of gradle require the plugin to be added to the plugins block.
+ /// Example:
+ /// plugins {
+ /// id 'com.android.application' version '7.4.2' apply false
+ /// id 'com.android.library' version '7.4.2' apply false
+ /// id 'com.applovin.quality' version '+' apply false
+ /// }
+ ///
+ /// Since Unity projects may use custom Gradle versions depending on the Unity version or
+ /// user modifications, this check ensures proper integration of the AppLovin plugin.
+ ///
+ /// The path to project's root build.gradle file.
+ /// true if the file contains a `dependencies` block, indicating an older Gradle version
+ protected static bool ShouldAddQualityServiceToDependencies(string rootGradleBuildFile)
+ {
+ var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
+ return lines.Any(line => TokenBuildScriptDependencies.IsMatch(line));
+ }
+
+ ///
+ /// Updates the provided Gradle script to add Quality Service plugin.
+ ///
+ /// The gradle file to update.
+ protected static void AddAppLovinQualityServicePlugin(string applicationGradleBuildFilePath)
+ {
+ if (!AppLovinSettings.Instance.QualityServiceEnabled) return;
+
+ var sdkKey = AppLovinSettings.Instance.SdkKey;
+ if (string.IsNullOrEmpty(sdkKey))
+ {
+ MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. SDK Key is empty. Please enter the AppLovin SDK Key in the Integration Manager.");
+ return;
+ }
+
+ // Retrieve the API Key using the SDK Key.
+ var qualityServiceData = RetrieveQualityServiceData(sdkKey);
+ var apiKey = qualityServiceData.api_key;
+ if (string.IsNullOrEmpty(apiKey))
+ {
+ MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. API Key is empty.");
+ return;
+ }
+
+ // Generate the updated Gradle file that needs to be written.
+ var lines = File.ReadAllLines(applicationGradleBuildFilePath).ToList();
+ var sanitizedLines = RemoveLegacySafeDkPlugin(lines);
+ 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
+ );
+ // outputLines can be null if we couldn't add the plugin.
+ if (outputLines == null) return;
+
+ try
+ {
+ File.WriteAllText(applicationGradleBuildFilePath, string.Join("\n", outputLines.ToArray()) + "\n");
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. Gradle file write failed.");
+ Console.WriteLine(exception);
+ }
+ }
+
+ ///
+ /// Adds AppLovin Quality Service plugin DSL element to the project's root build.gradle file.
+ /// Sample build.gradle file after adding quality service:
+ /// plugins {
+ /// id 'com.android.application' version '7.4.2' apply false
+ /// id 'com.android.library' version '7.4.2' apply false
+ /// id 'com.applovin.quality' version '+' apply false
+ /// }
+ /// tasks.register('clean', Delete) {
+ /// delete rootProject.layout.buildDirectory
+ /// }
+ ///
+ ///
+ /// The path to project's root build.gradle file.
+ /// true when the plugin was added successfully.
+ protected bool AddPluginToRootGradleBuildFile(string rootGradleBuildFile)
+ {
+ var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
+
+ // Check if the plugin is already added to the file.
+ var pluginAdded = lines.Any(line => line.Contains(QualityServicePluginRoot));
+ if (pluginAdded) return true;
+
+ var outputLines = new List();
+ var insidePluginsClosure = false;
+ foreach (var line in lines)
+ {
+ if (line.Contains(PluginsMatcher))
+ {
+ insidePluginsClosure = true;
+ }
+
+ if (!pluginAdded && insidePluginsClosure && line.Contains("}"))
+ {
+ outputLines.Add(QualityServicePluginRoot);
+ pluginAdded = true;
+ insidePluginsClosure = false;
+ }
+
+ outputLines.Add(line);
+ }
+
+ if (!pluginAdded) return false;
+
+ try
+ {
+ File.WriteAllText(rootGradleBuildFile, string.Join("\n", outputLines.ToArray()) + "\n");
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. Root Gradle file write failed.");
+ Console.WriteLine(exception);
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Adds the AppLovin maven repository to the project's settings.gradle file.
+ /// Sample settings.gradle file after adding AppLovin Repository:
+ /// pluginManagement {
+ /// repositories {
+ /// maven { url 'https://artifacts.applovin.com/android'; content { includeGroupByRegex 'com.applovin.*' } }
+ ///
+ /// gradlePluginPortal()
+ /// google()
+ /// mavenCentral()
+ /// }
+ /// }
+ /// ...
+ ///
+ ///
+ /// The path to the project's settings.gradle file.
+ /// true if the repository was added successfully.
+ protected bool AddAppLovinRepository(string settingsGradleFile)
+ {
+ var lines = File.ReadLines(settingsGradleFile).ToList();
+ var outputLines = new List();
+ var mavenRepoAdded = false;
+ var pluginManagementClosureDepth = 0;
+ var insidePluginManagementClosure = false;
+ var pluginManagementMatched = false;
+ foreach (var line in lines)
+ {
+ outputLines.Add(line);
+
+ if (!pluginManagementMatched && line.Contains(PluginManagementMatcher))
+ {
+ pluginManagementMatched = true;
+ insidePluginManagementClosure = true;
+ }
+
+ if (insidePluginManagementClosure)
+ {
+ if (line.Contains("{"))
+ {
+ pluginManagementClosureDepth++;
+ }
+
+ if (line.Contains("}"))
+ {
+ pluginManagementClosureDepth--;
+ }
+
+ if (pluginManagementClosureDepth == 0)
+ {
+ insidePluginManagementClosure = false;
+ }
+ }
+
+ if (insidePluginManagementClosure)
+ {
+ if (!mavenRepoAdded && TokenBuildScriptRepositories.IsMatch(line))
+ {
+ outputLines.Add(GetFormattedBuildScriptLine(QualityServiceMavenRepo));
+ mavenRepoAdded = true;
+ }
+ }
+ }
+
+ if (!mavenRepoAdded) return false;
+
+ try
+ {
+ File.WriteAllText(settingsGradleFile, string.Join("\n", outputLines.ToArray()) + "\n");
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. Setting Gradle file write failed.");
+ Console.WriteLine(exception);
+ return false;
+ }
+
+ return true;
+ }
+
+#if UNITY_2019_3_OR_NEWER
+ ///
+ /// 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:
+ /// allprojects {
+ /// buildscript {
+ /// repositories {
+ /// maven { url 'https://artifacts.applovin.com/android'; content { includeGroupByRegex 'com.applovin.*' } }
+ /// google()
+ /// jcenter()
+ /// }
+ ///
+ /// dependencies {
+ /// classpath 'com.android.tools.build:gradle:4.0.1'
+ /// classpath 'com.applovin.quality:AppLovinQualityServiceGradlePlugin:+'
+ /// }
+ /// ...
+ ///
+ ///
+ /// The root build.gradle file path
+ /// true if the build script lines were applied correctly.
+ protected bool AddQualityServiceBuildScriptLines(string rootGradleBuildFile)
+ {
+ var lines = File.ReadAllLines(rootGradleBuildFile).ToList();
+ var outputLines = GenerateUpdatedBuildFileLines(lines, null, true);
+
+ // outputLines will be null if we couldn't add the build script lines.
+ if (outputLines == null) return false;
+
+ try
+ {
+ File.WriteAllText(rootGradleBuildFile, string.Join("\n", outputLines.ToArray()) + "\n");
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to install AppLovin Quality Service plugin. Root Gradle file write failed.");
+ Console.WriteLine(exception);
+ return false;
+ }
+
+ return true;
+ }
+
+ ///
+ /// Removes the AppLovin Quality Service Plugin or Legacy SafeDK plugin from the given gradle template file if either of them are present.
+ ///
+ /// The gradle template file from which to remove the plugin from
+ protected static void RemoveAppLovinQualityServiceOrSafeDkPlugin(string gradleTemplateFile)
+ {
+ var lines = File.ReadAllLines(gradleTemplateFile).ToList();
+ lines = RemoveLegacySafeDkPlugin(lines);
+ lines = RemoveAppLovinQualityServicePlugin(lines);
+
+ try
+ {
+ File.WriteAllText(gradleTemplateFile, string.Join("\n", lines.ToArray()) + "\n");
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to remove AppLovin Quality Service Plugin from mainTemplate.gradle. Please remove the Quality Service plugin from the mainTemplate.gradle manually.");
+ 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"))
+ {
+ unityWebRequest.method = UnityWebRequest.kHttpVerbPOST;
+ unityWebRequest.uploadHandler = uploadHandler;
+ unityWebRequest.downloadHandler = new DownloadHandlerBuffer();
+
+ var operation = unityWebRequest.SendWebRequest();
+
+ // Wait for the download to complete or the request to timeout.
+ while (!operation.isDone) { }
+
+#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();
+ }
+
+ try
+ {
+ return JsonUtility.FromJson(unityWebRequest.downloadHandler.text);
+ }
+ catch (Exception exception)
+ {
+ MaxSdkLogger.UserError("Failed to parse API Key." + exception);
+ return new AppLovinQualityServiceData();
+ }
+ }
+ }
+
+ private static List RemoveLegacySafeDkPlugin(List lines)
+ {
+ return RemovePlugin(lines, SafeDkLegacyPlugin, SafeDkLegacyMavenRepo, SafeDkLegacyDependencyClassPath, TokenSafeDkLegacyApplyPlugin);
+ }
+
+ private static List RemoveAppLovinQualityServicePlugin(List lines)
+ {
+ return RemovePlugin(lines, QualityServicePlugin, QualityServiceMavenRepo, QualityServiceDependencyClassPath, TokenAppLovinPlugin);
+ }
+
+ private static List RemovePlugin(List lines, string pluginLine, string mavenRepo, string dependencyClassPath, Regex applyPluginToken)
+ {
+ var sanitizedLines = new List();
+ var legacyRepoRemoved = false;
+ var legacyDependencyClassPathRemoved = false;
+ var legacyPluginRemoved = false;
+ var legacyPluginMatched = false;
+ var insideLegacySafeDkClosure = false;
+ foreach (var line in lines)
+ {
+ if (!legacyPluginMatched && line.Contains(pluginLine))
+ {
+ legacyPluginMatched = true;
+ insideLegacySafeDkClosure = true;
+ }
+
+ if (insideLegacySafeDkClosure && line.Contains("}"))
+ {
+ insideLegacySafeDkClosure = false;
+ continue;
+ }
+
+ if (insideLegacySafeDkClosure)
+ {
+ continue;
+ }
+
+ if (!legacyRepoRemoved && line.Contains(mavenRepo))
+ {
+ legacyRepoRemoved = true;
+ continue;
+ }
+
+ if (!legacyDependencyClassPathRemoved && line.Contains(dependencyClassPath))
+ {
+ legacyDependencyClassPathRemoved = true;
+ continue;
+ }
+
+ if (!legacyPluginRemoved && applyPluginToken.IsMatch(line))
+ {
+ legacyPluginRemoved = true;
+ continue;
+ }
+
+ sanitizedLines.Add(line);
+ }
+
+ return sanitizedLines;
+ }
+
+ private static List GenerateUpdatedBuildFileLines(List lines, string apiKey, bool addBuildScriptLines)
+ {
+ var addPlugin = MaxSdkUtils.IsValidString(apiKey);
+ // A sample of the template file.
+ // ...
+ // allprojects {
+ // repositories {**ARTIFACTORYREPOSITORY**
+ // google()
+ // jcenter()
+ // flatDir {
+ // dirs 'libs'
+ // }
+ // }
+ // }
+ //
+ // apply plugin: 'com.android.application'
+ // **APPLY_PLUGINS**
+ //
+ // dependencies {
+ // implementation fileTree(dir: 'libs', include: ['*.jar'])
+ // **DEPS**}
+ // ...
+ var outputLines = new List();
+ // 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)
+ {
+ // 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);
+ }
+ }
+ }
+ // 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)
+ {
+ // 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;
+ }
+
+ public static string GetFormattedBuildScriptLine(string buildScriptLine)
+ {
+#if UNITY_2022_2_OR_NEWER
+ return " "
+#elif UNITY_2019_3_OR_NEWER
+ return " "
+#else
+ return " "
+#endif
+ + buildScriptLine;
+ }
+
+ private static IEnumerable GenerateAppLovinPluginClosure(string apiKey)
+ {
+ // applovin {
+ // // NOTE: DO NOT CHANGE - this is NOT your AppLovin MAX SDK key - this is a derived key.
+ // apiKey "456...a1b"
+ // }
+ var linesToInject = new List(5);
+ linesToInject.Add("");
+ linesToInject.Add("applovin {");
+ linesToInject.Add(" // NOTE: DO NOT CHANGE - this is NOT your AppLovin MAX SDK key - this is a derived key.");
+ linesToInject.Add(string.Format(QualityServiceApiKey, apiKey));
+ linesToInject.Add("}");
+
+ return linesToInject;
+ }
+ }
+}
+
+#endif
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinProcessGradleBuildFile.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinProcessGradleBuildFile.cs.meta
new file mode 100644
index 0000000..5e49844
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinProcessGradleBuildFile.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: 732b7510bc9c94aafb3fd3b8c0dc5d2d
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinProcessGradleBuildFile.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinSettings.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinSettings.cs
new file mode 100644
index 0000000..e34f0ba
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinSettings.cs
@@ -0,0 +1,160 @@
+//
+// AppLovinSettings.cs
+// AppLovin MAX Unity Plugin
+//
+// Created by Santosh Bagadi on 1/27/20.
+// Copyright © 2019 AppLovin. All rights reserved.
+//
+
+using AppLovinMax.Scripts.IntegrationManager.Editor;
+using System.IO;
+using UnityEditor;
+using UnityEngine;
+using UnityEngine.Serialization;
+
+///
+/// A representing the AppLovin Settings that can be set in the Integration Manager Window.
+///
+/// The scriptable object asset is created with the name AppLovinSettings.asset and is placed under the directory Assets/MaxSdk/Resources.
+///
+/// NOTE: Not name spacing this class since it is reflected upon by the Google adapter and will break compatibility.
+///
+public class AppLovinSettings : ScriptableObject
+{
+ private const string SettingsExportPath = "MaxSdk/Resources/AppLovinSettings.asset";
+
+ private static AppLovinSettings _instance;
+
+ [SerializeField] private bool qualityServiceEnabled = true;
+ [SerializeField] private string sdkKey;
+
+ [SerializeField] private string customGradleVersionUrl;
+ [SerializeField] private string customGradleToolsVersion;
+
+ [SerializeField] private string adMobAndroidAppId = string.Empty;
+ [SerializeField] private string adMobIosAppId = string.Empty;
+
+ ///
+ /// An instance of AppLovin Setting.
+ ///
+ public static AppLovinSettings Instance
+ {
+ get
+ {
+ if (_instance == null)
+ {
+ // Check for an existing AppLovinSettings somewhere in the project
+ var guids = AssetDatabase.FindAssets("AppLovinSettings t:ScriptableObject");
+ if (guids.Length > 1)
+ {
+ MaxSdkLogger.UserWarning("Multiple AppLovinSettings found. This may cause unexpected results.");
+ }
+
+ if (guids.Length != 0)
+ {
+ var path = AssetDatabase.GUIDToAssetPath(guids[0]);
+ _instance = AssetDatabase.LoadAssetAtPath(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 (AppLovinIntegrationManager.IsPluginInPackageManager)
+ {
+ // Note: Can't use absolute path when calling `CreateAsset`. Should use relative path to Assets/ directory.
+ settingsFilePath = Path.Combine("Assets", SettingsExportPath);
+
+ var maxSdkDir = Path.Combine(Application.dataPath, "MaxSdk");
+ if (!Directory.Exists(maxSdkDir))
+ {
+ Directory.CreateDirectory(maxSdkDir);
+ }
+ }
+ else
+ {
+ settingsFilePath = Path.Combine(AppLovinIntegrationManager.PluginParentDirectory, SettingsExportPath);
+ }
+
+ var settingsDir = Path.GetDirectoryName(settingsFilePath);
+ if (!Directory.Exists(settingsDir))
+ {
+ Directory.CreateDirectory(settingsDir);
+ }
+
+ // On script reload AssetDatabase.FindAssets() can fail and will overwrite AppLovinSettings without this check
+ if (!File.Exists(settingsFilePath))
+ {
+ _instance = CreateInstance();
+ AssetDatabase.CreateAsset(_instance, settingsFilePath);
+ MaxSdkLogger.D("Creating new AppLovinSettings asset at path: " + settingsFilePath);
+ }
+ }
+
+ return _instance;
+ }
+ }
+
+ ///
+ /// Whether or not to install Quality Service plugin.
+ ///
+ public bool QualityServiceEnabled
+ {
+ get { return Instance.qualityServiceEnabled; }
+ set { Instance.qualityServiceEnabled = value; }
+ }
+
+ ///
+ /// AppLovin SDK Key.
+ ///
+ public string SdkKey
+ {
+ get { return Instance.sdkKey; }
+ set { Instance.sdkKey = value; }
+ }
+
+ ///
+ /// A URL to set the distributionUrl in the gradle-wrapper.properties file (ex: https\://services.gradle.org/distributions/gradle-6.9.3-bin.zip)
+ ///
+ public string CustomGradleVersionUrl
+ {
+ get { return Instance.customGradleVersionUrl; }
+ set { Instance.customGradleVersionUrl = value; }
+ }
+
+ ///
+ /// A string to set the custom gradle tools version (ex: com.android.tools.build:gradle:4.2.0)
+ ///
+ public string CustomGradleToolsVersion
+ {
+ get { return Instance.customGradleToolsVersion; }
+ set { Instance.customGradleToolsVersion = value; }
+ }
+
+ ///
+ /// AdMob Android App ID.
+ ///
+ public string AdMobAndroidAppId
+ {
+ get { return Instance.adMobAndroidAppId; }
+ set { Instance.adMobAndroidAppId = value; }
+ }
+
+ ///
+ /// AdMob iOS App ID.
+ ///
+ public string AdMobIosAppId
+ {
+ get { return Instance.adMobIosAppId; }
+ set { Instance.adMobIosAppId = value; }
+ }
+
+ ///
+ /// Saves the instance of the settings.
+ ///
+ public void SaveAsync()
+ {
+ EditorUtility.SetDirty(_instance);
+ }
+}
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinSettings.cs.meta b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinSettings.cs.meta
new file mode 100644
index 0000000..7e40d73
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinSettings.cs.meta
@@ -0,0 +1,14 @@
+fileFormatVersion: 2
+guid: ebc0ba1b5ef6b4a6b9dd53d7eadfea16
+labels:
+- al_max
+- al_max_export_path-MaxSdk/Scripts/IntegrationManager/Editor/AppLovinSettings.cs
+MonoImporter:
+ externalObjects: {}
+ serializedVersion: 2
+ defaultReferences: []
+ executionOrder: 0
+ icon: {instanceID: 0}
+ userData:
+ assetBundleName:
+ assetBundleVariant:
diff --git a/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinUpmManifest.cs b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinUpmManifest.cs
new file mode 100644
index 0000000..38aab79
--- /dev/null
+++ b/Assets/MaxSdk/Scripts/IntegrationManager/Editor/AppLovinUpmManifest.cs
@@ -0,0 +1,192 @@
+#if UNITY_2019_2_OR_NEWER
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using AppLovinMax.ThirdParty.MiniJson;
+
+namespace AppLovinMax.Scripts.IntegrationManager.Editor
+{
+ public class AppLovinUpmManifest
+ {
+ private const string KeyUrl = "url";
+ private const string KeyName = "name";
+ private const string KeyScopes = "scopes";
+ private const string KeyScopedRegistry = "scopedRegistries";
+
+ private Dictionary manifest;
+
+ private static string ManifestPath
+ {
+ get { return Path.Combine(Directory.GetCurrentDirectory(), "Packages/manifest.json"); }
+ }
+
+ // Private constructor to enforce the use of the Load() method
+ private AppLovinUpmManifest() { }
+
+ ///
+ /// Creates a new instance of AppLovinUpmManifest and loads the manifest.json file.
+ ///
+ /// An instance of AppLovinUpmManifest
+ public static AppLovinUpmManifest Load()
+ {
+ return new AppLovinUpmManifest { manifest = GetManifest() };
+ }
+
+ ///
+ /// Adds or updates a scoped registry in the manifest.
+ ///
+ /// The name of the registry
+ /// The url of the registry
+ /// The scopes of the registry
+ public void AddOrUpdateRegistry(string name, string url, List scopes)
+ {
+ var registry = GetRegistry(name);
+ if (registry == null)
+ {
+ var registries = GetRegistries();
+ if (registries == null) return;
+
+ registries.Add(new Dictionary
+ {
+ {KeyName, name},
+ {KeyUrl, url},
+ {KeyScopes, scopes}
+ });
+
+ return;
+ }
+
+ UpdateRegistry(registry, scopes);
+ }
+
+ ///
+ /// Saves the manifest by serializing it back to JSON and writing to file.
+ ///
+ public void Save()
+ {
+ var content = Json.Serialize(manifest, true);
+ File.WriteAllText(ManifestPath, content);
+ }
+
+ ///
+ /// Adds a dependency to the manifest.
+ ///
+ /// The name of the package to add
+ /// The version of the package to add
+ public void AddPackageDependency(string packageName, string version)
+ {
+ var manifestDependencies = GetDependencies();
+ manifestDependencies[packageName] = version;
+ }
+
+ ///
+ /// Removes a dependency from the manifest.
+ ///
+ /// The name of the package to remove
+ public void RemovePackageDependency(string packageName)
+ {
+ var manifestDependencies = GetDependencies();
+ manifestDependencies.Remove(packageName);
+ }
+
+ #region Utility
+
+ ///
+ /// Returns the manifest.json file as a dictionary.
+ ///
+ private static Dictionary GetManifest()
+ {
+ if (!File.Exists(ManifestPath))
+ {
+ throw new Exception("Manifest not Found!");
+ }
+
+ var manifestJson = File.ReadAllText(ManifestPath);
+ if (string.IsNullOrEmpty(manifestJson))
+ {
+ throw new Exception("Manifest is empty!");
+ }
+
+ var deserializedManifest = Json.Deserialize(manifestJson) as Dictionary;
+ if (deserializedManifest == null)
+ {
+ throw new Exception("Failed to deserialize manifest");
+ }
+
+ return deserializedManifest;
+ }
+
+ ///
+ /// Gets the manifest's dependencies section.
+ ///
+ /// The dependencies section of the manifest.
+ private Dictionary GetDependencies()
+ {
+ var dependencies = manifest["dependencies"] as Dictionary;
+ if (dependencies == null)
+ {
+ throw new Exception("No dependencies found in manifest.");
+ }
+
+ return dependencies;
+ }
+
+ ///
+ /// Gets the manifest's registries section. Creates a new registries section if one does not exist.
+ ///
+ /// The registries section of the manifest.
+ private List