MaxSdkUtils.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Globalization;
  4. using System.IO;
  5. using System.Linq;
  6. using System.Runtime.InteropServices;
  7. using System.Text;
  8. using AppLovinMax.ThirdParty.MiniJson;
  9. using UnityEngine;
  10. #if UNITY_EDITOR
  11. using UnityEditor;
  12. #endif
  13. public class MaxSdkUtils
  14. {
  15. /// <summary>
  16. /// An Enum to be used when comparing two versions.
  17. ///
  18. /// If:
  19. /// A &lt; B return <see cref="Lesser"/>
  20. /// A == B return <see cref="Equal"/>
  21. /// A &gt; B return <see cref="Greater"/>
  22. /// </summary>
  23. public enum VersionComparisonResult
  24. {
  25. Lesser = -1,
  26. Equal = 0,
  27. Greater = 1
  28. }
  29. #if UNITY_ANDROID && !UNITY_EDITOR
  30. private static readonly AndroidJavaClass MaxUnityPluginClass = new AndroidJavaClass("com.applovin.mediation.unity.MaxUnityPlugin");
  31. #endif
  32. #if UNITY_IOS
  33. [DllImport("__Internal")]
  34. private static extern float _MaxGetAdaptiveBannerHeight(float width);
  35. #endif
  36. /// <summary>
  37. /// Get the adaptive banner size for the provided width.
  38. /// If the width is not provided, will assume full screen width for the current orientation.
  39. ///
  40. /// NOTE: Only AdMob / Google Ad Manager currently has support for adaptive banners and the maximum height is 15% the height of the screen.
  41. /// </summary>
  42. ///
  43. /// <param name="width">The width to retrieve the adaptive banner height for.</param>
  44. /// <returns>The adaptive banner height for the current orientation and width.</returns>
  45. public static float GetAdaptiveBannerHeight(float width = -1.0f)
  46. {
  47. #if UNITY_EDITOR
  48. return 50.0f;
  49. #elif UNITY_IOS
  50. return _MaxGetAdaptiveBannerHeight(width);
  51. #elif UNITY_ANDROID
  52. return MaxUnityPluginClass.CallStatic<float>("getAdaptiveBannerHeight", width);
  53. #else
  54. return -1.0f;
  55. #endif
  56. }
  57. /// <summary>
  58. /// Tries to get a dictionary for the given key if available, returns the default value if unavailable.
  59. /// </summary>
  60. /// <param name="dictionary">The dictionary from which to get the dictionary</param>
  61. /// <param name="key">The key to be used to retrieve the dictionary</param>
  62. /// <param name="defaultValue">The default value to be returned when a value for the given key is not found.</param>
  63. /// <returns>The dictionary for the given key if available, the default value otherwise.</returns>
  64. public static Dictionary<string, object> GetDictionaryFromDictionary(IDictionary<string, object> dictionary, string key, Dictionary<string, object> defaultValue = null)
  65. {
  66. if (dictionary == null) return defaultValue;
  67. object value;
  68. if (dictionary.TryGetValue(key, out value) && value is Dictionary<string, object>)
  69. {
  70. return value as Dictionary<string, object>;
  71. }
  72. return defaultValue;
  73. }
  74. /// <summary>
  75. /// Tries to get a list from the dictionary for the given key if available, returns the default value if unavailable.
  76. /// </summary>
  77. /// <param name="dictionary">The dictionary from which to get the list</param>
  78. /// <param name="key">The key to be used to retrieve the list</param>
  79. /// <param name="defaultValue">The default value to be returned when a value for the given key is not found.</param>
  80. /// <returns>The list for the given key if available, the default value otherwise.</returns>
  81. public static List<object> GetListFromDictionary(IDictionary<string, object> dictionary, string key, List<object> defaultValue = null)
  82. {
  83. if (dictionary == null) return defaultValue;
  84. object value;
  85. if (dictionary.TryGetValue(key, out value) && value is List<object>)
  86. {
  87. return value as List<object>;
  88. }
  89. return defaultValue;
  90. }
  91. /// <summary>
  92. /// Tries to get a <c>string</c> value from dictionary for the given key if available, returns the default value if unavailable.
  93. /// </summary>
  94. /// <param name="dictionary">The dictionary from which to get the <c>string</c> value.</param>
  95. /// <param name="key">The key to be used to retrieve the <c>string</c> value.</param>
  96. /// <param name="defaultValue">The default value to be returned when a value for the given key is not found.</param>
  97. /// <returns>The <c>string</c> value from the dictionary if available, the default value otherwise.</returns>
  98. public static string GetStringFromDictionary(IDictionary<string, object> dictionary, string key, string defaultValue = "")
  99. {
  100. if (dictionary == null) return defaultValue;
  101. object value;
  102. if (dictionary.TryGetValue(key, out value) && value != null)
  103. {
  104. return value.ToString();
  105. }
  106. return defaultValue;
  107. }
  108. /// <summary>
  109. /// Tries to get a <c>bool</c> value from dictionary for the given key if available, returns the default value if unavailable.
  110. /// </summary>
  111. /// <param name="dictionary">The dictionary from which to get the <c>bool</c> value.</param>
  112. /// <param name="key">The key to be used to retrieve the <c>bool</c> value.</param>
  113. /// <param name="defaultValue">The default value to be returned when a <c>bool</c> value for the given key is not found.</param>
  114. /// <returns>The <c>bool</c> value from the dictionary if available, the default value otherwise.</returns>
  115. public static bool GetBoolFromDictionary(IDictionary<string, object> dictionary, string key, bool defaultValue = false)
  116. {
  117. if (dictionary == null) return defaultValue;
  118. object obj;
  119. bool value;
  120. if (dictionary.TryGetValue(key, out obj) && obj != null && bool.TryParse(obj.ToString(), out value))
  121. {
  122. return value;
  123. }
  124. return defaultValue;
  125. }
  126. /// <summary>
  127. /// Tries to get a <c>int</c> value from dictionary for the given key if available, returns the default value if unavailable.
  128. /// </summary>
  129. /// <param name="dictionary">The dictionary from which to get the <c>int</c> value.</param>
  130. /// <param name="key">The key to be used to retrieve the <c>int</c> value.</param>
  131. /// <param name="defaultValue">The default value to be returned when a <c>int</c> value for the given key is not found.</param>
  132. /// <returns>The <c>int</c> value from the dictionary if available, the default value otherwise.</returns>
  133. public static int GetIntFromDictionary(IDictionary<string, object> dictionary, string key, int defaultValue = 0)
  134. {
  135. if (dictionary == null) return defaultValue;
  136. object obj;
  137. int value;
  138. if (dictionary.TryGetValue(key, out obj) &&
  139. obj != null &&
  140. int.TryParse(InvariantCultureToString(obj), NumberStyles.Any, CultureInfo.InvariantCulture, out value))
  141. {
  142. return value;
  143. }
  144. return defaultValue;
  145. }
  146. /// <summary>
  147. /// Tries to get a <c>long</c> value from dictionary for the given key if available, returns the default value if unavailable.
  148. /// </summary>
  149. /// <param name="dictionary">The dictionary from which to get the <c>long</c> value.</param>
  150. /// <param name="key">The key to be used to retrieve the <c>long</c> value.</param>
  151. /// <param name="defaultValue">The default value to be returned when a <c>long</c> value for the given key is not found.</param>
  152. /// <returns>The <c>long</c> value from the dictionary if available, the default value otherwise.</returns>
  153. public static long GetLongFromDictionary(IDictionary<string, object> dictionary, string key, long defaultValue = 0L)
  154. {
  155. if (dictionary == null) return defaultValue;
  156. object obj;
  157. long value;
  158. if (dictionary.TryGetValue(key, out obj) &&
  159. obj != null &&
  160. long.TryParse(InvariantCultureToString(obj), NumberStyles.Any, CultureInfo.InvariantCulture, out value))
  161. {
  162. return value;
  163. }
  164. return defaultValue;
  165. }
  166. /// <summary>
  167. /// Tries to get a <c>float</c> value from dictionary for the given key if available, returns the default value if unavailable.
  168. /// </summary>
  169. /// <param name="dictionary">The dictionary from which to get the <c>float</c> value.</param>
  170. /// <param name="key">The key to be used to retrieve the <c>float</c> value.</param>
  171. /// <param name="defaultValue">The default value to be returned when a <c>string</c> value for the given key is not found.</param>
  172. /// <returns>The <c>float</c> value from the dictionary if available, the default value otherwise.</returns>
  173. public static float GetFloatFromDictionary(IDictionary<string, object> dictionary, string key, float defaultValue = 0F)
  174. {
  175. if (dictionary == null) return defaultValue;
  176. object obj;
  177. float value;
  178. if (dictionary.TryGetValue(key, out obj) &&
  179. obj != null &&
  180. float.TryParse(InvariantCultureToString(obj), NumberStyles.Any, CultureInfo.InvariantCulture, out value))
  181. {
  182. return value;
  183. }
  184. return defaultValue;
  185. }
  186. /// <summary>
  187. /// Tries to get a <c>double</c> value from dictionary for the given key if available, returns the default value if unavailable.
  188. /// </summary>
  189. /// <param name="dictionary">The dictionary from which to get the <c>double</c> value.</param>
  190. /// <param name="key">The key to be used to retrieve the <c>double</c> value.</param>
  191. /// <param name="defaultValue">The default value to be returned when a <c>double</c> value for the given key is not found.</param>
  192. /// <returns>The <c>double</c> value from the dictionary if available, the default value otherwise.</returns>
  193. public static double GetDoubleFromDictionary(IDictionary<string, object> dictionary, string key, int defaultValue = 0)
  194. {
  195. if (dictionary == null) return defaultValue;
  196. object obj;
  197. double value;
  198. if (dictionary.TryGetValue(key, out obj) &&
  199. obj != null &&
  200. double.TryParse(InvariantCultureToString(obj), NumberStyles.Any, CultureInfo.InvariantCulture, out value))
  201. {
  202. return value;
  203. }
  204. return defaultValue;
  205. }
  206. /// <summary>
  207. /// Converts the given object to a string without locale specific conversions.
  208. /// </summary>
  209. public static string InvariantCultureToString(object obj)
  210. {
  211. return string.Format(CultureInfo.InvariantCulture, "{0}", obj);
  212. }
  213. /// <summary>
  214. /// The native iOS and Android plugins forward JSON arrays of JSON Objects.
  215. /// </summary>
  216. public static List<T> PropsStringsToList<T>(string str)
  217. {
  218. var result = new List<T>();
  219. if (string.IsNullOrEmpty(str)) return result;
  220. var infoArray = Json.Deserialize(str) as List<object>;
  221. if (infoArray == null) return result;
  222. foreach (var infoObject in infoArray)
  223. {
  224. var dictionary = infoObject as Dictionary<string, object>;
  225. if (dictionary == null) continue;
  226. // Dynamically construct generic type with string argument.
  227. // The type T must have a constructor that creates a new object from an info string, i.e., new T(infoString)
  228. var instance = (T) Activator.CreateInstance(typeof(T), dictionary);
  229. result.Add(instance);
  230. }
  231. return result;
  232. }
  233. /// <summary>
  234. /// Returns the hexidecimal color code string for the given Color.
  235. /// </summary>
  236. public static String ParseColor(Color color)
  237. {
  238. int a = (int) (Mathf.Clamp01(color.a) * Byte.MaxValue);
  239. int r = (int) (Mathf.Clamp01(color.r) * Byte.MaxValue);
  240. int g = (int) (Mathf.Clamp01(color.g) * Byte.MaxValue);
  241. int b = (int) (Mathf.Clamp01(color.b) * Byte.MaxValue);
  242. return BitConverter.ToString(new[]
  243. {
  244. Convert.ToByte(a),
  245. Convert.ToByte(r),
  246. Convert.ToByte(g),
  247. Convert.ToByte(b),
  248. }).Replace("-", "").Insert(0, "#");
  249. }
  250. #if UNITY_IOS
  251. [DllImport("__Internal")]
  252. private static extern bool _MaxIsTablet();
  253. #endif
  254. /// <summary>
  255. /// Returns whether or not the device is a tablet.
  256. /// </summary>
  257. public static bool IsTablet()
  258. {
  259. #if UNITY_EDITOR
  260. return false;
  261. #elif UNITY_IOS
  262. return _MaxIsTablet();
  263. #elif UNITY_ANDROID
  264. return MaxUnityPluginClass.CallStatic<bool>("isTablet");
  265. #else
  266. return false;
  267. #endif
  268. }
  269. #if UNITY_IOS
  270. [DllImport("__Internal")]
  271. private static extern bool _MaxIsPhysicalDevice();
  272. #endif
  273. /// <summary>
  274. /// Returns whether or not a physical device is being used, as opposed to an emulator / simulator.
  275. /// </summary>
  276. public static bool IsPhysicalDevice()
  277. {
  278. #if UNITY_EDITOR
  279. return false;
  280. #elif UNITY_IOS
  281. return _MaxIsPhysicalDevice();
  282. #elif UNITY_ANDROID
  283. return MaxUnityPluginClass.CallStatic<bool>("isPhysicalDevice");
  284. #else
  285. return false;
  286. #endif
  287. }
  288. #if UNITY_IOS
  289. [DllImport("__Internal")]
  290. private static extern float _MaxScreenDensity();
  291. #endif
  292. /// <summary>
  293. /// Returns the screen density.
  294. /// </summary>
  295. public static float GetScreenDensity()
  296. {
  297. #if UNITY_EDITOR
  298. return 1;
  299. #elif UNITY_IOS
  300. return _MaxScreenDensity();
  301. #elif UNITY_ANDROID
  302. return MaxUnityPluginClass.CallStatic<float>("getScreenDensity");
  303. #else
  304. return -1;
  305. #endif
  306. }
  307. /// <summary>
  308. /// Parses the IABTCF_VendorConsents string to determine the consent status of the IAB vendor with the provided ID.
  309. /// NOTE: Must be called after AppLovin MAX SDK has been initialized.
  310. /// </summary>
  311. /// <param name="vendorId">Vendor ID as defined in the Global Vendor List.</param>
  312. /// <returns><c>true</c> if the vendor has consent, <c>false</c> if not, or <c>null</c> if TC data is not available on disk.</returns>
  313. /// <see href="https://vendor-list.consensu.org/v3/vendor-list.json">Current Version of Global Vendor List</see>
  314. public static bool? GetTcfConsentStatus(int vendorId)
  315. {
  316. var tcfConsentStatus = GetPlatformSpecificTcfConsentStatus(vendorId);
  317. return GetConsentStatusValue(tcfConsentStatus);
  318. }
  319. #if UNITY_IOS
  320. [DllImport("__Internal")]
  321. private static extern int _MaxGetTcfVendorConsentStatus(int vendorIdentifier);
  322. #endif
  323. private static int GetPlatformSpecificTcfConsentStatus(int vendorId)
  324. {
  325. #if UNITY_EDITOR
  326. return -1;
  327. #elif UNITY_IOS
  328. return _MaxGetTcfVendorConsentStatus(vendorId);
  329. #elif UNITY_ANDROID
  330. return MaxUnityPluginClass.CallStatic<int>("getTcfVendorConsentStatus", vendorId);
  331. #else
  332. return -1;
  333. #endif
  334. }
  335. /// <summary>
  336. /// Parses the IABTCF_AddtlConsent string to determine the consent status of the advertising entity with the provided Ad Technology Provider (ATP) ID.
  337. /// NOTE: Must be called after AppLovin MAX SDK has been initialized.
  338. /// </summary>
  339. /// <param name="atpId">ATP ID of the advertising entity (e.g. 89 for Meta Audience Network).</param>
  340. /// <returns>
  341. /// <c>true</c> if the advertising entity has consent, <c>false</c> if not, or <c>null</c> if no AC string is available on disk or the ATP network was not listed in the CMP flow.
  342. /// </returns>
  343. /// <see href="https://support.google.com/admanager/answer/9681920">Google’s Additional Consent Mode technical specification</see>
  344. /// <see href="https://storage.googleapis.com/tcfac/additional-consent-providers.csv">List of Google ATPs and their IDs</see>
  345. public static bool? GetAdditionalConsentStatus(int atpId)
  346. {
  347. var additionalConsentStatus = GetPlatformSpecificAdditionalConsentStatus(atpId);
  348. return GetConsentStatusValue(additionalConsentStatus);
  349. }
  350. #if UNITY_IOS
  351. [DllImport("__Internal")]
  352. private static extern int _MaxGetAdditionalConsentStatus(int atpIdentifier);
  353. #endif
  354. private static int GetPlatformSpecificAdditionalConsentStatus(int atpId)
  355. {
  356. #if UNITY_EDITOR
  357. return -1;
  358. #elif UNITY_IOS
  359. return _MaxGetAdditionalConsentStatus(atpId);
  360. #elif UNITY_ANDROID
  361. return MaxUnityPluginClass.CallStatic<int>("getAdditionalConsentStatus", atpId);
  362. #else
  363. return -1;
  364. #endif
  365. }
  366. /// <summary>
  367. /// Parses the IABTCF_PurposeConsents String to determine the consent status of the IAB defined data processing purpose.
  368. /// NOTE: Must be called after AppLovin MAX SDK has been initialized.
  369. /// </summary>
  370. /// <param name="purposeId">Purpose ID.</param>
  371. /// <returns><c>true</c> if the purpose has consent, <c>false</c> if not, or <c>null</c> if TC data is not available on disk.</returns>
  372. /// <see href="https://storage.googleapis.com/tcfac/additional-consent-providers.csv">see IAB Europe Transparency and Consent Framework Policies (Appendix A) for purpose definitions.</see>
  373. public static bool? GetPurposeConsentStatus(int purposeId)
  374. {
  375. var purposeConsentStatus = GetPlatformSpecificPurposeConsentStatus(purposeId);
  376. return GetConsentStatusValue(purposeConsentStatus);
  377. }
  378. #if UNITY_IOS
  379. [DllImport("__Internal")]
  380. private static extern int _MaxGetPurposeConsentStatus(int purposeIdentifier);
  381. #endif
  382. private static int GetPlatformSpecificPurposeConsentStatus(int purposeId)
  383. {
  384. #if UNITY_EDITOR
  385. return -1;
  386. #elif UNITY_IOS
  387. return _MaxGetPurposeConsentStatus(purposeId);
  388. #elif UNITY_ANDROID
  389. return MaxUnityPluginClass.CallStatic<int>("getPurposeConsentStatus", purposeId);
  390. #else
  391. return -1;
  392. #endif
  393. }
  394. /// <summary>
  395. /// Parses the IABTCF_SpecialFeaturesOptIns String to determine the opt-in status of the IAB defined special feature.
  396. /// NOTE: Must be called after AppLovin MAX SDK has been initialized.
  397. /// </summary>
  398. /// <param name="specialFeatureId">Special feature ID.</param>
  399. /// <returns><c>true</c> if the user opted in for the special feature, <c>false</c> if not, or <c>null</c> if TC data is not available on disk.</returns>
  400. /// <see href="https://iabeurope.eu/iab-europe-transparency-consent-framework-policies">IAB Europe Transparency and Consent Framework Policies (Appendix A) for special features </see>
  401. public static bool? GetSpecialFeatureOptInStatus(int specialFeatureId)
  402. {
  403. var specialFeatureOptInStatus = GetPlatformSpecificSpecialFeatureOptInStatus(specialFeatureId);
  404. return GetConsentStatusValue(specialFeatureOptInStatus);
  405. }
  406. #if UNITY_IOS
  407. [DllImport("__Internal")]
  408. private static extern int _MaxGetSpecialFeatureOptInStatus(int specialFeatureIdentifier);
  409. #endif
  410. private static int GetPlatformSpecificSpecialFeatureOptInStatus(int specialFeatureId)
  411. {
  412. #if UNITY_EDITOR
  413. return -1;
  414. #elif UNITY_IOS
  415. return _MaxGetSpecialFeatureOptInStatus(specialFeatureId);
  416. #elif UNITY_ANDROID
  417. return MaxUnityPluginClass.CallStatic<int>("getSpecialFeatureOptInStatus", specialFeatureId);
  418. #else
  419. return -1;
  420. #endif
  421. }
  422. private static bool? GetConsentStatusValue(int consentStatus)
  423. {
  424. if (consentStatus == -1)
  425. {
  426. return null;
  427. }
  428. else
  429. {
  430. return consentStatus == 1;
  431. }
  432. }
  433. /// <summary>
  434. /// Compares its two arguments for order. Returns <see cref="VersionComparisonResult.Lesser"/>, <see cref="VersionComparisonResult.Equal"/>,
  435. /// or <see cref="VersionComparisonResult.Greater"/> as the first version is less than, equal to, or greater than the second.
  436. /// </summary>
  437. /// <param name="versionA">The first version to be compared.</param>
  438. /// <param name="versionB">The second version to be compared.</param>
  439. /// <returns>
  440. /// <see cref="VersionComparisonResult.Lesser"/> if versionA is less than versionB.
  441. /// <see cref="VersionComparisonResult.Equal"/> if versionA and versionB are equal.
  442. /// <see cref="VersionComparisonResult.Greater"/> if versionA is greater than versionB.
  443. /// </returns>
  444. public static VersionComparisonResult CompareVersions(string versionA, string versionB)
  445. {
  446. if (versionA.Equals(versionB)) return VersionComparisonResult.Equal;
  447. // Check if either of the versions are beta versions. Beta versions could be of format x.y.z-beta or x.y.z-betaX.
  448. // Split the version string into beta component and the underlying version.
  449. int piece;
  450. var isVersionABeta = versionA.Contains("-beta");
  451. var versionABetaNumber = 0;
  452. if (isVersionABeta)
  453. {
  454. var components = versionA.Split(new[] {"-beta"}, StringSplitOptions.None);
  455. versionA = components[0];
  456. versionABetaNumber = int.TryParse(components[1], out piece) ? piece : 0;
  457. }
  458. var isVersionBBeta = versionB.Contains("-beta");
  459. var versionBBetaNumber = 0;
  460. if (isVersionBBeta)
  461. {
  462. var components = versionB.Split(new[] {"-beta"}, StringSplitOptions.None);
  463. versionB = components[0];
  464. versionBBetaNumber = int.TryParse(components[1], out piece) ? piece : 0;
  465. }
  466. // Now that we have separated the beta component, check if the underlying versions are the same.
  467. if (versionA.Equals(versionB))
  468. {
  469. // The versions are the same, compare the beta components.
  470. if (isVersionABeta && isVersionBBeta)
  471. {
  472. if (versionABetaNumber < versionBBetaNumber) return VersionComparisonResult.Lesser;
  473. if (versionABetaNumber > versionBBetaNumber) return VersionComparisonResult.Greater;
  474. }
  475. // Only VersionA is beta, so A is older.
  476. else if (isVersionABeta)
  477. {
  478. return VersionComparisonResult.Lesser;
  479. }
  480. // Only VersionB is beta, A is newer.
  481. else
  482. {
  483. return VersionComparisonResult.Greater;
  484. }
  485. }
  486. // Compare the non beta component of the version string.
  487. var versionAComponents = versionA.Split('.').Select(version => int.TryParse(version, out piece) ? piece : 0).ToArray();
  488. var versionBComponents = versionB.Split('.').Select(version => int.TryParse(version, out piece) ? piece : 0).ToArray();
  489. var length = Mathf.Max(versionAComponents.Length, versionBComponents.Length);
  490. for (var i = 0; i < length; i++)
  491. {
  492. var aComponent = i < versionAComponents.Length ? versionAComponents[i] : 0;
  493. var bComponent = i < versionBComponents.Length ? versionBComponents[i] : 0;
  494. if (aComponent < bComponent) return VersionComparisonResult.Lesser;
  495. if (aComponent > bComponent) return VersionComparisonResult.Greater;
  496. }
  497. return VersionComparisonResult.Equal;
  498. }
  499. /// <summary>
  500. /// Check if the given string is valid - not <c>null</c> and not empty.
  501. /// </summary>
  502. /// <param name="toCheck">The string to be checked.</param>
  503. /// <returns><c>true</c> if the given string is not <c>null</c> and not empty.</returns>
  504. public static bool IsValidString(string toCheck)
  505. {
  506. return !string.IsNullOrEmpty(toCheck);
  507. }
  508. #if UNITY_EDITOR
  509. /// <summary>
  510. /// Gets the path of the asset in the project for a given MAX plugin export path.
  511. /// </summary>
  512. /// <param name="exportPath">The actual exported path of the asset.</param>
  513. /// <returns>The exported path of the MAX plugin asset or the default export path if the asset is not found.</returns>
  514. public static string GetAssetPathForExportPath(string exportPath)
  515. {
  516. var defaultPath = Path.Combine("Assets", exportPath);
  517. var assetLabelToFind = "l:al_max_export_path-" + exportPath.Replace(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar);
  518. var assetGuids = AssetDatabase.FindAssets(assetLabelToFind);
  519. return assetGuids.Length < 1 ? defaultPath : AssetDatabase.GUIDToAssetPath(assetGuids[0]);
  520. }
  521. #endif
  522. }