Files
ft/Client/Assets/Scripts/Services/ILocalNotificationService.cs

253 lines
9.8 KiB
C#

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<cfg.Notification> _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;
DefaultSettingService promptSettings;
Task initialization;
Task opening;
public LocalNotificationService()
{
_notificationList = GContext.container.Resolve<cfg.Tables>().TbNotification.DataList;
}
internal LocalNotificationService(cfg.Tables tables)
{
_notificationList = (tables ?? throw new ArgumentNullException(nameof(tables))).TbNotification.DataList;
}
internal LocalNotificationService(DefaultSettingService settings, cfg.Tables tables)
{
if (settings == null || !settings.HasKVPromptOwner)
throw new ArgumentException("Fixed KV settings required.", nameof(settings));
promptSettings = settings;
_notificationList = (tables ?? throw new ArgumentNullException(nameof(tables))).TbNotification.DataList;
RequirePromptOwner();
}
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);
}
internal bool HasFormalPromptSettings(DefaultSettingService settings) =>
ReferenceEquals(promptSettings, settings);
public Task Init()
{
RequirePromptOwner();
if (isInit) return Task.CompletedTask;
if (initialization != null && !initialization.IsCompleted) return initialization;
return initialization = InitCoreAsync();
}
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<EventRankData>().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<ISettingService>() 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();
}
async Task OpenSettingsCoreAsync()
{
ISettingService settings = promptSettings ?? GContext.container.Resolve<ISettingService>();
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($"<color=#00d6b9>Try schedule Rank Notification at {fireTime}.</color>");
ScheduleOneTimeNotification(notification.ID, fireTime.ToLocalTime(),
LocalizationMgr.GetText(notification.Title_l10n_key),
LocalizationMgr.GetText(notification.Content_l10n_key));
rankNotifyScheduled = true;
Debug.Log("[LocalNotificationService::SendRankEventNotification] ");
}
}