using System.Collections.Generic; using System; using UnityEngine; using Unity.Notifications; using asap.core; using GameCore; using System.Linq; using game; using UniRx; using Notification = Unity.Notifications.Notification; using System.Threading.Tasks; public interface ILocalNotificationService { public System.Threading.Tasks.Task Init(); public bool IsNoticeOpen { get; } public void OpenSettings(); public void ScheduleOneTimeNotification(int notificationID, DateTime fireTime, string title, string text); public void ScheduleDailyNotification(int notificationID, DateTime fireTime, string title, string text); public void ClearAllNotification(); public void ClearNotification(int notificationID); public void ScheduleDefaultNotification(); } public class LocalNotificationService : ILocalNotificationService { private List _notificationList; #if UNITY_ANDROID public bool IsNoticeOpen => Unity.Notifications.Android.AndroidNotificationCenter.UserPermissionToPost == Unity.Notifications.Android.PermissionStatus.Allowed; #else public bool IsNoticeOpen => Unity.Notifications.iOS.iOSNotificationCenter.GetNotificationSettings().AuthorizationStatus == Unity.Notifications.iOS.AuthorizationStatus.Authorized; #endif bool isInit = false; bool initing = false; private DefaultSettingService promptSettings; private Task initialization; private Task opening; public LocalNotificationService() { _notificationList = GContext.container.Resolve().TbNotification.DataList; } internal void RequirePromptOwner(ISettingService expected = null) { if (expected != null && (promptSettings != null || expected is DefaultSettingService setting && setting.HasKVPromptOwner) && !ReferenceEquals(expected, promptSettings)) throw DefaultSettingService.PromptFailure("ShowNotice: notification/settings owner mismatch."); promptSettings?.RequirePromptOwner(); } internal void PreflightFormalPromptSettings(DefaultSettingService settings) { if (settings == null || !isInit || initing || (initialization != null && !initialization.IsCompleted) || (opening != null && !opening.IsCompleted) || (promptSettings != null && !ReferenceEquals(promptSettings, settings))) throw DefaultSettingService.PromptFailure("ShowNotice: notification handoff is not idle."); } internal void BindFormalPromptSettings(DefaultSettingService settings) { PreflightFormalPromptSettings(settings); if (!settings.HasKVPromptOwner) throw DefaultSettingService.PromptFailure("ShowNotice: notification owner not handed off."); settings.RequirePromptOwner(); if (promptSettings == null) promptSettings = settings; RequirePromptOwner(settings); } public Task Init() { RequirePromptOwner(); if (isInit) return Task.CompletedTask; if (initialization != null && !initialization.IsCompleted) return initialization; return initialization = InitCoreAsync(); } private async Task InitCoreAsync() { RequirePromptOwner(); #if UNITY_EDITOR await System.Threading.Tasks.Task.Yield(); #else Debug.Log("[LocalNotificationService::Init]"); var args = NotificationCenterArgs.Default; args.AndroidChannelId = "default"; args.AndroidChannelName = "Notifications"; args.AndroidChannelDescription = "Main notifications"; NotificationCenter.Initialize(args); Debug.Log("[LocalNotificationService::Init] Channel Inited"); await NotificationCenter.RequestPermission(); RequirePromptOwner(); ScheduleDefaultNotification(); GContext.OnEvent().Subscribe(ScheduleRankNotification); #endif RequirePromptOwner(); isInit = true; } public void ClearAllNotification() { if (!isInit) return; rankNotifyScheduled = false; NotificationCenter.CancelAllScheduledNotifications(); NotificationCenter.CancelAllDeliveredNotifications(); } public void ClearNotification(int notificationID) { if (!IsNoticeOpen || !isInit) { return; } NotificationCenter.CancelScheduledNotification(notificationID); NotificationCenter.CancelDeliveredNotification(notificationID); } public async void OpenSettings() { try { await OpenSettingsAsync(); } catch (Exception ex) { game.DefaultSettingService.LogPromptFailure(ex); } } internal Task OpenSettingsAsync() { if (promptSettings == null && GContext.container.Resolve() is DefaultSettingService pending) pending.RequirePromptOwner(); RequirePromptOwner(); if (initing && opening == null) throw DefaultSettingService.PromptFailure("ShowNotice: reentrant permission request."); if (opening != null && !opening.IsCompleted) return opening; return opening = OpenSettingsCoreAsync(); } private async Task OpenSettingsCoreAsync() { ISettingService settings = promptSettings ?? GContext.container.Resolve(); RequirePromptOwner(settings); (settings as DefaultSettingService)?.RequirePromptOwner(); initing = true; try { if (!isInit) await Init(); else NotificationCenter.OpenNotificationSettings(); RequirePromptOwner(settings); (settings as DefaultSettingService)?.RequirePromptOwner(); settings.ShowNotice = true; } finally { initing = false; } } public void ScheduleDailyNotification(int notificationID, DateTime fireTime, string title, string text) { if (!IsNoticeOpen || !isInit) { Debug.Log("[LocalNotificationService::ScheduleDailyNotification] Not Open"); return; } NotificationCenter.CancelScheduledNotification(notificationID); NotificationCenter.CancelDeliveredNotification(notificationID); var notification = new Notification() { Title = title, Text = text, Identifier = notificationID }; NotificationCenter.ScheduleNotification(notification, new NotificationDateTimeSchedule(fireTime, NotificationRepeatInterval.Daily)); } public void ScheduleOneTimeNotification(int notificationID, DateTime fireTime, string title, string text) { if (!IsNoticeOpen || !isInit) return; NotificationCenter.CancelScheduledNotification(notificationID); NotificationCenter.CancelDeliveredNotification(notificationID); var notification = new Notification() { Title = title, Text = text, Identifier = notificationID }; NotificationCenter.ScheduleNotification(notification, new NotificationDateTimeSchedule(fireTime, NotificationRepeatInterval.OneTime)); } public void ScheduleDefaultNotification() { if (!IsNoticeOpen || !isInit) return; Debug.Log("[LocalNotificationService::SendNotification] Daily"); var dailyNotification = _notificationList.Where(x => x.Type == 0); foreach (var item in dailyNotification) { var dateTime = DateTime.SpecifyKind(DateTime.Parse(item.NotTime), DateTimeKind.Utc); ScheduleDailyNotification(item.ID, dateTime.ToLocalTime(), LocalizationMgr.GetText(item.Title_l10n_key), LocalizationMgr.GetText(item.Content_l10n_key)); } Debug.Log("[LocalNotificationService::SendNotification] Daily Done"); } private bool rankNotifyScheduled = false; private void ScheduleRankNotification(EventRankData evt) { try { RequirePromptOwner(); } catch (InvalidOperationException ex) { Debug.LogError(ex.Message); return; } if (rankNotifyScheduled || !IsNoticeOpen || !isInit) return; Debug.Log("[LocalNotificationService::SendNotification] RankEvent"); var notification = _notificationList.FirstOrDefault(x => x.Type == 1); if (notification == null) return; // var fireTime = ConvertTools.GetDateTimeYMD(ZZTimeHelper.UtcNow().UtcNowOffset()); // var dayTime = DateTime.Parse(notification.NotTime); // fireTime = fireTime.AddDays(1).AddHours(dayTime.Hour).AddMinutes(dayTime.Minute).AddSeconds(dayTime.Second); var now = ZZTimeHelper.UtcNow(); var fireTime = DateTime.SpecifyKind(DateTime.Parse(notification.NotTime), DateTimeKind.Utc); fireTime = fireTime.AddDays(now <= fireTime ? 0 : 1); Debug.Log($"Try schedule Rank Notification at {fireTime}."); ScheduleOneTimeNotification(notification.ID, fireTime.ToLocalTime(), LocalizationMgr.GetText(notification.Title_l10n_key), LocalizationMgr.GetText(notification.Content_l10n_key)); rankNotifyScheduled = true; Debug.Log("[LocalNotificationService::SendRankEventNotification] "); } }