using System; using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Text; using AppLovinMax.ThirdParty.MiniJson; using AppLovinMax.Internal; using UnityEngine; #if UNITY_IOS && !UNITY_EDITOR using System.Runtime.InteropServices; #endif public abstract class MaxSdkBase { // Shared Properties protected static readonly MaxUserSegment SharedUserSegment = new MaxUserSegment(); protected static readonly MaxTargetingData SharedTargetingData = new MaxTargetingData(); /// /// This enum represents the user's geography used to determine the type of consent flow shown to the user. /// public enum ConsentFlowUserGeography { /// /// User's geography is unknown. /// Unknown, /// /// The user is in GDPR region. /// Gdpr, /// /// The user is in a non-GDPR region. /// Other } #if UNITY_EDITOR || UNITY_IPHONE || UNITY_IOS /// /// App tracking status values. Primarily used in conjunction with iOS14's AppTrackingTransparency.framework. /// public enum AppTrackingStatus { /// /// Device is on < iOS14, AppTrackingTransparency.framework is not available. /// Unavailable, /// /// The value returned if a user has not yet received an authorization request to authorize access to app-related data that can be used for tracking the user or the device. /// NotDetermined, /// /// The value returned if authorization to access app-related data that can be used for tracking the user or the device is restricted. /// Restricted, /// /// The value returned if the user denies authorization to access app-related data that can be used for tracking the user or the device. /// Denied, /// /// The value returned if the user authorizes access to app-related data that can be used for tracking the user or the device. /// Authorized, } #endif public enum AdViewPosition { TopLeft, TopCenter, TopRight, Centered, CenterLeft, CenterRight, BottomLeft, BottomCenter, BottomRight } public enum BannerPosition { TopLeft, TopCenter, TopRight, Centered, CenterLeft, CenterRight, BottomLeft, BottomCenter, BottomRight } public class SdkConfiguration { /// /// Whether or not the SDK has been initialized successfully. /// public bool IsSuccessfullyInitialized { get; private set; } /// /// Get the country code for this user. /// public string CountryCode { get; private set; } #if UNITY_EDITOR || UNITY_IPHONE || UNITY_IOS /// /// App tracking status values. Primarily used in conjunction with iOS14's AppTrackingTransparency.framework. /// public AppTrackingStatus AppTrackingStatus { get; private set; } #endif public bool IsTestModeEnabled { get; private set; } /// /// Get the user's geography used to determine the type of consent flow shown to the user. /// If no such determination could be made, will be returned. /// public ConsentFlowUserGeography ConsentFlowUserGeography { get; private set; } [Obsolete("This API has been deprecated and will be removed in a future release.")] public ConsentDialogState ConsentDialogState { get; private set; } #if UNITY_EDITOR || !(UNITY_ANDROID || UNITY_IPHONE || UNITY_IOS) public static SdkConfiguration CreateEmpty() { var sdkConfiguration = new SdkConfiguration(); sdkConfiguration.IsSuccessfullyInitialized = true; #pragma warning disable 0618 sdkConfiguration.ConsentDialogState = ConsentDialogState.Unknown; #pragma warning restore 0618 #if UNITY_EDITOR sdkConfiguration.AppTrackingStatus = AppTrackingStatus.Authorized; #endif var currentRegion = RegionInfo.CurrentRegion; sdkConfiguration.CountryCode = currentRegion != null ? currentRegion.TwoLetterISORegionName : "US"; sdkConfiguration.IsTestModeEnabled = false; return sdkConfiguration; } #endif public static SdkConfiguration Create(IDictionary eventProps) { var sdkConfiguration = new SdkConfiguration(); sdkConfiguration.IsSuccessfullyInitialized = MaxSdkUtils.GetBoolFromDictionary(eventProps, "isSuccessfullyInitialized"); sdkConfiguration.CountryCode = MaxSdkUtils.GetStringFromDictionary(eventProps, "countryCode", ""); sdkConfiguration.IsTestModeEnabled = MaxSdkUtils.GetBoolFromDictionary(eventProps, "isTestModeEnabled"); var consentFlowUserGeographyStr = MaxSdkUtils.GetStringFromDictionary(eventProps, "consentFlowUserGeography", ""); if ("1".Equals(consentFlowUserGeographyStr)) { sdkConfiguration.ConsentFlowUserGeography = ConsentFlowUserGeography.Gdpr; } else if ("2".Equals(consentFlowUserGeographyStr)) { sdkConfiguration.ConsentFlowUserGeography = ConsentFlowUserGeography.Other; } else { sdkConfiguration.ConsentFlowUserGeography = ConsentFlowUserGeography.Unknown; } #pragma warning disable 0618 var consentDialogStateStr = MaxSdkUtils.GetStringFromDictionary(eventProps, "consentDialogState", ""); if ("1".Equals(consentDialogStateStr)) { sdkConfiguration.ConsentDialogState = ConsentDialogState.Applies; } else if ("2".Equals(consentDialogStateStr)) { sdkConfiguration.ConsentDialogState = ConsentDialogState.DoesNotApply; } else { sdkConfiguration.ConsentDialogState = ConsentDialogState.Unknown; } #pragma warning restore 0618 #if UNITY_IPHONE || UNITY_IOS var appTrackingStatusStr = MaxSdkUtils.GetStringFromDictionary(eventProps, "appTrackingStatus", "-1"); if ("-1".Equals(appTrackingStatusStr)) { sdkConfiguration.AppTrackingStatus = AppTrackingStatus.Unavailable; } else if ("0".Equals(appTrackingStatusStr)) { sdkConfiguration.AppTrackingStatus = AppTrackingStatus.NotDetermined; } else if ("1".Equals(appTrackingStatusStr)) { sdkConfiguration.AppTrackingStatus = AppTrackingStatus.Restricted; } else if ("2".Equals(appTrackingStatusStr)) { sdkConfiguration.AppTrackingStatus = AppTrackingStatus.Denied; } else // "3" is authorized { sdkConfiguration.AppTrackingStatus = AppTrackingStatus.Authorized; } #endif return sdkConfiguration; } } public struct Reward { public string Label; public int Amount; public override string ToString() { return "Reward: " + Amount + " " + Label; } public bool IsValid() { return !string.IsNullOrEmpty(Label) && Amount > 0; } } /** * This enum contains various error codes that the SDK can return when a MAX ad fails to load or display. */ public enum ErrorCode { /// /// This error code represents an error that could not be categorized into one of the other defined errors. See the message field in the error object for more details. /// Unspecified = -1, /// /// This error code indicates that MAX returned no eligible ads from any mediated networks for this app/device. /// NoFill = 204, /// /// This error code indicates that MAX returned eligible ads from mediated networks, but all ads failed to load. See the adLoadFailureInfo field in the error object for more details. /// AdLoadFailed = -5001, /// /// This error code represents an error that was encountered when showing an ad. /// AdDisplayFailed = -4205, /// /// This error code indicates that the ad request failed due to a generic network error. See the message field in the error object for more details. /// NetworkError = -1000, /// /// This error code indicates that the ad request timed out due to a slow internet connection. /// NetworkTimeout = -1001, /// /// This error code indicates that the ad request failed because the device is not connected to the internet. /// NoNetwork = -1009, /// /// This error code indicates that you attempted to show a fullscreen ad while another fullscreen ad is still showing. /// FullscreenAdAlreadyShowing = -23, /// /// This error code indicates you are attempting to show a fullscreen ad before the one has been loaded. /// FullscreenAdNotReady = -24, #if UNITY_ANDROID /// /// This error code indicates that the SDK failed to load an ad because it could not find the top Activity. /// NoActivity = -5601, /// /// This error code indicates that the SDK failed to display an ad because the user has the "Don't Keep Activities" developer setting enabled. /// DontKeepActivitiesEnabled = -5602, #endif } /** * This enum contains possible states of an ad in the waterfall the adapter response info could represent. */ public enum MaxAdLoadState { /// /// The AppLovin Max SDK did not attempt to load an ad from this network in the waterfall because an ad higher /// in the waterfall loaded successfully. /// AdLoadNotAttempted, /// /// An ad successfully loaded from this network. /// AdLoaded, /// /// An ad failed to load from this network. /// FailedToLoad } public class AdInfo { public string AdUnitIdentifier { get; private set; } public string AdFormat { get; private set; } public string NetworkName { get; private set; } public string NetworkPlacement { get; private set; } public string Placement { get; private set; } public string CreativeIdentifier { get; private set; } public double Revenue { get; private set; } public string RevenuePrecision { get; private set; } public WaterfallInfo WaterfallInfo { get; private set; } public long LatencyMillis { get; private set; } public string DspName { get; private set; } public AdInfo(IDictionary adInfoDictionary) { AdUnitIdentifier = MaxSdkUtils.GetStringFromDictionary(adInfoDictionary, "adUnitId"); AdFormat = MaxSdkUtils.GetStringFromDictionary(adInfoDictionary, "adFormat"); NetworkName = MaxSdkUtils.GetStringFromDictionary(adInfoDictionary, "networkName"); NetworkPlacement = MaxSdkUtils.GetStringFromDictionary(adInfoDictionary, "networkPlacement"); CreativeIdentifier = MaxSdkUtils.GetStringFromDictionary(adInfoDictionary, "creativeId"); Placement = MaxSdkUtils.GetStringFromDictionary(adInfoDictionary, "placement"); Revenue = MaxSdkUtils.GetDoubleFromDictionary(adInfoDictionary, "revenue", -1); RevenuePrecision = MaxSdkUtils.GetStringFromDictionary(adInfoDictionary, "revenuePrecision"); WaterfallInfo = new WaterfallInfo(MaxSdkUtils.GetDictionaryFromDictionary(adInfoDictionary, "waterfallInfo", new Dictionary())); LatencyMillis = MaxSdkUtils.GetLongFromDictionary(adInfoDictionary, "latencyMillis"); DspName = MaxSdkUtils.GetStringFromDictionary(adInfoDictionary, "dspName"); } public override string ToString() { return "[AdInfo adUnitIdentifier: " + AdUnitIdentifier + ", adFormat: " + AdFormat + ", networkName: " + NetworkName + ", networkPlacement: " + NetworkPlacement + ", creativeIdentifier: " + CreativeIdentifier + ", placement: " + Placement + ", revenue: " + Revenue + ", revenuePrecision: " + RevenuePrecision + ", latency: " + LatencyMillis + ", dspName: " + DspName + "]"; } } /// /// Returns information about the ad response in a waterfall. /// public class WaterfallInfo { public String Name { get; private set; } public String TestName { get; private set; } public List NetworkResponses { get; private set; } public long LatencyMillis { get; private set; } public WaterfallInfo(IDictionary waterfallInfoDict) { Name = MaxSdkUtils.GetStringFromDictionary(waterfallInfoDict, "name"); TestName = MaxSdkUtils.GetStringFromDictionary(waterfallInfoDict, "testName"); var networkResponsesList = MaxSdkUtils.GetListFromDictionary(waterfallInfoDict, "networkResponses", new List()); NetworkResponses = new List(); foreach (var networkResponseObject in networkResponsesList) { var networkResponseDict = networkResponseObject as Dictionary; if (networkResponseDict == null) continue; var networkResponse = new NetworkResponseInfo(networkResponseDict); NetworkResponses.Add(networkResponse); } LatencyMillis = MaxSdkUtils.GetLongFromDictionary(waterfallInfoDict, "latencyMillis"); } public override string ToString() { return "[MediatedNetworkInfo: name = " + Name + ", testName = " + TestName + ", latency = " + LatencyMillis + ", networkResponse = " + string.Join(", ", NetworkResponses.Select(networkResponseInfo => networkResponseInfo.ToString()).ToArray()) + "]"; } } public class NetworkResponseInfo { public MaxAdLoadState AdLoadState { get; private set; } public MediatedNetworkInfo MediatedNetwork { get; private set; } public Dictionary Credentials { get; private set; } public bool IsBidding { get; private set; } public long LatencyMillis { get; private set; } public ErrorInfo Error { get; private set; } public NetworkResponseInfo(IDictionary networkResponseInfoDict) { var mediatedNetworkInfoDict = MaxSdkUtils.GetDictionaryFromDictionary(networkResponseInfoDict, "mediatedNetwork"); MediatedNetwork = mediatedNetworkInfoDict != null ? new MediatedNetworkInfo(mediatedNetworkInfoDict) : null; Credentials = MaxSdkUtils.GetDictionaryFromDictionary(networkResponseInfoDict, "credentials", new Dictionary()); IsBidding = MaxSdkUtils.GetBoolFromDictionary(networkResponseInfoDict, "isBidding"); LatencyMillis = MaxSdkUtils.GetLongFromDictionary(networkResponseInfoDict, "latencyMillis"); AdLoadState = (MaxAdLoadState) MaxSdkUtils.GetIntFromDictionary(networkResponseInfoDict, "adLoadState"); var errorInfoDict = MaxSdkUtils.GetDictionaryFromDictionary(networkResponseInfoDict, "error"); Error = errorInfoDict != null ? new ErrorInfo(errorInfoDict) : null; } public override string ToString() { var stringBuilder = new StringBuilder("[NetworkResponseInfo: adLoadState = ").Append(AdLoadState); stringBuilder.Append(", mediatedNetwork = ").Append(MediatedNetwork); stringBuilder.Append(", credentials = ").Append(string.Join(", ", Credentials.Select(keyValuePair => keyValuePair.ToString()).ToArray())); switch (AdLoadState) { case MaxAdLoadState.FailedToLoad: stringBuilder.Append(", error = ").Append(Error); break; case MaxAdLoadState.AdLoaded: stringBuilder.Append(", latency = ").Append(LatencyMillis); break; } return stringBuilder.Append("]").ToString(); } } public class MediatedNetworkInfo { public string Name { get; private set; } public string AdapterClassName { get; private set; } public string AdapterVersion { get; private set; } public string SdkVersion { get; private set; } public MediatedNetworkInfo(IDictionary mediatedNetworkDictionary) { // NOTE: Unity Editor creates empty string Name = MaxSdkUtils.GetStringFromDictionary(mediatedNetworkDictionary, "name", ""); AdapterClassName = MaxSdkUtils.GetStringFromDictionary(mediatedNetworkDictionary, "adapterClassName", ""); AdapterVersion = MaxSdkUtils.GetStringFromDictionary(mediatedNetworkDictionary, "adapterVersion", ""); SdkVersion = MaxSdkUtils.GetStringFromDictionary(mediatedNetworkDictionary, "sdkVersion", ""); } public override string ToString() { return "[MediatedNetworkInfo name: " + Name + ", adapterClassName: " + AdapterClassName + ", adapterVersion: " + AdapterVersion + ", sdkVersion: " + SdkVersion + "]"; } } public class ErrorInfo { public ErrorCode Code { get; private set; } public string Message { get; private set; } public int MediatedNetworkErrorCode { get; private set; } public string MediatedNetworkErrorMessage { get; private set; } public string AdLoadFailureInfo { get; private set; } public WaterfallInfo WaterfallInfo { get; private set; } public long LatencyMillis { get; private set; } public ErrorInfo(IDictionary errorInfoDictionary) { Code = (ErrorCode) MaxSdkUtils.GetIntFromDictionary(errorInfoDictionary, "errorCode", -1); Message = MaxSdkUtils.GetStringFromDictionary(errorInfoDictionary, "errorMessage", ""); MediatedNetworkErrorCode = MaxSdkUtils.GetIntFromDictionary(errorInfoDictionary, "mediatedNetworkErrorCode", (int) ErrorCode.Unspecified); MediatedNetworkErrorMessage = MaxSdkUtils.GetStringFromDictionary(errorInfoDictionary, "mediatedNetworkErrorMessage", ""); AdLoadFailureInfo = MaxSdkUtils.GetStringFromDictionary(errorInfoDictionary, "adLoadFailureInfo", ""); WaterfallInfo = new WaterfallInfo(MaxSdkUtils.GetDictionaryFromDictionary(errorInfoDictionary, "waterfallInfo", new Dictionary())); LatencyMillis = MaxSdkUtils.GetLongFromDictionary(errorInfoDictionary, "latencyMillis"); } public override string ToString() { var stringbuilder = new StringBuilder("[ErrorInfo code: ").Append(Code); stringbuilder.Append(", message: ").Append(Message); if (Code == ErrorCode.AdDisplayFailed) { stringbuilder.Append(", mediatedNetworkCode: ").Append(MediatedNetworkErrorCode); stringbuilder.Append(", mediatedNetworkMessage: ").Append(MediatedNetworkErrorMessage); } stringbuilder.Append(", latency: ").Append(LatencyMillis); return stringbuilder.Append(", adLoadFailureInfo: ").Append(AdLoadFailureInfo).Append("]").ToString(); } } /// /// Inset values for the safe area on the screen used to render banner ads. /// public class SafeAreaInsets { public int Left { get; private set; } public int Top { get; private set; } public int Right { get; private set; } public int Bottom { get; private set; } /// /// Creates a new instance of . /// /// An integer array with insets values in the order of left, top, right, and bottom internal SafeAreaInsets(int[] insets) { Left = insets[0]; Top = insets[1]; Right = insets[2]; Bottom = insets[3]; } public override string ToString() { return "[SafeAreaInsets: Left: " + Left + ", Top: " + Top + ", Right: " + Right + ", Bottom: " + Bottom + "]"; } } /// /// Determines whether ad events raised by the AppLovin's Unity plugin should be invoked on the Unity main thread. /// public static bool? InvokeEventsOnUnityMainThread { get; set; } /// /// The CMP service, which provides direct APIs for interfacing with the Google-certified CMP installed, if any. /// public static MaxCmpService CmpService { get { return MaxCmpService.Instance; } } protected static void ValidateAdUnitIdentifier(string adUnitIdentifier, string debugPurpose) { if (string.IsNullOrEmpty(adUnitIdentifier)) { MaxSdkLogger.UserError("No MAX Ads Ad Unit ID specified for: " + debugPurpose); } } // Allocate the MaxEventExecutor singleton which handles pushing callbacks from the background to the main thread. protected static void InitializeEventExecutor() { MaxEventExecutor.InitializeIfNeeded(); } /// /// Generates serialized Unity meta data to be passed to the SDK. /// /// Serialized Unity meta data. protected static string GenerateMetaData() { var metaData = new Dictionary(2); metaData.Add("UnityVersion", Application.unityVersion); var graphicsMemorySize = SystemInfo.graphicsMemorySize; metaData.Add("GraphicsMemorySizeMegabytes", graphicsMemorySize.ToString()); return Json.Serialize(metaData); } /// /// Parses the prop string provided to a . /// /// A prop string representing a Rect /// A the prop string represents. protected static Rect GetRectFromString(string rectPropString) { var rectDict = Json.Deserialize(rectPropString) as Dictionary; var originX = MaxSdkUtils.GetFloatFromDictionary(rectDict, "origin_x", 0); var originY = MaxSdkUtils.GetFloatFromDictionary(rectDict, "origin_y", 0); var width = MaxSdkUtils.GetFloatFromDictionary(rectDict, "width", 0); var height = MaxSdkUtils.GetFloatFromDictionary(rectDict, "height", 0); return new Rect(originX, originY, width, height); } /// /// Handles forwarding callbacks from native to C#. /// /// A prop string with the event data protected static void HandleBackgroundCallback(string propsStr) { try { MaxSdkCallbacks.ForwardEvent(propsStr); } catch (Exception exception) { var eventProps = Json.Deserialize(propsStr) as Dictionary; if (eventProps == null) return; var eventName = MaxSdkUtils.GetStringFromDictionary(eventProps, "name", ""); MaxSdkLogger.UserError("Unable to notify ad delegate due to an error in the publisher callback '" + eventName + "' due to exception: " + exception.Message); Debug.LogException(exception); } } protected static string SerializeLocalExtraParameterValue(object value) { if (!(value.GetType().IsPrimitive || value is string || value is IList || value is IDictionary)) { MaxSdkLogger.UserError("Local extra parameters must be an IList, IDictionary, string, or a primitive type"); return ""; } Dictionary data = new Dictionary { {"value", value} }; return Json.Serialize(data); } [Obsolete("This API has been deprecated and will be removed in a future release.")] public enum ConsentDialogState { Unknown, Applies, DoesNotApply } } /// /// An extension class for and enums. /// internal static class AdPositionExtenstion { public static string ToSnakeCaseString(this MaxSdkBase.BannerPosition position) { if (position == MaxSdkBase.BannerPosition.TopLeft) { return "top_left"; } else if (position == MaxSdkBase.BannerPosition.TopCenter) { return "top_center"; } else if (position == MaxSdkBase.BannerPosition.TopRight) { return "top_right"; } else if (position == MaxSdkBase.BannerPosition.Centered) { return "centered"; } else if (position == MaxSdkBase.BannerPosition.CenterLeft) { return "center_left"; } else if (position == MaxSdkBase.BannerPosition.CenterRight) { return "center_right"; } else if (position == MaxSdkBase.BannerPosition.BottomLeft) { return "bottom_left"; } else if (position == MaxSdkBase.BannerPosition.BottomCenter) { return "bottom_center"; } else // position == MaxSdkBase.BannerPosition.BottomRight { return "bottom_right"; } } public static string ToSnakeCaseString(this MaxSdkBase.AdViewPosition position) { if (position == MaxSdkBase.AdViewPosition.TopLeft) { return "top_left"; } else if (position == MaxSdkBase.AdViewPosition.TopCenter) { return "top_center"; } else if (position == MaxSdkBase.AdViewPosition.TopRight) { return "top_right"; } else if (position == MaxSdkBase.AdViewPosition.Centered) { return "centered"; } else if (position == MaxSdkBase.AdViewPosition.CenterLeft) { return "center_left"; } else if (position == MaxSdkBase.AdViewPosition.CenterRight) { return "center_right"; } else if (position == MaxSdkBase.AdViewPosition.BottomLeft) { return "bottom_left"; } else if (position == MaxSdkBase.AdViewPosition.BottomCenter) { return "bottom_center"; } else // position == MaxSdkBase.AdViewPosition.BottomRight { return "bottom_right"; } } } namespace AppLovinMax.Internal.API { [Obsolete("This class has been deprecated and will be removed in a future SDK release.")] public class CFError { public int Code { get; private set; } public string Message { get; private set; } public static CFError Create(int code = -1, string message = "") { return new CFError(code, message); } private CFError(int code, string message) { Code = code; Message = message; } public override string ToString() { return "[CFError Code: " + Code + ", Message: " + Message + "]"; } } [Obsolete("This enum has been deprecated. Please use `MaxSdk.GetSdkConfiguration().ConsentFlowUserGeography` instead.")] public enum CFType { Unknown, Standard, Detailed } public class CFService { [Obsolete("This property has been deprecated. Please use `MaxSdk.GetSdkConfiguration().ConsentFlowUserGeography` instead.")] public static CFType CFType { get { switch (MaxSdk.GetSdkConfiguration().ConsentFlowUserGeography) { case MaxSdkBase.ConsentFlowUserGeography.Unknown: return CFType.Unknown; case MaxSdkBase.ConsentFlowUserGeography.Gdpr: return CFType.Detailed; case MaxSdkBase.ConsentFlowUserGeography.Other: return CFType.Standard; default: throw new ArgumentOutOfRangeException(); } } } [Obsolete("This method has been deprecated. Please use `MaxSdk.CmpService.ShowCmpForExistingUser` instead.")] public static void SCF(Action onFlowCompletedAction) { MaxSdkBase.CmpService.ShowCmpForExistingUser(error => { if (onFlowCompletedAction == null) return; var cfError = error == null ? null : CFError.Create((int) error.Code, error.Message); onFlowCompletedAction(cfError); }); } } }