|
|
@@ -0,0 +1,960 @@
|
|
|
+## FirebaseCloudMessage.cs
|
|
|
+
|
|
|
+``` cs
|
|
|
+using System;
|
|
|
+using Firebase.Extensions;
|
|
|
+using System.Collections;
|
|
|
+using System.Collections.Generic;
|
|
|
+using System.Linq;
|
|
|
+using Firebase;
|
|
|
+using UnityEngine;
|
|
|
+using UnityEngine.Networking;
|
|
|
+using Newtonsoft.Json;
|
|
|
+using UnityEngine.Android;
|
|
|
+using Firebase.Extensions;
|
|
|
+
|
|
|
+/// <summary>
|
|
|
+/// FCM推送插件 & 灵光系统
|
|
|
+/// </summary>
|
|
|
+public class FirebaseCloudMessage : MonoBehaviour
|
|
|
+{
|
|
|
+
|
|
|
+ private static string firebaseMessageReportUrl = String.Empty; //灵光平台 上报地址
|
|
|
+
|
|
|
+ private static string _android_channel; //安卓渠道号(和服务端确认即可)
|
|
|
+ private static string _ios_channel; //ios渠道号(和服务端确认即可)
|
|
|
+ private static string _language; //语言
|
|
|
+ private static string _country; //国家
|
|
|
+ private static string fcmToken = String.Empty; //token of fcm令牌
|
|
|
+
|
|
|
+ private static GameObject firebaseMessageUtilGameObject;
|
|
|
+ private static string userID; //玩家ID
|
|
|
+ private static bool inited = false; //是否初始化完成
|
|
|
+
|
|
|
+ private string _platform; //移动平台
|
|
|
+ private string _channel; //渠道号
|
|
|
+
|
|
|
+ // private static FirebaseApp app;
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 初始化
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="appID">应用代号</param>
|
|
|
+ /// <param name="ReportUrl">上报地址</param>
|
|
|
+ /// <param name="android_channel">安卓渠道</param>
|
|
|
+ /// <param name="ios_channel">ios渠道</param>
|
|
|
+ /// <param name="language">语言</param>
|
|
|
+ /// <param name="country">国家</param>
|
|
|
+ public static void Init(string appID,string ReportUrl,string android_channel = "2001",string ios_channel = "1001",string language = "en",string country = "US")
|
|
|
+ {
|
|
|
+
|
|
|
+ Firebase.FirebaseApp.CheckAndFixDependenciesAsync().ContinueWithOnMainThread(task => {
|
|
|
+ var dependencyStatus = task.Result;
|
|
|
+ if (dependencyStatus == Firebase.DependencyStatus.Available) {
|
|
|
+ // Create and hold a reference to your FirebaseApp,
|
|
|
+ // where app is a Firebase.FirebaseApp property of your application class.
|
|
|
+ // app = Firebase.FirebaseApp.DefaultInstance;
|
|
|
+ firebaseMessageReportUrl = $"{ReportUrl}/api/app/{appID}/user/update";
|
|
|
+ _android_channel = android_channel;
|
|
|
+ _ios_channel = ios_channel;
|
|
|
+ _language = language;
|
|
|
+ _country = country;
|
|
|
+
|
|
|
+ RequestNotificationPermission();
|
|
|
+
|
|
|
+ // Set a flag here to indicate whether Firebase is ready to use by your app.
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ Debug.Log("当前设备 谷歌框架等依赖缺失 不支持Firebase");
|
|
|
+ // UnityEngine.Debug.LogError(System.String.Format(
|
|
|
+ // "Could not resolve all Firebase dependencies: {0}", dependencyStatus));
|
|
|
+ // Firebase Unity SDK is not safe to use here.
|
|
|
+ }
|
|
|
+ });
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置玩家ID
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="userid"></param>
|
|
|
+ public static void SetUserID(string userid)
|
|
|
+ {
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"Firebase 功能测试 设置玩家ID{userid}");
|
|
|
+#endif
|
|
|
+ userID = userid;
|
|
|
+#if !UNITY_EDITOR
|
|
|
+ // 设置完用户ID后,初始化Firebase获取token
|
|
|
+ GetInstance().InitializeFirebase();
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ /// <summary>
|
|
|
+ /// 订阅消息主题,订阅成功后可以通过主题推送
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="topic"></param>
|
|
|
+ public static void SubscribeTopic(string topicJsonStr)
|
|
|
+ {
|
|
|
+ if (String.IsNullOrEmpty(fcmToken))
|
|
|
+ {
|
|
|
+ Debug.LogError("灵光系统 无法获得fcm token令牌");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //检测json键值合法性
|
|
|
+ Dictionary<string, string> paramsMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(topicJsonStr);
|
|
|
+ var (isValid, error) = CheckJsonDataValid(paramsMap);
|
|
|
+ if (!isValid)
|
|
|
+ {
|
|
|
+ Debug.LogWarning($"Failed to subscribe to the topic: {error}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (string key in paramsMap.Keys)
|
|
|
+ {
|
|
|
+ Firebase.Messaging.FirebaseMessaging.SubscribeAsync(paramsMap[key]).ContinueWithOnMainThread(task =>
|
|
|
+ {
|
|
|
+ if (task.IsFaulted)
|
|
|
+ Debug.LogWarning($"Failed to subscribe to the topic: {task.Exception}");
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 取消订阅消息主题
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="topicJsonStr"></param>
|
|
|
+ public static void unSubscribeTopic(string topicJsonStr)
|
|
|
+ {
|
|
|
+ if (String.IsNullOrEmpty(fcmToken))
|
|
|
+ {
|
|
|
+ Debug.LogError("灵光系统 无法获得fcm token令牌");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ //检测json键值合法性
|
|
|
+ Dictionary<string, string> paramsMap = JsonConvert.DeserializeObject<Dictionary<string, string>>(topicJsonStr);
|
|
|
+ var (isValid, error) = CheckJsonDataValid(paramsMap);
|
|
|
+ if (!isValid)
|
|
|
+ {
|
|
|
+ Debug.LogWarning($"Failed to subscribe to the topic: {error}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ foreach (string key in paramsMap.Keys)
|
|
|
+ {
|
|
|
+ Firebase.Messaging.FirebaseMessaging.UnsubscribeAsync(paramsMap[key]).ContinueWithOnMainThread(task =>
|
|
|
+ {
|
|
|
+ if (task.IsFaulted)
|
|
|
+ Debug.LogWarning($"Failed to subscribe to the topic: {task.Exception}");
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private void OnDestroy()
|
|
|
+ {
|
|
|
+ RemoveFirebase();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取单例实例
|
|
|
+ /// </summary>
|
|
|
+ private static FirebaseCloudMessage GetInstance()
|
|
|
+ {
|
|
|
+ if (firebaseMessageUtilGameObject == null)
|
|
|
+ {
|
|
|
+ firebaseMessageUtilGameObject = new GameObject();
|
|
|
+ DontDestroyOnLoad(firebaseMessageUtilGameObject);
|
|
|
+ firebaseMessageUtilGameObject.name = "firebaseMessage";
|
|
|
+ firebaseMessageUtilGameObject.AddComponent<FirebaseCloudMessage>();
|
|
|
+ }
|
|
|
+ return firebaseMessageUtilGameObject.GetComponent<FirebaseCloudMessage>();
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 初始化FirebaseCloudMessage消息推送
|
|
|
+ /// </summary>
|
|
|
+ private void InitializeFirebase()
|
|
|
+ {
|
|
|
+ // 避免重复初始化
|
|
|
+ if (inited)
|
|
|
+ {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ inited = true;
|
|
|
+
|
|
|
+ Firebase.Messaging.FirebaseMessaging.MessageReceived += OnMessageReceived;
|
|
|
+ Firebase.Messaging.FirebaseMessaging.TokenReceived += OnTokenReceived;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 移除FirebaseCloudMessage消息推送
|
|
|
+ /// </summary>
|
|
|
+ private void RemoveFirebase()
|
|
|
+ {
|
|
|
+ Firebase.Messaging.FirebaseMessaging.MessageReceived -= OnMessageReceived;
|
|
|
+ Firebase.Messaging.FirebaseMessaging.TokenReceived -= OnTokenReceived;
|
|
|
+ inited = false;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void OnMessageReceived(object sender, Firebase.Messaging.MessageReceivedEventArgs e)
|
|
|
+ {
|
|
|
+ Dictionary<string, object> args = new Dictionary<string, object>();
|
|
|
+ // args.Add(NativeEventConst.EVENT_ID_KEY, NativeEventConst.FIREBASE_MESSAGE_RECEIVED);
|
|
|
+ //
|
|
|
+ var notification = e.Message.Notification;
|
|
|
+ if (notification != null)
|
|
|
+ {
|
|
|
+ args.Add("title", notification.Title);
|
|
|
+ args.Add("body", notification.Body);
|
|
|
+
|
|
|
+#if !UNITY_EDITOR && UNITY_ANDROID
|
|
|
+ var android = notification.Android;
|
|
|
+ if (android != null)
|
|
|
+ {
|
|
|
+ args.Add("android channel_id", android.ChannelId);
|
|
|
+ }
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ if (e.Message.From.Length > 0)
|
|
|
+ {
|
|
|
+ args.Add("from", e.Message.From);
|
|
|
+ }
|
|
|
+
|
|
|
+ if (e.Message.Link != null)
|
|
|
+ {
|
|
|
+ args.Add("link", e.Message.Link.ToString());
|
|
|
+ }
|
|
|
+
|
|
|
+ if (e.Message.Data.Count > 0)
|
|
|
+ {
|
|
|
+ foreach (KeyValuePair<string, string> item in e.Message.Data)
|
|
|
+ {
|
|
|
+ args.Add(item.Key, item.Value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private void OnTokenReceived(object sender, Firebase.Messaging.TokenReceivedEventArgs token)
|
|
|
+ {
|
|
|
+ fcmToken = token.Token;
|
|
|
+
|
|
|
+ // 获取到token后,立即发送给服务器(此时已经有用户ID了)
|
|
|
+ PostToServer();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 发送给灵光服务器
|
|
|
+ /// </summary>
|
|
|
+ private void PostToServer()
|
|
|
+ {
|
|
|
+ SetPlatform();
|
|
|
+ var deviceData = new
|
|
|
+ {
|
|
|
+ token = fcmToken,
|
|
|
+ platform = _platform,
|
|
|
+ channel = _channel,
|
|
|
+ device = SystemInfo.deviceUniqueIdentifier,
|
|
|
+ uid = userID,
|
|
|
+ language = _language,
|
|
|
+ country = _country,
|
|
|
+ };
|
|
|
+
|
|
|
+ string jsonString = JsonConvert.SerializeObject(deviceData);
|
|
|
+ CoroutineManager.Instance.StartManagedCoroutine(PostRequest(firebaseMessageReportUrl, jsonString));
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 设置 移动平台 & 渠道号相关(渠道号和服务端确认即可)
|
|
|
+ /// </summary>
|
|
|
+ private void SetPlatform()
|
|
|
+ {
|
|
|
+ switch (Application.platform)
|
|
|
+ {
|
|
|
+ case RuntimePlatform.Android:
|
|
|
+ _platform = "android";
|
|
|
+ _channel = _android_channel;
|
|
|
+ break;
|
|
|
+ case RuntimePlatform.IPhonePlayer:
|
|
|
+ _platform = "ios";
|
|
|
+ _channel = _ios_channel;
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检测json合法性
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="jsonStr"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private static (bool isValid, string error) CheckJsonDataValid<TKey, TValue>(Dictionary<TKey, TValue> jsonMap)
|
|
|
+ {
|
|
|
+ if (jsonMap == null)
|
|
|
+ return (false, "The dictionary object is null");
|
|
|
+
|
|
|
+ var emptyItems = jsonMap
|
|
|
+ .Where(kv => IsInvalidValue(kv.Value))
|
|
|
+ .Select(kv => kv.Key.ToString())
|
|
|
+ .ToList();
|
|
|
+
|
|
|
+ return emptyItems.Count == 0
|
|
|
+ ? (true, null)
|
|
|
+ : (false, $"The following key values are empty: {string.Join(", ", emptyItems)}");
|
|
|
+ }
|
|
|
+
|
|
|
+ private static bool IsInvalidValue<T>(T value)
|
|
|
+ {
|
|
|
+ if (value == null) return true;
|
|
|
+ if (value is string str) return string.IsNullOrEmpty(str);
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 主动申请通知权限
|
|
|
+ /// </summary>
|
|
|
+ private static void RequestNotificationPermission()
|
|
|
+ {
|
|
|
+#if !UNITY_EDITOR
|
|
|
+#if UNITY_ANDROID
|
|
|
+ // 检查Android版本
|
|
|
+ if (GetAndroidVersion() >= 33) // Android 13+
|
|
|
+ {
|
|
|
+ // 检查是否已经有通知权限
|
|
|
+ if (!CheckNotificationPermissionStatus())
|
|
|
+ {
|
|
|
+ Debug.Log("请求通知权限...");
|
|
|
+ // 请求通知权限
|
|
|
+ Permission.RequestUserPermission("android.permission.POST_NOTIFICATIONS");
|
|
|
+
|
|
|
+ // 添加权限回调
|
|
|
+ PermissionCallbacks callbacks = new PermissionCallbacks();
|
|
|
+ callbacks.PermissionGranted += (permissionName) => {
|
|
|
+ Debug.Log($"通知权限已授予: {permissionName}");
|
|
|
+ };
|
|
|
+ callbacks.PermissionDenied += (permissionName) => {
|
|
|
+ Debug.LogWarning($"通知权限被拒绝: {permissionName}");
|
|
|
+ };
|
|
|
+ callbacks.PermissionDeniedAndDontAskAgain += (permissionName) => {
|
|
|
+ Debug.LogWarning($"通知权限被拒绝且不再询问: {permissionName}");
|
|
|
+ };
|
|
|
+
|
|
|
+ Permission.RequestUserPermission("android.permission.POST_NOTIFICATIONS", callbacks);
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.Log("已有通知权限");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.Log("Android版本低于13,无需申请通知权限");
|
|
|
+ }
|
|
|
+#elif UNITY_IOS
|
|
|
+ // 使用iOSNotificationPermission类处理iOS通知权限
|
|
|
+ if (!iOSNotificationPermission.CheckPermission())
|
|
|
+ {
|
|
|
+ Debug.Log("请求iOS通知权限...");
|
|
|
+ iOSNotificationPermission.RequestPermission();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.Log("已有iOS通知权限");
|
|
|
+ }
|
|
|
+#else
|
|
|
+ Debug.Log("其他平台,无需申请通知权限");
|
|
|
+#endif
|
|
|
+#else
|
|
|
+ Debug.Log("Unity编辑器模式,跳过通知权限申请");
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 获取Android版本
|
|
|
+ /// </summary>
|
|
|
+ private static int GetAndroidVersion()
|
|
|
+ {
|
|
|
+#if !UNITY_EDITOR && UNITY_ANDROID
|
|
|
+ try
|
|
|
+ {
|
|
|
+ using (AndroidJavaClass buildVersion = new AndroidJavaClass("android.os.Build$VERSION"))
|
|
|
+ {
|
|
|
+ return buildVersion.GetStatic<int>("SDK_INT");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ Debug.LogError($"获取Android版本失败: {e.Message}");
|
|
|
+ return 0;
|
|
|
+ }
|
|
|
+#else
|
|
|
+ return 0; // 非Android平台返回0
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检查通知权限状态
|
|
|
+ /// </summary>
|
|
|
+ private static bool CheckNotificationPermissionStatus()
|
|
|
+ {
|
|
|
+#if !UNITY_EDITOR && UNITY_ANDROID
|
|
|
+ if (GetAndroidVersion() >= 33)
|
|
|
+ {
|
|
|
+ try
|
|
|
+ {
|
|
|
+ using (AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer"))
|
|
|
+ using (AndroidJavaObject currentActivity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity"))
|
|
|
+ using (AndroidJavaObject context = currentActivity.Call<AndroidJavaObject>("getApplicationContext"))
|
|
|
+ using (AndroidJavaClass notificationManager = new AndroidJavaClass("android.app.NotificationManager"))
|
|
|
+ using (AndroidJavaObject notificationService = context.Call<AndroidJavaObject>("getSystemService", "notification"))
|
|
|
+ {
|
|
|
+ // 直接调用areNotificationsEnabled方法,因为Android 13+肯定支持这个方法
|
|
|
+ return notificationService.Call<bool>("areNotificationsEnabled");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception e)
|
|
|
+ {
|
|
|
+ Debug.LogError($"检查通知权限状态失败: {e.Message}");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return true; // 低版本默认返回true
|
|
|
+#elif !UNITY_EDITOR && UNITY_IOS
|
|
|
+ // 使用iOSNotificationPermission类检查iOS通知权限状态
|
|
|
+ return iOSNotificationPermission.CheckPermission();
|
|
|
+#else
|
|
|
+ // Unity编辑器或其他平台,默认返回true
|
|
|
+ return true;
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ [System.Serializable]
|
|
|
+ private class PostResponse
|
|
|
+ {
|
|
|
+ public int code;
|
|
|
+ public string message;
|
|
|
+ public string data;
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 上报灵光事件
|
|
|
+ /// </summary>
|
|
|
+ /// <param name="url"></param>
|
|
|
+ /// <param name="jsonData"></param>
|
|
|
+ /// <returns></returns>
|
|
|
+ private static IEnumerator PostRequest(string url,string jsonData)
|
|
|
+ {
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"Firebase 功能测试,发送链接:{url}");
|
|
|
+ Debug.Log($"Firebase 功能测试,发送数据:{jsonData}");
|
|
|
+#endif
|
|
|
+
|
|
|
+ using (UnityWebRequest request = UnityWebRequest.PostWwwForm(url, "POST"))
|
|
|
+ {
|
|
|
+ byte[] bodyRaw = System.Text.Encoding.UTF8.GetBytes(jsonData);
|
|
|
+
|
|
|
+ request.uploadHandler = new UploadHandlerRaw(bodyRaw);
|
|
|
+ request.downloadHandler = new DownloadHandlerBuffer();
|
|
|
+ request.SetRequestHeader("Content-Type", "application/json");
|
|
|
+
|
|
|
+ yield return request.SendWebRequest();
|
|
|
+
|
|
|
+ if (request.result != UnityWebRequest.Result.Success)
|
|
|
+ {
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"Firebase 功能测试,错误码:{request.error}");
|
|
|
+#endif
|
|
|
+ Debug.LogWarning("Equipment reporting failed." + request.error);
|
|
|
+ yield break;
|
|
|
+ }
|
|
|
+
|
|
|
+ var response = JsonUtility.FromJson<PostResponse>(request.downloadHandler.text);
|
|
|
+ if (response.code != 0)
|
|
|
+ {
|
|
|
+ Debug.LogError($"灵光系统 错误码:{response.code}");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"Firebase 功能测试,发送回包:{request.downloadHandler.text}");
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ };
|
|
|
+ }
|
|
|
+}
|
|
|
+```
|
|
|
+
|
|
|
+## NotificationServiceExtensionCreator.cs
|
|
|
+
|
|
|
+``` cs
|
|
|
+#if UNITY_IOS
|
|
|
+using System.IO;
|
|
|
+using UnityEditor;
|
|
|
+using UnityEditor.Callbacks;
|
|
|
+using UnityEditor.iOS.Xcode;
|
|
|
+using UnityEditor.iOS.Xcode.Extensions;
|
|
|
+using UnityEngine;
|
|
|
+using UnityEditor.iOS.Xcode.PBX;
|
|
|
+using System.Diagnostics;
|
|
|
+using System.Threading;
|
|
|
+using System;
|
|
|
+using Debug = UnityEngine.Debug;
|
|
|
+
|
|
|
+public static class FirebaseNotificationServiceExtensionCreator
|
|
|
+{
|
|
|
+ private const string ExtensionName = "FirebaseNotificationService";
|
|
|
+
|
|
|
+ [PostProcessBuild(200)]
|
|
|
+ public static void OnPostProcessBuild(BuildTarget target, string buildPath)
|
|
|
+ {
|
|
|
+ if (target != BuildTarget.iOS) return;
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 开始创建 Firebase Notification Service Extension...");
|
|
|
+#endif
|
|
|
+
|
|
|
+ string projPath = PBXProject.GetPBXProjectPath(buildPath);
|
|
|
+ PBXProject proj = new PBXProject();
|
|
|
+ proj.ReadFromFile(projPath);
|
|
|
+
|
|
|
+#if UNITY_2019_3_OR_NEWER
|
|
|
+ string mainTargetGuid = proj.GetUnityMainTargetGuid();
|
|
|
+#else
|
|
|
+ string mainTargetGuid = proj.TargetGuidByName(PBXProject.GetUnityTargetName());
|
|
|
+#endif
|
|
|
+
|
|
|
+ // 1. 创建 Extension Target(这会自动创建基础文件)
|
|
|
+ string extGuid = proj.AddAppExtension(mainTargetGuid, ExtensionName, ExtensionName,
|
|
|
+ $"{ExtensionName}/Info.plist");
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($" FireBase功能测试 创建 Extension Target: {extGuid}");
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+ // 2. 配置基本设置
|
|
|
+ proj.SetBuildProperty(extGuid, "PRODUCT_BUNDLE_IDENTIFIER",
|
|
|
+ PlayerSettings.GetApplicationIdentifier(BuildTargetGroup.iOS) + "." + ExtensionName);
|
|
|
+ proj.SetBuildProperty(extGuid, "ENABLE_BITCODE", "NO");
|
|
|
+ proj.SetBuildProperty(extGuid, "IPHONEOS_DEPLOYMENT_TARGET", "15.0");
|
|
|
+
|
|
|
+ // 3. 配置签名
|
|
|
+ ConfigureCodeSigning(proj, extGuid);
|
|
|
+
|
|
|
+ // 4. 移动预制的代码文件到扩展目录
|
|
|
+ MovePrebuiltSourceFiles(buildPath);
|
|
|
+
|
|
|
+ // 5. 添加系统框架
|
|
|
+ proj.AddFrameworkToProject(extGuid, "UserNotifications.framework", false);
|
|
|
+
|
|
|
+ // 6. 配置 Push Notifications 能力
|
|
|
+ ConfigurePushNotificationsCapability(proj, extGuid, buildPath);
|
|
|
+
|
|
|
+ // 7. 关键修复:确保文件被添加到 Xcode 项目结构和编译阶段
|
|
|
+ AddFilesToProjectStructure(proj, extGuid, buildPath);
|
|
|
+
|
|
|
+ // 8. 配置 Podfile
|
|
|
+ ConfigureFirebasePodfile(buildPath);
|
|
|
+
|
|
|
+ // 9. 保存工程
|
|
|
+ proj.WriteToFile(projPath);
|
|
|
+ AssetDatabase.Refresh();
|
|
|
+
|
|
|
+ // 10. 执行 pod install
|
|
|
+ PodInstallFirebaseNotificationService(buildPath);
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ // 关键方法:确保文件被添加到 Xcode 项目结构
|
|
|
+ private static void AddFilesToProjectStructure(PBXProject proj, string extGuid, string buildPath)
|
|
|
+ {
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 开始添加文件到 Xcode 项目结构...");
|
|
|
+#endif
|
|
|
+
|
|
|
+ // 1. 获取或创建 Sources Build Phase
|
|
|
+ var buildPhaseID = proj.AddSourcesBuildPhase(extGuid);
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"FireBase功能测试 Sources Build Phase ID: {buildPhaseID}");
|
|
|
+#endif
|
|
|
+
|
|
|
+ // 2. 添加 .h 文件到项目结构
|
|
|
+ string headerPath = Path.Combine(ExtensionName, "FirebaseNotificationService.h");
|
|
|
+ string headerGuid = proj.AddFile(headerPath, headerPath, PBXSourceTree.Source);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"FireBase功能测试 Header 文件添加到项目: {headerGuid}");
|
|
|
+#endif
|
|
|
+
|
|
|
+ // 3. 添加 .m 文件到项目结构
|
|
|
+ string sourcePath = Path.Combine(ExtensionName, "FirebaseNotificationService.m");
|
|
|
+ string sourceGuid = proj.AddFile(sourcePath, sourcePath, PBXSourceTree.Source);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"FireBase功能测试 Source 文件添加到项目: {sourceGuid}");
|
|
|
+#endif
|
|
|
+
|
|
|
+ // 4. 将文件添加到编译阶段(确保 Target Membership 正确)
|
|
|
+ proj.AddFileToBuildSection(extGuid, buildPhaseID, headerGuid);
|
|
|
+ proj.AddFileToBuildSection(extGuid, buildPhaseID, sourceGuid);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"FireBase功能测试 文件已添加到编译阶段");
|
|
|
+#endif
|
|
|
+
|
|
|
+ // 5. 确保 Info.plist 也被添加到项目
|
|
|
+ string plistPath = Path.Combine(ExtensionName, "Info.plist");
|
|
|
+ string plistGuid = proj.AddFile(plistPath, plistPath, PBXSourceTree.Source);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"FireBase功能测试 Info.plist 添加到项目: {plistGuid}");
|
|
|
+ Debug.Log("FireBase功能测试 🎯 所有文件已成功添加到 Xcode 项目结构!");
|
|
|
+#endif
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void ConfigureCodeSigning(PBXProject proj, string extGuid)
|
|
|
+ {
|
|
|
+ string teamId = PlayerSettings.iOS.appleDeveloperTeamID;
|
|
|
+ if (!string.IsNullOrEmpty(teamId))
|
|
|
+ {
|
|
|
+ proj.SetBuildProperty(extGuid, "DEVELOPMENT_TEAM", teamId);
|
|
|
+ proj.SetBuildProperty(extGuid, "CODE_SIGN_STYLE", "Automatic");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void MovePrebuiltSourceFiles(string buildPath)
|
|
|
+ {
|
|
|
+ string extDir = Path.Combine(buildPath, ExtensionName);
|
|
|
+
|
|
|
+ // 确保目录存在
|
|
|
+ if (!Directory.Exists(extDir))
|
|
|
+ Directory.CreateDirectory(extDir);
|
|
|
+
|
|
|
+ // 源文件路径(Plugins/iOS 目录)
|
|
|
+ string sourceDir = Path.Combine(Application.dataPath, "Plugins", "iOS");
|
|
|
+
|
|
|
+ // 移动 .h 文件
|
|
|
+ string headerSource = Path.Combine(sourceDir, "FirebaseNotificationService.h");
|
|
|
+ string headerDest = Path.Combine(extDir, "FirebaseNotificationService.h");
|
|
|
+ if (File.Exists(headerSource))
|
|
|
+ {
|
|
|
+ // 如果目标文件已存在,先删除
|
|
|
+ if (File.Exists(headerDest))
|
|
|
+ {
|
|
|
+ File.Delete(headerDest);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 删除已存在的目标文件: " + headerDest);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ File.Copy(headerSource, headerDest);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 复制 FirebaseNotificationService.h 文件");
|
|
|
+#endif
|
|
|
+
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.LogError("❌ 找不到源文件: " + headerSource);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 移动 .m 文件
|
|
|
+ string sourceSource = Path.Combine(sourceDir, "FirebaseNotificationService.m");
|
|
|
+ string sourceDest = Path.Combine(extDir, "FirebaseNotificationService.m");
|
|
|
+ if (File.Exists(sourceSource))
|
|
|
+ {
|
|
|
+ // 如果目标文件已存在,先删除
|
|
|
+ if (File.Exists(sourceDest))
|
|
|
+ {
|
|
|
+ File.Delete(sourceDest);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试️ 删除已存在的目标文件: " + sourceDest);
|
|
|
+#endif
|
|
|
+ }
|
|
|
+
|
|
|
+ File.Copy(sourceSource, sourceDest);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 复制 FirebaseNotificationService.m 文件");
|
|
|
+#endif
|
|
|
+
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.LogError("❌ 找不到源文件: " + sourceSource);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 创建 Info.plist(如果需要)
|
|
|
+ string plistPath = Path.Combine(extDir, "Info.plist");
|
|
|
+ if (!File.Exists(plistPath))
|
|
|
+ {
|
|
|
+ File.WriteAllText(plistPath, FirebaseInfoPlistContent);
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 创建 Info.plist 文件");
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private static void ConfigurePushNotificationsCapability(PBXProject proj, string extGuid, string buildPath)
|
|
|
+ {
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 开始配置 Push Notifications 能力...");
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+ // 1. 添加 Push Notifications 能力
|
|
|
+ proj.AddCapability(extGuid, PBXCapabilityType.PushNotifications);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 添加 Push Notifications 能力");
|
|
|
+#endif
|
|
|
+
|
|
|
+
|
|
|
+ // 2. 确保签名配置正确
|
|
|
+ string teamId = PlayerSettings.iOS.appleDeveloperTeamID;
|
|
|
+ if (!string.IsNullOrEmpty(teamId))
|
|
|
+ {
|
|
|
+ proj.SetBuildProperty(extGuid, "DEVELOPMENT_TEAM", teamId);
|
|
|
+ proj.SetBuildProperty(extGuid, "CODE_SIGN_STYLE", "Automatic");
|
|
|
+
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"FireBase功能测试 配置开发团队: {teamId}");
|
|
|
+#endif
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 Push Notifications 能力配置完成");
|
|
|
+#endif
|
|
|
+
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 配置 Podfile,添加 Firebase 相关依赖
|
|
|
+ /// </summary>
|
|
|
+ private static void ConfigureFirebasePodfile(string buildPath)
|
|
|
+ {
|
|
|
+ string podfilePath = Path.Combine(buildPath, "Podfile");
|
|
|
+
|
|
|
+ // 如果 Podfile 不存在,直接警告并返回
|
|
|
+ if (!File.Exists(podfilePath))
|
|
|
+ {
|
|
|
+ Debug.LogWarning("FireBase功能测试 Podfile 不存在,跳过 Firebase 依赖配置");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查是否包含 FirebaseNotificationService target
|
|
|
+ string podfileContent = File.ReadAllText(podfilePath);
|
|
|
+
|
|
|
+ if (!podfileContent.Contains("target 'FirebaseNotificationService'"))
|
|
|
+ {
|
|
|
+ // 检测 UnityFramework 中的 Firebase 版本
|
|
|
+ string firebaseVersion = DetectFirebaseVersionInUnityFramework(podfileContent);
|
|
|
+
|
|
|
+ // 在文件末尾添加 FirebaseNotificationService target
|
|
|
+ string firebaseTargetContent = $@"
|
|
|
+
|
|
|
+target 'FirebaseNotificationService' do
|
|
|
+ pod 'Firebase/Messaging', '{firebaseVersion}'
|
|
|
+end
|
|
|
+
|
|
|
+use_frameworks! :linkage => :static";
|
|
|
+
|
|
|
+ podfileContent += firebaseTargetContent;
|
|
|
+ File.WriteAllText(podfilePath, podfileContent);
|
|
|
+
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"FireBase功能测试 在现有 Podfile 中添加 FirebaseNotificationService target,使用 Firebase 版本: {firebaseVersion}");
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log("FireBase功能测试 FirebaseNotificationService target 已存在于 Podfile 中");
|
|
|
+#endif
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 检测 UnityFramework target 中的 Firebase 版本
|
|
|
+ /// </summary>
|
|
|
+ private static string DetectFirebaseVersionInUnityFramework(string podfileContent)
|
|
|
+ {
|
|
|
+ // 默认版本
|
|
|
+ string defaultVersion = "12.0.0";
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 查找 UnityFramework target 中的 Firebase 版本
|
|
|
+ int unityFrameworkStart = podfileContent.IndexOf("target 'UnityFramework' do");
|
|
|
+ if (unityFrameworkStart == -1)
|
|
|
+ {
|
|
|
+ Debug.LogWarning("FireBase功能测试 未找到 UnityFramework target,使用默认版本: " + defaultVersion);
|
|
|
+ return defaultVersion;
|
|
|
+ }
|
|
|
+
|
|
|
+ int unityFrameworkEnd = podfileContent.IndexOf("end", unityFrameworkStart);
|
|
|
+ if (unityFrameworkEnd == -1)
|
|
|
+ {
|
|
|
+ Debug.LogWarning("FireBase功能测试 UnityFramework target 格式错误,使用默认版本: " + defaultVersion);
|
|
|
+ return defaultVersion;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 提取 UnityFramework target 的内容
|
|
|
+ string unityFrameworkContent = podfileContent.Substring(unityFrameworkStart, unityFrameworkEnd - unityFrameworkStart);
|
|
|
+
|
|
|
+ // 查找 Firebase 相关的 pod 声明
|
|
|
+ string[] firebasePods = { "Firebase/Analytics", "Firebase/Core", "Firebase/Messaging", "Firebase" };
|
|
|
+ string detectedVersion = null;
|
|
|
+
|
|
|
+ foreach (string pod in firebasePods)
|
|
|
+ {
|
|
|
+ int podIndex = unityFrameworkContent.IndexOf($"pod '{pod}'");
|
|
|
+ if (podIndex != -1)
|
|
|
+ {
|
|
|
+ // 找到 pod 声明,提取版本号
|
|
|
+ int versionStart = unityFrameworkContent.IndexOf("'", podIndex + pod.Length + 6);
|
|
|
+ if (versionStart != -1)
|
|
|
+ {
|
|
|
+ int versionEnd = unityFrameworkContent.IndexOf("'", versionStart + 1);
|
|
|
+ if (versionEnd != -1)
|
|
|
+ {
|
|
|
+ detectedVersion = unityFrameworkContent.Substring(versionStart + 1, versionEnd - versionStart - 1);
|
|
|
+ break;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!string.IsNullOrEmpty(detectedVersion))
|
|
|
+ {
|
|
|
+#if Firebase_Debug
|
|
|
+ Debug.Log($"FireBase功能测试 检测到 UnityFramework 中的 Firebase 版本: {detectedVersion}");
|
|
|
+#endif
|
|
|
+ return detectedVersion;
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.LogWarning("FireBase功能测试 未在 UnityFramework 中找到 Firebase 版本信息,使用默认版本: " + defaultVersion);
|
|
|
+ return defaultVersion;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ Debug.LogError($"FireBase功能测试 检测 Firebase 版本时发生错误: {ex.Message}");
|
|
|
+ return defaultVersion;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /// <summary>
|
|
|
+ /// 执行 pod install 安装 Firebase 通知扩展组件
|
|
|
+ /// </summary>
|
|
|
+ private static void PodInstallFirebaseNotificationService(string buildPath)
|
|
|
+ {
|
|
|
+ Debug.Log("=== 开始执行 Pod 安装流程 ===");
|
|
|
+
|
|
|
+ if (!Directory.Exists(buildPath))
|
|
|
+ {
|
|
|
+ Debug.LogError($"Xcode工程路径不存在: {buildPath}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ string podfilePath = Path.Combine(buildPath, "Podfile");
|
|
|
+ if (!File.Exists(podfilePath))
|
|
|
+ {
|
|
|
+ Debug.LogError($"Podfile文件不存在: {podfilePath}");
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try
|
|
|
+ {
|
|
|
+ // 转义路径中的单引号
|
|
|
+ string escapedPath = buildPath.Replace("'", "'\\''");
|
|
|
+
|
|
|
+ string appleScript = $@"
|
|
|
+ tell application ""Terminal""
|
|
|
+ activate
|
|
|
+ do script ""cd '{escapedPath}' && pod install && exit""
|
|
|
+ end tell";
|
|
|
+
|
|
|
+ Debug.Log($"正在启动终端执行pod install...");
|
|
|
+
|
|
|
+ using (Process process = new Process())
|
|
|
+ {
|
|
|
+ process.StartInfo = new ProcessStartInfo
|
|
|
+ {
|
|
|
+ FileName = "/usr/bin/osascript",
|
|
|
+ Arguments = $"-e '{appleScript}'",
|
|
|
+ UseShellExecute = false,
|
|
|
+ RedirectStandardError = true,
|
|
|
+ CreateNoWindow = true
|
|
|
+ };
|
|
|
+
|
|
|
+ process.Start();
|
|
|
+ process.WaitForExit();
|
|
|
+
|
|
|
+ if (process.ExitCode != 0)
|
|
|
+ {
|
|
|
+ string error = process.StandardError.ReadToEnd();
|
|
|
+ Debug.LogError($"启动终端失败: {error}");
|
|
|
+ }
|
|
|
+ else
|
|
|
+ {
|
|
|
+ Debug.Log("已启动终端执行pod install,请稍后在终端中查看结果");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ catch (Exception ex)
|
|
|
+ {
|
|
|
+ Debug.LogError($"执行命令时发生异常: {ex}");
|
|
|
+ }
|
|
|
+
|
|
|
+ Debug.Log("=== Pod安装流程结束 ===");
|
|
|
+ }
|
|
|
+
|
|
|
+ // Firebase 的 Info.plist 内容
|
|
|
+ private static readonly string FirebaseInfoPlistContent =
|
|
|
+@"<?xml version=""1.0"" encoding=""UTF-8""?>
|
|
|
+<!DOCTYPE plist PUBLIC ""-//Apple//DTD PLIST 1.0//EN"" ""http://www.apple.com/DTDs/PropertyList-1.0.dtd"">
|
|
|
+<plist version=""1.0"">
|
|
|
+<dict>
|
|
|
+ <key>CFBundleDevelopmentRegion</key>
|
|
|
+ <string>$(DEVELOPMENT_LANGUAGE)</string>
|
|
|
+ <key>CFBundleDisplayName</key>
|
|
|
+ <string>FirebaseNotificationService</string>
|
|
|
+ <key>CFBundleExecutable</key>
|
|
|
+ <string>$(EXECUTABLE_NAME)</string>
|
|
|
+ <key>CFBundleIdentifier</key>
|
|
|
+ <string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
|
|
+ <key>CFBundleInfoDictionaryVersion</key>
|
|
|
+ <string>6.0</string>
|
|
|
+ <key>CFBundleName</key>
|
|
|
+ <string>$(PRODUCT_NAME)</string>
|
|
|
+ <key>CFBundlePackageType</key>
|
|
|
+ <string>XPC!</string>
|
|
|
+ <key>CFBundleShortVersionString</key>
|
|
|
+ <string>1.0</string>
|
|
|
+ <key>CFBundleVersion</key>
|
|
|
+ <string>1</string>
|
|
|
+ <key>NSExtension</key>
|
|
|
+ <dict>
|
|
|
+ <key>NSExtensionPointIdentifier</key>
|
|
|
+ <string>com.apple.usernotifications.service</string>
|
|
|
+ <key>NSExtensionPrincipalClass</key>
|
|
|
+ <string>FirebaseNotificationService</string>
|
|
|
+ </dict>
|
|
|
+</dict>
|
|
|
+</plist>";
|
|
|
+
|
|
|
+}
|
|
|
+
|
|
|
+#endif
|
|
|
+```
|