update: 新增 Notification 服务和对一个的 noti 查询功能

Signed-off-by: huyufei <yufei.hu@castbox.fm>
main
胡宇飞 2024-06-20 18:20:38 +08:00
parent 9024b8171c
commit 4e24169b25
17 changed files with 584 additions and 1 deletions

View File

@ -17,7 +17,9 @@
"Google.Play.Review",
"Google.Play.Common",
"Guru.LitJson",
"Unity.Advertisement.IosSupport"
"Unity.Advertisement.IosSupport",
"Unity.Notifications.Android",
"Unity.Notifications.iOS"
],
"includePlatforms": [],
"excludePlatforms": [],

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 1fef034d48ac449fb99531b40139954e
timeCreated: 1718844748

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 8c0dd2d4b63445c2828e05c10274d672
timeCreated: 1718845536

View File

@ -0,0 +1,6 @@
{
"name": "GuruNotification.Editor",
"includePlatforms": [
"Editor"
]
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e75025463d0c436482bf4c3dab674315
timeCreated: 1718845553

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: d6dd827f370441718ca2e49f3f603e4e
timeCreated: 1718845525

View File

@ -0,0 +1,15 @@
namespace Guru.Notification
{
using System;
public interface INotificationAgent
{
void Init();
string GetStatus();
bool IsAllowed();
void RequestPermission(Action<string> callback = null);
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 43e051a65e9b469b8b9e8a9f6b4be944
timeCreated: 1718844775

View File

@ -0,0 +1,247 @@
namespace Guru.Notification
{
using System;
using UnityEngine;
#if UNITY_ANDROID
using UnityEngine.Android;
using Unity.Notifications.Android;
#endif
public class NotificationAgentAndroid : INotificationAgent
{
public const string FCM_DEFAULT_CHANNEL_ID = "fcm_default_channel";
private const string STATUS_GRANTED = "granted";
private const string STATUS_DENIDED = "denied";
private const int REQUEST_PERMISSION_SDK_VERSION = 33;
private const string PERMISSION_POST_NOTIFICATION = "android.permission.POST_NOTIFICATIONS";
private bool _initOnce = false;
private string _notiStatus;
private bool _isPluginReady = false;
/// <summary>
/// 初始化
/// </summary>
public void Init()
{
if (!_initOnce) return;
SetGrantStatus(false);
#if UNITY_ANDROID
InitPlugins();
#endif
}
/// <summary>
/// 获取状态
/// </summary>
/// <returns></returns>
public string GetStatus()
{
if (!_initOnce) Init();
#if UNITY_ANDROID
UpdateNotiStatus();
#endif
return _notiStatus;
}
/// <summary>
/// 设置授权状态
/// </summary>
/// <param name="flag"></param>
private void SetGrantStatus(bool flag = false)
{
_notiStatus = flag ? STATUS_GRANTED : STATUS_DENIDED;
}
public bool IsAllowed()
{
return _notiStatus == STATUS_GRANTED;
}
public void RequestPermission(Action<string> callback = null)
{
#if UNITY_ANDROID
RequestAndroidPermission(callback);
#endif
}
// -------------------- Android 获取状态逻辑 --------------------
#if UNITY_ANDROID
private PermissionStatus _permissionStatus;
private void TryExecute(Action handler)
{
try
{
handler?.Invoke();
}
catch (Exception ex)
{
Debug.LogError(ex);
}
}
/// <summary>
/// 初始化插件
/// </summary>
private void InitPlugins()
{
_isPluginReady = AndroidNotificationCenter.Initialize();
if (!_isPluginReady)
{
Debug.LogError($"[Noti][AND] --- AndroidNotificationCenter init failed!");
// TODO: 处理初始化失败的情况
return;
}
UpdateNotiStatus();
}
/// <summary>
/// 更新 Notification 状态码
/// </summary>
private void UpdateNotiStatus()
{
if (!_isPluginReady) return;
TryExecute(() =>
{
_permissionStatus = AndroidNotificationCenter.UserPermissionToPost;
switch (_permissionStatus)
{
case PermissionStatus.Allowed:
SetGrantStatus(true);
break;
default:
_notiStatus = STATUS_DENIDED;
break;
}
});
}
private Action<string> _onPermissionCallback;
private PermissionCallbacks _permissionCallbacks;
private void RequestAndroidPermission(Action<string> callback = null)
{
if (!_isPluginReady) return;
_onPermissionCallback = callback;
UpdateNotiStatus();
TryExecute(() =>
{
bool hasPerm = Permission.HasUserAuthorizedPermission(PERMISSION_POST_NOTIFICATION);
var sdkInt = GetAndroidSDKVersion();
if (sdkInt < REQUEST_PERMISSION_SDK_VERSION)
{
// 低版本处理方式
if (_notiStatus == STATUS_GRANTED)
{
SetGrantStatus(true);
// 已经允许了
callback?.Invoke(_notiStatus);
}
else
{
if (_permissionStatus == PermissionStatus.NotRequested ||
AndroidNotificationCenter.ShouldShowPermissionToPostRationale)
{
AndroidNotificationCenter.OpenNotificationSettings(FCM_DEFAULT_CHANNEL_ID); // 打开ChannelID
}
}
}
else if (hasPerm)
{
SetGrantStatus(true);
// 已经允许了
callback?.Invoke(_notiStatus);
}
else
{
// 未允许
// 则请求弹窗
Permission.RequestUserPermission(PERMISSION_POST_NOTIFICATION, SetupPermissionCallbacks());
}
});
}
private PermissionCallbacks SetupPermissionCallbacks()
{
if(_permissionCallbacks != null) DisposePermissionCallbacks();
_permissionCallbacks = new PermissionCallbacks();
_permissionCallbacks.PermissionDenied += OnPermissionGranted;
_permissionCallbacks.PermissionDeniedAndDontAskAgain += OnPermissionGranted;
_permissionCallbacks.PermissionGranted += OnPermissionGranted;
return _permissionCallbacks;
}
private void DisposePermissionCallbacks()
{
if (_permissionCallbacks != null)
{
_permissionCallbacks.PermissionGranted -= OnPermissionGranted;
_permissionCallbacks.PermissionDenied -= PermissionDenied;
_permissionCallbacks.PermissionDeniedAndDontAskAgain -= PermissionDenied;
_permissionCallbacks = null;
}
}
/// <summary>
/// 请求通过
/// </summary>
/// <param name="permissionName"></param>
private void OnPermissionGranted(string permissionName)
{
if (permissionName == PERMISSION_POST_NOTIFICATION)
{
_notiStatus = STATUS_GRANTED;
_onPermissionCallback?.Invoke(_notiStatus);
}
DisposePermissionCallbacks();
}
/// <summary>
/// 请求拒绝
/// </summary>
/// <param name="permissionName"></param>
private void PermissionDenied(string permissionName)
{
if (permissionName == PERMISSION_POST_NOTIFICATION)
{
_notiStatus = STATUS_DENIDED;
_onPermissionCallback?.Invoke(_notiStatus);
}
DisposePermissionCallbacks();
}
private int GetAndroidSDKVersion()
{
int sdkInt = 999;
TryExecute(() =>
{
using (AndroidJavaClass jc = new AndroidJavaClass("android.os.Build$VERSION"))
{
sdkInt = jc.GetStatic<int>("SDK_INT");
Debug.LogWarning($"[SDK] --- Android SDK Version:{sdkInt}");
}
});
return sdkInt;
}
#endif
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 554fcea56ce74a80a2424e5c037ce6c0
timeCreated: 1718844764

View File

@ -0,0 +1,129 @@
namespace Guru.Notification
{
using System;
using UnityEngine;
#if UNITY_IOS
using System.Threading.Tasks;
using Unity.Notifications.iOS;
#endif
public class NotificationAgentIOS : INotificationAgent
{
private const string STATUS_GRANTED = "granted";
private const string STATUS_DENIDED = "denied";
private const string STATUS_PROVISIONAL = "provisional";
private const string STATUS_NOT_DETERMINED = "not_determined";
private static bool _initOnce;
private static int _waitSeconds = 30;
private string _notiStatus;
public void Init()
{
if (_initOnce) return;
_initOnce = true;
_notiStatus = STATUS_NOT_DETERMINED;
#if UNITY_IOS
InitPlugins();
#endif
}
public string GetStatus()
{
if (!_initOnce) Init();
#if UNITY_IOS
UpdateStatus();
#endif
return _notiStatus;
}
public bool IsAllowed()
{
return _notiStatus == STATUS_GRANTED;
}
public void RequestPermission(Action<string> callback = null)
{
if (!_initOnce) Init();
#if UNITY_IOS
RequestIOSPermission(callback);
#endif
}
#if UNITY_IOS
private void InitPlugins()
{
UpdateStatus();
}
/// <summary>
/// 更新状态
/// </summary>
private void UpdateStatus()
{
var status = iOSNotificationCenter.GetNotificationSettings().AuthorizationStatus;
switch (status)
{
case AuthorizationStatus.Authorized:
_notiStatus = STATUS_GRANTED;
break;
case AuthorizationStatus.Denied:
_notiStatus = STATUS_DENIDED;
break;
case AuthorizationStatus.NotDetermined:
_notiStatus = STATUS_NOT_DETERMINED;
break;
case AuthorizationStatus.Provisional:
_notiStatus = STATUS_PROVISIONAL;
break;
default:
Debug.Log($"[SDK][Noti][iOS] --- Unmarked AuthorizationStatus: {status}");
break;
}
}
/// <summary>
/// 请求 IOS 的推送
/// </summary>
/// <param name="callback"></param>
private async void RequestIOSPermission(Action<string> callback = null)
{
Debug.Log($"[SDK][Noti][iOS] --- RequestIOSPermission start");
int timePassed = 0;
using (var req = new AuthorizationRequest(AuthorizationOption.Alert | AuthorizationOption.Badge, true))
{
while (!req.IsFinished && timePassed < _waitSeconds)
{
timePassed++;
await Task.Delay(1000);
};
if (timePassed >= _waitSeconds)
{
Debug.LogWarning($"[SDK][Noti][iOS] --- RequestIOSPermission timeout");
}
UpdateStatus();
callback?.Invoke(_notiStatus);
Debug.Log($"[SDK][Noti][iOS] --- User Selected: {_notiStatus}");
}
}
#endif
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 5068c6e238e74251955320761c96182b
timeCreated: 1718871877

View File

@ -0,0 +1,58 @@
namespace Guru.Notification
{
using System;
using System.Threading.Tasks;
using UnityEngine;
/// <summary>
/// For Editor to use Notifications
/// </summary>
public class NotificationAgentStub: INotificationAgent
{
private const string STATUS_GRANTED = "granted";
private const string STATUS_DENIDED = "denied";
private const string STATUS_NOT_DETERMINED = "not_determined";
private Action<string> _onPermissionCallback;
private float _delaySeconds = 1.0f;
private string EditorGrantedStatus
{
get => PlayerPrefs.GetString(nameof(EditorGrantedStatus), STATUS_NOT_DETERMINED);
set => PlayerPrefs.SetString(nameof(EditorGrantedStatus), value);
}
public void Init()
{
Debug.Log($"[SDK][Noti][EDT] --- NotificationAgentStub Init: {EditorGrantedStatus}");
}
public string GetStatus() => EditorGrantedStatus;
public bool IsAllowed()
{
return EditorGrantedStatus == STATUS_GRANTED;
}
public void RequestPermission(Action<string> callback = null)
{
Debug.Log($"[SDK][Noti][EDT] --- RequestPermission ---");
_onPermissionCallback = callback;
DelayCallPermissionHandle();
}
/// <summary>
/// 延迟模拟回调
/// </summary>
private async void DelayCallPermissionHandle()
{
await Task.Delay((int)(1000 * _delaySeconds));
EditorGrantedStatus = STATUS_GRANTED;
_onPermissionCallback?.Invoke(EditorGrantedStatus);
}
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: 6550e85d25634c46b7a57c490dcea173
timeCreated: 1718850440

View File

@ -0,0 +1,98 @@
namespace Guru.Notification
{
using UnityEngine;
using System;
/// <summary>
/// 消息管理器
/// </summary>
public class NotificationService
{
// 服务版本号
public const string Version = "0.0.1";
// 初始化标志位
private static bool _initOnce;
private static string DEFAULT_USER_STATUS = "not_determined";
#region 初始化
private static INotificationAgent _agent;
internal static INotificationAgent Agent
{
get
{
if (_agent == null)
{
_agent = GetAgent();
}
return _agent;
}
}
private static INotificationAgent GetAgent()
{
#if UNITY_EDITOR
return new NotificationAgentStub();
#elif UNITY_ANDROID
return new NotificationAgentAndroid();
#elif UNITY_IOS
return new NotificationAgentIOS();
#endif
return null;
}
/// <summary>
/// 初始化
/// </summary>
public static void Initialize()
{
if (_initOnce) return;
_initOnce = true;
Agent?.Init(); // 初始化代理
}
#endregion
#region 接口
/// <summary>
/// 拉起 Noti 请求
/// </summary>
/// <param name="callback"></param>
public static void RequestPermission(Action<string> callback = null)
{
if (Agent != null)
{
Agent.RequestPermission(callback);
return;
}
callback?.Invoke(DEFAULT_USER_STATUS);
}
public static bool IsPermissionGranted()
{
return Agent?.IsAllowed() ?? false;
}
public static string GetStatus()
{
return Agent?.GetStatus() ?? DEFAULT_USER_STATUS;
}
#endregion
}
}

View File

@ -0,0 +1,3 @@
fileFormatVersion: 2
guid: e0f31750e8bc48a6a5e4f56ee2d5e1cc
timeCreated: 1718846076

View File

@ -12,6 +12,7 @@
"dependencies": {
"com.unity.ads.ios-support": "1.2.0",
"com.unity.editorcoroutines": "1.0.0",
"com.unity.mobile.notifications": "2.2.2",
"com.unity.mobile.android-logcat": "1.3.2",
"com.unity.purchasing": "4.10.0"
}