update: 完善push图标生成工具
							parent
							
								
									794673c272
								
							
						
					
					
						commit
						9d08938d77
					
				|  | @ -1,2 +1,2 @@ | |||
| target=android-9 | ||||
| android.library=true | ||||
| android.library=`true` | ||||
|  | @ -0,0 +1,57 @@ | |||
| namespace Guru.Editor | ||||
| { | ||||
|     using System; | ||||
|     using System.IO; | ||||
|     using UnityEngine; | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Create androidlib assets | ||||
|     /// </summary> | ||||
|     public class AndroidLibHelper | ||||
|     { | ||||
|          | ||||
|         private static readonly string PluginsRoot = "Plugins/Android"; | ||||
|         private static readonly string Extends = "androidlib"; | ||||
|         private static readonly string ProjectPropertiesName = "project.properties"; | ||||
|         private static readonly string ProjectPropertiesContent= "target=android-9\nandroid.library=true"; | ||||
|         private static readonly string AndroidManifestName = "AndroidManifest.xml"; | ||||
|         private static readonly string AndroidManifestContent = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n          package=\"{0}\"\n          android:versionCode=\"1\"\n          android:versionName=\"1.0\">\n</manifest>"; | ||||
| 
 | ||||
|          | ||||
|         public static bool IsEmbeddedAndroidLibExists(string fileName) | ||||
|         { | ||||
|             string dir = Path.GetFullPath($"{Application.dataPath}/{PluginsRoot}/{fileName}.{Extends}"); | ||||
|             return Directory.Exists(dir); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public static string CreateLibRoot(string packageName, string fileName = "") | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(packageName)) return ""; | ||||
|              | ||||
|             if(string.IsNullOrEmpty(fileName)) fileName = packageName; | ||||
|              | ||||
|             string dir = Path.GetFullPath($"{Application.dataPath}/{PluginsRoot}/{fileName}.{Extends}"); | ||||
|             if (Directory.Exists(dir)) | ||||
|             { | ||||
|                 return dir; | ||||
|             } | ||||
|             Directory.CreateDirectory(dir); | ||||
| 
 | ||||
|             string path = ""; | ||||
|             string content = ""; | ||||
|              | ||||
|             //------ Create project.properties ------ | ||||
|             content = ProjectPropertiesContent; | ||||
|             path = $"{dir}/{ProjectPropertiesName}"; | ||||
|             File.WriteAllText(path, content); | ||||
|             // ------ Create AndroidManifest.xml ------ | ||||
|             content = AndroidManifestContent.Replace("{0}", packageName); | ||||
|             path = $"{dir}/{AndroidManifestName}"; | ||||
|             File.WriteAllText(path, content); | ||||
|              | ||||
|             return dir; | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: ee3d47ab5d544373ae6a43fdd36d96fb | ||||
| timeCreated: 1711761343 | ||||
|  | @ -67,7 +67,7 @@ namespace Guru.Editor | |||
|                 { | ||||
|                     if (e != null) | ||||
|                     { | ||||
|                         if (e.HasAttribute("android:name")) | ||||
|                         if (e.HasAttribute("name", NamespaceAndroid)) | ||||
|                         { | ||||
|                             if (e.Attributes["android:name"].Value == ValOptimizeInitialization) item1 = 1; | ||||
|                             if (e.Attributes["android:name"].Value == ValOptimizeAdLoading) item2 = 1; | ||||
|  |  | |||
|  | @ -0,0 +1,257 @@ | |||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| namespace Guru.Editor | ||||
| { | ||||
|     using System.Xml; | ||||
|     using System.IO; | ||||
|     using UnityEngine; | ||||
|     using System.Collections.Generic; | ||||
|      | ||||
|      | ||||
|     /// <summary> | ||||
|     /// Android 配置修改器 | ||||
|     /// </summary> | ||||
|     public class MainManifestDoc | ||||
|     { | ||||
|         private const string TargetPath = "Plugins/Android/AndroidManifest.xml"; | ||||
|         private const string XmlnsAndroid = "xmlns:android"; | ||||
|         private const string NamespaceAndroid = "http://schemas.android.com/apk/res/android"; | ||||
|         private const string XmlnsTools= "xmlns:tools"; | ||||
|         private const string NamespaceTools = "http://schemas.android.com/tools"; | ||||
|          | ||||
|          | ||||
|          | ||||
|         private XmlDocument _doc; | ||||
|         public XmlDocument Doc => _doc; | ||||
| 
 | ||||
|         private string _docPath; | ||||
|         private bool _isReady = false; | ||||
|          | ||||
|         private XmlElement _manifestNode; | ||||
|         private XmlElement _applicationNode; | ||||
|         private XmlNodeList _metadataList; | ||||
|         private XmlNodeList _permissionList; | ||||
| 
 | ||||
|          | ||||
|         #region Initiallize | ||||
|          | ||||
|         /// <summary> | ||||
|         /// 加载文件 | ||||
|         /// </summary> | ||||
|         /// <param name="docPath"></param> | ||||
|         /// <returns></returns> | ||||
|         public static MainManifestDoc Load(string docPath = "") | ||||
|         { | ||||
|             if (string.IsNullOrEmpty(docPath)) | ||||
|             { | ||||
|                 docPath = Path.GetFullPath(Path.Combine(Application.dataPath, TargetPath)); | ||||
|             } | ||||
| 
 | ||||
|             if (!File.Exists(docPath)) | ||||
|             { | ||||
|                 Debug.LogError($"--- File not found: {docPath}"); | ||||
|                 return null; | ||||
|             } | ||||
| 
 | ||||
|             var mod = new MainManifestDoc(); | ||||
|             mod.ReadFromPath(docPath); | ||||
|             return mod; | ||||
|         } | ||||
| 
 | ||||
|         public static MainManifestDoc Read(string xmlStr, string docPath = "") | ||||
|         { | ||||
|             var mod = new MainManifestDoc(); | ||||
|             mod.ReadFromXml(xmlStr, docPath); | ||||
|             return mod; | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 从文件路径读取 | ||||
|         /// </summary> | ||||
|         /// <param name="docPath"></param> | ||||
|         public void ReadFromPath(string docPath) | ||||
|         { | ||||
|             _isReady = false; | ||||
|             if (File.Exists(docPath)) | ||||
|             { | ||||
|                 _docPath = docPath; | ||||
|                 var xmlStr = File.ReadAllText(docPath); | ||||
|                 ReadFromXml(xmlStr); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         public void ReadFromXml(string xmlStr, string docPath = "") | ||||
|         { | ||||
|             _doc = new XmlDocument(); | ||||
|             _doc.LoadXml(xmlStr); | ||||
|             _docPath = docPath; | ||||
|             Init(); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Initializes the Doc | ||||
|         /// </summary> | ||||
|         private void Init() | ||||
|         { | ||||
|             // --- Root Nodes --- | ||||
|             _manifestNode = _doc.SelectSingleNode("manifest") as XmlElement; | ||||
|             _applicationNode = _doc.SelectSingleNode("manifest/application") as XmlElement; | ||||
|             // --- Metadatas --- | ||||
|             _metadataList = _applicationNode.SelectNodes("meta-data"); | ||||
|             // --- Permissions --- | ||||
|             _permissionList = _applicationNode.SelectNodes("uses-permission"); | ||||
| 
 | ||||
|             AddXmlnsAndroid(); | ||||
|             AddXmlnsTools(); | ||||
|             _isReady = true; | ||||
|         } | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Save Doc | ||||
|         /// </summary> | ||||
|         public void Save(string docPath = "") | ||||
|         { | ||||
|             if (_isReady) | ||||
|             { | ||||
|                 if(string.IsNullOrEmpty(docPath)) docPath = _docPath; | ||||
|                 if (!string.IsNullOrEmpty(docPath)) | ||||
|                 { | ||||
|                     var dir = Directory.GetParent(docPath); | ||||
|                     if(!dir.Exists) dir.Create(); | ||||
|                     _doc.Save(_docPath); | ||||
| 
 | ||||
|                 } | ||||
| 
 | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
| 
 | ||||
|         #region Node Opreation | ||||
| 
 | ||||
|          | ||||
|         public static bool AddAttribute(XmlElement node, string key, string value) | ||||
|         { | ||||
|             if (node != null) | ||||
|             { | ||||
|                 if (node.HasAttribute(key)) | ||||
|                 { | ||||
|                     node.Attributes[key].Value = value; | ||||
|                 } | ||||
|                 else | ||||
|                 { | ||||
|                     node.SetAttribute(key, value); | ||||
|                 } | ||||
|                 return true; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|          | ||||
|          | ||||
| 
 | ||||
|         #endregion | ||||
|          | ||||
| 
 | ||||
|         #region API | ||||
| 
 | ||||
|         public bool AddXmlnsAndroid() | ||||
|         { | ||||
|             return AddAttribute(_manifestNode, XmlnsAndroid, NamespaceAndroid); | ||||
|         } | ||||
|          | ||||
|         public bool AddXmlnsTools() | ||||
|         { | ||||
|             return AddAttribute(_manifestNode, XmlnsTools, NamespaceTools); | ||||
|         } | ||||
|          | ||||
|         /// <summary> | ||||
|         /// Add Replace Item | ||||
|         /// </summary> | ||||
|         /// <param name="item"></param> | ||||
|         public void AddReplaceItem(string item) | ||||
|         { | ||||
|             if (_manifestNode != null) | ||||
|             { | ||||
|                 List<string> items = new List<string>(5); | ||||
|                 if (_manifestNode.HasAttribute("replace", NamespaceTools)) | ||||
|                 { | ||||
|                     var arr = _manifestNode.GetAttribute("replace",NamespaceTools).Split(','); | ||||
|                     if(arr != null && arr.Length > 0) | ||||
|                     { | ||||
|                         items.AddRange(arr); | ||||
|                     } | ||||
|                 } | ||||
| 
 | ||||
|                 if (!items.Contains(item)) items.Add(item); | ||||
|                  | ||||
|                 _manifestNode.SetAttribute("replace",  NamespaceTools, string.Join(",", items)); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// Set metadata | ||||
|         /// </summary> | ||||
|         /// <param name="key"></param> | ||||
|         /// <param name="value"></param> | ||||
|         /// <param name="valueName"></param> | ||||
|         /// <param name="keyName"></param> | ||||
|         public void SetMetadata(string key, string value, string valueName = "value", string keyName = "name") | ||||
|         { | ||||
|             _metadataList = _applicationNode?.SelectNodes("meta-data") ?? null; | ||||
|              | ||||
|             XmlElement item = null; | ||||
|             if (_metadataList != null) | ||||
|             { | ||||
|                 foreach (XmlElement e in _metadataList) | ||||
|                 { | ||||
|                     // var nm =    e.GetAttribute("name", NamespaceAndroid); | ||||
|                     if (e.GetAttribute(keyName, NamespaceAndroid) is string k && k == key) | ||||
|                     { | ||||
|                         item = e; | ||||
|                         break; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             if (item == null) | ||||
|             { | ||||
|                 item = _doc.CreateElement("meta-data"); | ||||
|                 _applicationNode?.AppendChild(item); | ||||
|             } | ||||
|              | ||||
|             item.SetAttribute(keyName, NamespaceAndroid, key); | ||||
|             item.SetAttribute(valueName, NamespaceAndroid, value); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         #endregion | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|         #region Output | ||||
| 
 | ||||
| 
 | ||||
|         public override string ToString() | ||||
|         { | ||||
|             if (_doc != null) return _doc.InnerXml; | ||||
|             return this.ToString(); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         #endregion | ||||
|          | ||||
|          | ||||
|          | ||||
|          | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 89a1c7f77fcf4982adbbaf6dc61bd62d | ||||
| timeCreated: 1711505949 | ||||
|  | @ -0,0 +1,148 @@ | |||
| 
 | ||||
| using System.Net; | ||||
| using UnityEditor; | ||||
| 
 | ||||
| namespace Guru.Editor | ||||
| { | ||||
|     using UnityEngine; | ||||
|     using System.IO; | ||||
| 
 | ||||
|     public class PushIconHelper | ||||
|     { | ||||
|         public static readonly int targetWidth = 96; // 目标宽度 | ||||
|         public static readonly int targetHeight = 96; | ||||
|         private static readonly string LibName = "SDKRes"; | ||||
|         private static readonly string PackageName = "com.guru.unity.res"; | ||||
|         private static readonly string IconName = "ic_notification.png"; | ||||
| 
 | ||||
|         private static readonly string ColorContent = | ||||
|             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <color name=\"colorAccent\">#{0}</color>\n</resources>"; | ||||
|         private static readonly string ValueContent = | ||||
|             "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<resources>\n    <string name=\"default_notification_channel_id\" translatable=\"false\">{0}</string>\n</resources>"; | ||||
|         private static readonly string[] iconNames = new string[] | ||||
|         { | ||||
|             "drawable-mdpi", | ||||
|             "drawable-hdpi", | ||||
|             "drawable-xhdpi", | ||||
|             "drawable-xxhdpi", | ||||
|             "drawable-xxxhdpi" | ||||
|         }; | ||||
|          | ||||
|          | ||||
|         private static string FcmChannelId = "fcm_default_channel"; | ||||
| 
 | ||||
|         /// <summary> | ||||
|         /// 设置推送图标 | ||||
|         /// </summary> | ||||
|         /// <param name="source"></param> | ||||
|         /// <param name="color"></param> | ||||
|         public static bool SetPushIconAssets(Texture2D source, Color color = default(Color)) | ||||
|         { | ||||
|             if (source == null) | ||||
|             { | ||||
|                 Debug.LogError($"=== No Texture2D found ==="); | ||||
|                 return false; | ||||
|             } | ||||
|             return DeployAllIcons(source, color); | ||||
|         } | ||||
|          | ||||
|         private static Texture2D ResizeTexture(Texture2D source, int newWidth, int newHeight) | ||||
|         { | ||||
|             MakeTextureReadable(source); | ||||
|              | ||||
|             Texture2D result = new Texture2D(newWidth, newHeight); | ||||
|             Color[] newColors = new Color[newWidth * newHeight]; | ||||
|   | ||||
|             for (int y = 0; y < newHeight; y++) | ||||
|             { | ||||
|                 for (int x = 0; x < newWidth; x++) | ||||
|                 { | ||||
|                     // 应用一些缩放逻辑来获取新的颜色值 | ||||
|                     newColors[x + y * newWidth] = source.GetPixelBilinear((float)x / newWidth * source.width, (float)y / newHeight * source.height); | ||||
|                 } | ||||
|             } | ||||
|   | ||||
|             result.SetPixels(newColors); | ||||
|             result.Apply(); | ||||
|             return result; | ||||
|         } | ||||
|          | ||||
|         private static void MakeTextureReadable(Texture2D source) | ||||
|         { | ||||
|             if (source.isReadable) return; | ||||
| 
 | ||||
|             var path = AssetDatabase.GetAssetPath(source); | ||||
|             TextureImporter ti = (TextureImporter)AssetImporter.GetAtPath(path); | ||||
|             if (!ti.isReadable) | ||||
|             { | ||||
|                 ti.isReadable = true; | ||||
|                 ti.SaveAndReimport(); | ||||
|             } | ||||
|         } | ||||
|          | ||||
|          | ||||
|         private static string ColorToHex(Color color) | ||||
|         { | ||||
|             return string.Format("{0:X2}{1:X2}{2:X2}", (int)(color.r * 255), (int)(color.g * 255), (int)(color.b * 255)); | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         private static bool  DeployAllIcons(Texture2D source, Color color) | ||||
|         { | ||||
|             var dir = AndroidLibHelper.CreateLibRoot(PackageName, LibName); | ||||
|             string path = ""; | ||||
|             string content = ""; | ||||
|              | ||||
|             var result = ResizeTexture(source, targetWidth, targetHeight); | ||||
|             byte[] bytes = result.EncodeToPNG(); | ||||
| 
 | ||||
| 
 | ||||
|             var resPath = $"{dir}/res"; | ||||
|             if (!Directory.Exists(resPath)) | ||||
|             { | ||||
|                 Directory.CreateDirectory(resPath); | ||||
|             } | ||||
|              | ||||
|             File.WriteAllBytes($"{resPath}/{IconName}", bytes); // Base Icon; | ||||
|             // ----- Build all  Icons ------ | ||||
|             foreach (var iconName in iconNames) | ||||
|             { | ||||
|                 var iconPath = $"{resPath}/{iconName}"; | ||||
|                 if (!Directory.Exists(iconPath)) | ||||
|                 { | ||||
|                     Directory.CreateDirectory(iconPath); | ||||
|                 } | ||||
|                 File.WriteAllBytes($"{iconPath}/{IconName}", bytes); | ||||
|             } | ||||
| 
 | ||||
|             var valuesPath = $"{resPath}/values"; | ||||
|             if (!Directory.Exists(valuesPath)) Directory.CreateDirectory(valuesPath); | ||||
|              | ||||
|             // ----- Build colors.xml ------ | ||||
|             path = $"{valuesPath}/colors.xml"; | ||||
|             content = ColorContent.Replace("{0}", ColorToHex(color)); | ||||
|             File.WriteAllText(path, content); | ||||
|             // ----- Build strings.xml ------ | ||||
|             path = $"{valuesPath}/strings.xml"; | ||||
|             content = ValueContent.Replace("{0}", FcmChannelId); | ||||
|             File.WriteAllText(path, content); | ||||
|              | ||||
|             // ----- Inject AndroidManifest.xml ------ | ||||
|             var doc = MainManifestDoc.Load(); | ||||
|             if (doc != null) | ||||
|             { | ||||
|                 doc.SetMetadata("com.google.firebase.messaging.default_notification_icon", "@drawable/ic_notification", valueName:"resource"); | ||||
|                 doc.SetMetadata("com.google.firebase.messaging.default_notification_color", "@color/colorAccent", valueName:"resource"); | ||||
|                 doc.SetMetadata("com.google.firebase.messaging.default_notification_channel_id", "@string/default_notification_channel_id"); | ||||
|                 doc.Save(); | ||||
|                 Debug.Log("<color=#88ff00> --- Push Icon Build Success --- </color>"); | ||||
|                 return true; | ||||
|             } | ||||
|              | ||||
|             Debug.LogError("AndroidManifest.xml not found ..."); | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  | @ -0,0 +1,3 @@ | |||
| fileFormatVersion: 2 | ||||
| guid: 39d65f42b9694641b0c3509262aca1d4 | ||||
| timeCreated: 1711724404 | ||||
|  | @ -394,6 +394,11 @@ namespace Guru.Editor | |||
|             { | ||||
|                 GUI_OnConfigEnabled(); | ||||
|             } | ||||
|              | ||||
|             GUILayout.Space(10); | ||||
| 
 | ||||
|             // Push | ||||
|             GUI_PushIconMaker(); | ||||
| 
 | ||||
|         } | ||||
| 
 | ||||
|  | @ -460,7 +465,7 @@ namespace Guru.Editor | |||
|             } | ||||
|              | ||||
|             GUILayout.Space(4); | ||||
|           | ||||
|              | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|  | @ -1000,6 +1005,60 @@ namespace Guru.Editor | |||
|             } | ||||
|         } | ||||
| 
 | ||||
|         #endregion | ||||
| 
 | ||||
| 
 | ||||
|         #region Push Icon Maker | ||||
| 
 | ||||
|         private bool _showSegmentPush = false; | ||||
|         private Texture2D _pushIconTexture; | ||||
|         private Color _pushIconColor = Color.white; | ||||
|          | ||||
|         private void GUI_PushIconMaker() | ||||
|         { | ||||
|             float btnH = 24; | ||||
| 
 | ||||
| 
 | ||||
|             _showSegmentPush = EditorGUILayout.Foldout(_showSegmentPush, "[ Push Icon ]"); | ||||
|              | ||||
|             // EditorGUILayout.LabelField("[ Push Icon ]", StyleItemTitle); | ||||
| 
 | ||||
|             if (_showSegmentPush) | ||||
|             { | ||||
|                 EditorGUI.indentLevel++; | ||||
|                 EditorGUILayout.BeginHorizontal("box"); | ||||
|                 EditorGUILayout.LabelField("Icon ( 96x96 PNG )"); | ||||
|                 _pushIconTexture = EditorGUILayout.ObjectField( _pushIconTexture, typeof(Texture2D), false) as Texture2D; | ||||
|                 EditorGUILayout.EndHorizontal(); | ||||
|                  | ||||
|                 EditorGUILayout.BeginHorizontal("box"); | ||||
|                 EditorGUILayout.LabelField("Icon Color"); | ||||
|                 _pushIconColor = EditorGUILayout.ColorField(_pushIconColor); | ||||
|                 EditorGUILayout.EndHorizontal(); | ||||
|              | ||||
|                 if (null != _pushIconTexture) | ||||
|                 { | ||||
|                     GUI_Button("CREATE PUSH ASSETS", () => | ||||
|                     { | ||||
|                         if (PushIconHelper.SetPushIconAssets(_pushIconTexture, _pushIconColor)) | ||||
|                         { | ||||
|                             EditorUtility.DisplayDialog("Set Push Icon", "Push Icon assets created success!", "OK"); | ||||
|                         } | ||||
|                         else | ||||
|                         { | ||||
|                             EditorUtility.DisplayDialog("Set Push Icon", "Push Icon assets created failed!", "Well..."); | ||||
|                         } | ||||
|                     }, null, GUILayout.Height(btnH)); | ||||
|                 } | ||||
|                 EditorGUI.indentLevel--; | ||||
|             } | ||||
| 
 | ||||
|              | ||||
|         } | ||||
|          | ||||
|          | ||||
|          | ||||
| 
 | ||||
|         #endregion | ||||
|          | ||||
|     } | ||||
|  |  | |||
|  | @ -6,7 +6,8 @@ | |||
|         "GuruSDK", | ||||
|         "Guru.LitJson", | ||||
|         "Guru.Runtime", | ||||
|         "MaxSdk.Scripts.IntegrationManager.Editor" | ||||
|         "MaxSdk.Scripts.IntegrationManager.Editor", | ||||
|         "Guru.Editor" | ||||
|     ], | ||||
|     "includePlatforms": [ | ||||
|         "Editor" | ||||
|  |  | |||
|  | @ -8,7 +8,7 @@ namespace Guru | |||
|      | ||||
|     public partial class GuruSDK: MonoBehaviour | ||||
|     { | ||||
|         public const string Version = "1.0.9"; | ||||
|         public const string Version = "1.0.10"; | ||||
|         public const string Tag = "[Guru]"; | ||||
|         public const string ServicesConfigKey = "guru_services"; | ||||
|          | ||||
|  |  | |||
|  | @ -1,7 +1,7 @@ | |||
| { | ||||
|   "name": "com.guru.unity.sdk", | ||||
|   "displayName": "Guru SDK", | ||||
|   "version": "1.0.9", | ||||
|   "version": "1.0.10", | ||||
|   "description": "Guru SDK for unity project", | ||||
|   "unity": "2020.3", | ||||
|   "author":{ | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue