diff --git a/Runtime/GuruNoification/Editor/Android_PostCustomActivity.cs b/Runtime/GuruNoification/Editor/Android_PostCustomActivity.cs new file mode 100644 index 0000000..5974496 --- /dev/null +++ b/Runtime/GuruNoification/Editor/Android_PostCustomActivity.cs @@ -0,0 +1,178 @@ + + +#if UNITY_ANDROID + +namespace Guru.Notification +{ + using System; + using System.IO; + using UnityEditor.Android; + using System.Xml; + + public class PostCustomActivity: IPostGenerateGradleAndroidProject + { + private const int POST_ORDER = 10; + private const string K_PREMISSION_POST_NOTIFICATIONS = "android.permission.POST_NOTIFICATIONS"; + private const string K_CUSTOM_NOTIFICATION_ACTIVITY = "custom_notification_android_activity"; + private const string V_DEFAULT_GURU_ACTIVITY = "com.google.firebase.messaging.MessageForwardingService"; + const string K_ANDROID_NAMESPACE_URI = "http://schemas.android.com/apk/res/android"; + + public int callbackOrder => POST_ORDER; + + public void OnPostGenerateGradleAndroidProject(string path) + { + SetupAndroidManifest(path); + } + + /// + /// 设置 Android Manifest + /// + /// + /// + private void SetupAndroidManifest(string projectPath) + { + var manifestPath = $"{projectPath}/src/main/AndroidManifest.xml"; + if (!File.Exists(manifestPath)) + throw new FileNotFoundException($"'{manifestPath}' doesn't exist."); + + XmlDocument manifestDoc = new XmlDocument(); + manifestDoc.Load(manifestPath); + + InjectAndroidManifest(manifestPath, manifestDoc); + + manifestDoc.Save(manifestPath); + } + + internal static void InjectAndroidManifest(string manifestPath, XmlDocument manifestDoc) + { + string mainActivity = GetLauncherActivity(manifestDoc); + + AppendAndroidMetadataField(manifestPath, manifestDoc, K_CUSTOM_NOTIFICATION_ACTIVITY, mainActivity); + AppendAndroidPermissionField(manifestPath, manifestDoc, K_PREMISSION_POST_NOTIFICATIONS); + + UnityEngine.Debug.Log($"Add custom notification activity: {mainActivity} success!!"); + } + + internal static void AppendAndroidPermissionField(string manifestPath, XmlDocument xmlDoc, string name, string maxSdk = null) + { + AppendAndroidPermissionField(manifestPath, xmlDoc, "uses-permission", name, maxSdk); + } + + internal static void AppendAndroidPermissionField(string manifestPath, XmlDocument xmlDoc, string tagName, string name, string maxSdk) + { + var manifestNode = xmlDoc.SelectSingleNode("manifest"); + if (manifestNode == null) + throw new ArgumentException(string.Format("Missing 'manifest' node in '{0}'.", manifestPath)); + + XmlElement metaDataNode = null; + foreach (XmlNode node in manifestNode.ChildNodes) + { + if (!(node is XmlElement) || node.Name != tagName) + continue; + + var element = (XmlElement)node; + var elementName = element.GetAttribute("name", K_ANDROID_NAMESPACE_URI); + if (elementName == name) + { + if (maxSdk == null) + return; + var maxSdkAttr = element.GetAttribute("maxSdkVersion", K_ANDROID_NAMESPACE_URI); + if (!string.IsNullOrEmpty(maxSdkAttr)) + return; + metaDataNode = element; + } + } + + if (metaDataNode == null) + { + metaDataNode = xmlDoc.CreateElement(tagName); + metaDataNode.SetAttribute("name", K_ANDROID_NAMESPACE_URI, name); + } + if (maxSdk != null) + metaDataNode.SetAttribute("maxSdkVersion", K_ANDROID_NAMESPACE_URI, maxSdk); + + manifestNode.AppendChild(metaDataNode); + } + + internal static void AppendAndroidMetadataField(string manifestPath, XmlDocument xmlDoc, string name, string value) + { + var applicationNode = xmlDoc.SelectSingleNode("manifest/application"); + if (applicationNode == null) + throw new ArgumentException(string.Format("Missing 'application' node in '{0}'.", manifestPath)); + + var nodes = xmlDoc.SelectNodes("manifest/application/meta-data"); + if (nodes != null) + { + // Check if there is a 'meta-data' with the same name. + foreach (XmlNode node in nodes) + { + var element = node as XmlElement; + if (element == null) + continue; + + var elementName = element.GetAttribute("name", K_ANDROID_NAMESPACE_URI); + if (elementName == name) + { + element.SetAttribute("value", K_ANDROID_NAMESPACE_URI, value); + return; + } + } + } + + XmlElement metaDataNode = xmlDoc.CreateElement("meta-data"); + metaDataNode.SetAttribute("name", K_ANDROID_NAMESPACE_URI, name); + metaDataNode.SetAttribute("value", K_ANDROID_NAMESPACE_URI, value); + + applicationNode.AppendChild(metaDataNode); + } + + internal static string GetLauncherActivity(XmlDocument xmlDoc) + { + var applicationNode = xmlDoc.SelectSingleNode("manifest/application"); + if (applicationNode == null) + throw new ArgumentException($"Missing 'application' node in doc."); + + var nodes = xmlDoc.SelectNodes("manifest/application/activity"); + if (nodes != null) + { + foreach (XmlNode node in nodes) + { + var activityNode = node as XmlElement; + if (activityNode == null) + continue; + + if (activityNode.HasChildNodes) + { + var intentFilterNode = activityNode.SelectSingleNode("intent-filter"); + + if(intentFilterNode == null || !intentFilterNode.HasChildNodes) + continue; + + foreach (XmlElement childNode in intentFilterNode) + { + if(childNode == null) continue; + + // 判断 action/category 二者取其一 + if (childNode.Name == "action" && childNode.InnerXml.Contains("android.intent.action.MAIN")) + { + var activityName = activityNode.GetAttribute("name", K_ANDROID_NAMESPACE_URI); + return activityName; + } + + if (childNode.Name == "category" && childNode.InnerXml.Contains("android.intent.category.LAUNCHER")) + { + var activityName = activityNode.GetAttribute("name", K_ANDROID_NAMESPACE_URI); + return activityName; + } + } + } + } + } + + return V_DEFAULT_GURU_ACTIVITY; + } + + } +} + +#endif diff --git a/Runtime/GuruNoification/Editor/Android_PostCustomActivity.cs.meta b/Runtime/GuruNoification/Editor/Android_PostCustomActivity.cs.meta new file mode 100644 index 0000000..c51fbb5 --- /dev/null +++ b/Runtime/GuruNoification/Editor/Android_PostCustomActivity.cs.meta @@ -0,0 +1,3 @@ +fileFormatVersion: 2 +guid: 2e6de94961cc47abbabbc7e872085a98 +timeCreated: 1718934648 \ No newline at end of file diff --git a/Runtime/GuruNoification/Manager/NotificationAgentAndroid.cs b/Runtime/GuruNoification/Manager/NotificationAgentAndroid.cs index d2cdaae..33a087e 100644 --- a/Runtime/GuruNoification/Manager/NotificationAgentAndroid.cs +++ b/Runtime/GuruNoification/Manager/NotificationAgentAndroid.cs @@ -134,6 +134,7 @@ namespace Guru.Notification // 低版本处理方式 if (_notiStatus == STATUS_GRANTED) { + Debug.Log($"[SDK][Noti] --- #1 SDK {sdkInt} has already Grated!"); SetGrantStatus(true); // 已经允许了 callback?.Invoke(_notiStatus); @@ -141,9 +142,11 @@ namespace Guru.Notification } else { - if (_permissionStatus == PermissionStatus.NotRequested || + if (_permissionStatus == PermissionStatus.NotRequested || + _permissionStatus == PermissionStatus.RequestPending || AndroidNotificationCenter.ShouldShowPermissionToPostRationale) { + Debug.Log($"[SDK][Noti] --- #2 SDK {sdkInt} not requested -> open channel"); AndroidNotificationCenter.OpenNotificationSettings(FCM_DEFAULT_CHANNEL_ID); // 打开ChannelID } } @@ -151,6 +154,7 @@ namespace Guru.Notification } else if (hasPerm) { + Debug.Log($"[SDK][Noti] --- #3 SDK {sdkInt} :: Post permission has already granted!"); SetGrantStatus(true); // 已经允许了 callback?.Invoke(_notiStatus); @@ -159,6 +163,7 @@ namespace Guru.Notification { // 未允许 // 则请求弹窗 + Debug.Log($"[SDK][Noti] --- #3 SDK {sdkInt} :: Ask Post Permission"); Permission.RequestUserPermission(PERMISSION_POST_NOTIFICATION, SetupPermissionCallbacks()); } }); @@ -168,9 +173,9 @@ namespace Guru.Notification { if(_permissionCallbacks != null) DisposePermissionCallbacks(); _permissionCallbacks = new PermissionCallbacks(); - _permissionCallbacks.PermissionDenied += OnPermissionGranted; - _permissionCallbacks.PermissionDeniedAndDontAskAgain += OnPermissionGranted; _permissionCallbacks.PermissionGranted += OnPermissionGranted; + _permissionCallbacks.PermissionDenied += OnPermissionDenied; + _permissionCallbacks.PermissionDeniedAndDontAskAgain += OnPermissionDenied; return _permissionCallbacks; } @@ -179,8 +184,8 @@ namespace Guru.Notification if (_permissionCallbacks != null) { _permissionCallbacks.PermissionGranted -= OnPermissionGranted; - _permissionCallbacks.PermissionDenied -= PermissionDenied; - _permissionCallbacks.PermissionDeniedAndDontAskAgain -= PermissionDenied; + _permissionCallbacks.PermissionDenied -= OnPermissionDenied; + _permissionCallbacks.PermissionDeniedAndDontAskAgain -= OnPermissionDenied; _permissionCallbacks = null; } } @@ -204,7 +209,7 @@ namespace Guru.Notification /// 请求拒绝 /// /// - private void PermissionDenied(string permissionName) + private void OnPermissionDenied(string permissionName) { if (permissionName == PERMISSION_POST_NOTIFICATION) {