using Android.App;
using Android.Content;
using AndroidX.Core.App;
using System.Collections.Generic;
using System.Common;
using AndroidApplication = Android.App.Application;
using JClass = Java.Lang.Class;
namespace System.Application.Services.Implementation
{
///
public abstract class PlatformNotificationServiceImpl : INotificationService
where TNotificationType : Enum
where TNotificationChannelType : Enum
{
protected readonly IPlatformNavigationService nav;
public PlatformNotificationServiceImpl(IPlatformNavigationService nav)
{
this.nav = nav;
}
protected virtual int GetNotifyId(TNotificationType notificationType)
=> Enum2.ConvertToInt32(notificationType);
public bool AreNotificationsEnabled()
{
// https://www.jianshu.com/p/1e27efb1dcac
var context = AndroidApplication.Context;
var manager = NotificationManagerCompat.From(context);
return manager.AreNotificationsEnabled();
}
public void Cancel(TNotificationType notificationType)
{
var context = AndroidApplication.Context;
var manager = NotificationManagerCompat.From(context);
var id = GetNotifyId(notificationType);
manager.Cancel(id);
}
public void CancelAll()
{
var context = AndroidApplication.Context;
var manager = NotificationManagerCompat.From(context);
manager.CancelAll();
}
///
/// 初始化通知渠道
///
///
public void InitNotificationChannels(Context context)
{
var manager = NotificationManagerCompat.From(context);
var items = Enum.GetValues(typeof(TNotificationChannelType));
foreach (TNotificationChannelType item in items)
{
CreateNotificationChannel(manager, item);
}
}
///
/// 获取渠道的ID
/// 参考:https://developer.android.google.cn/reference/android/app/NotificationChannel?hl=en#public-constructors
///
///
///
protected virtual string GetChannelId(TNotificationChannelType notificationChannelType)
{
var valueInt = Enum2.ConvertToInt32(notificationChannelType);
return "chan_" + valueInt;
}
///
/// 获取渠道的用户可见名称
/// 建议的最大长度为40个字符,如果该值太长,可能会被截断
/// 参考:https://developer.android.google.cn/reference/android/app/NotificationChannel?hl=en#setName%28java.lang.CharSequence%29
///
///
///
protected abstract string GetName(TNotificationChannelType notificationChannelType);
///
/// 获取渠道的用户可见描述
/// 建议的最大长度为300个字符,如果该值太长,可能会被截断
/// 参考:https://developer.android.google.cn/reference/android/app/NotificationChannel?hl=en#setDescription%28java.lang.String%29
///
///
///
protected abstract string GetDescription(TNotificationChannelType notificationChannelType);
protected abstract void CreateNotificationChannel(TNotificationChannelType notificationChannelType, NotificationChannel notificationChannel);
///
/// 创建通知渠道 >= Android O
///
///
///
///
protected NotificationChannel CreateNotificationChannel(NotificationManagerCompat manager,
TNotificationChannelType notificationChannelType)
{
var channelId = GetChannelId(notificationChannelType);
var name = GetName(notificationChannelType);
var description = GetDescription(notificationChannelType);
var level = GetNotificationImportance(GetImportanceLevel(notificationChannelType));
var notificationChannel = manager.GetNotificationChannel(channelId);
if (notificationChannel == null)
{
notificationChannel = new NotificationChannel(channelId, name, level)
{
Description = description,
};
CreateNotificationChannel(notificationChannelType, notificationChannel);
manager.CreateNotificationChannel(notificationChannel);
}
return notificationChannel;
}
///
/// 获取所属的通知渠道
///
///
///
protected abstract TNotificationChannelType GetChannel(TNotificationType notificationType);
///
/// 获取渠道的重要性级别
///
///
///
protected abstract NotificationImportanceLevel GetImportanceLevel(TNotificationChannelType notificationChannelType);
///
/// 获取渠道的优先级 Android 7.1 and lower
/// 参考:https://developer.android.google.cn/training/notify-user/channels#importance
///
///
///
static int GetNotificationPriority(NotificationImportanceLevel level) => level switch
{
NotificationImportanceLevel.Low => NotificationCompat.PriorityMin,
NotificationImportanceLevel.Medium => NotificationCompat.PriorityLow,
NotificationImportanceLevel.High => NotificationCompat.PriorityDefault,
NotificationImportanceLevel.Urgent => NotificationCompat.PriorityHigh,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
};
///
/// 获取渠道的重要性级别 Android 8.0 and higher
/// 参考:https://developer.android.google.cn/training/notify-user/channels#importance
///
///
///
static NotificationImportance GetNotificationImportance(NotificationImportanceLevel level) => level switch
{
NotificationImportanceLevel.Low => NotificationImportance.Min,
NotificationImportanceLevel.Medium => NotificationImportance.Low,
NotificationImportanceLevel.High => NotificationImportance.Default,
NotificationImportanceLevel.Urgent => NotificationImportance.High,
_ => throw new ArgumentOutOfRangeException(nameof(level), level, null),
};
protected NotificationCompat.Builder BuildNotify(
Context context,
NotificationManagerCompat manager,
string text,
TNotificationType notificationType,
bool? autoCancel = null,
string? title = null,
JClass? entrance = null,
IReadOnlyCollection? actions = null)
{
var channelType = GetChannel(notificationType);
var channel = CreateNotificationChannel(manager, channelType);
var builder = new NotificationCompat.Builder(context, channel.Id);
var level = GetImportanceLevel(channelType);
builder.SetPriority(GetNotificationPriority(level));
builder.SetSmallIcon(R.drawable.ic_stat_notify_msg);
title ??= R.@string.app_name;
builder.SetContentTitle(title);
builder.SetContentText(text);
if (autoCancel.HasValue) builder.SetAutoCancel(autoCancel.Value);
if (actions != default && actions.Count > 0 && actions.Count <= 3)
{
foreach (var item in actions)
{
builder.AddAction(item);
}
}
if (entrance != null)
{
var intent = new Intent(context, entrance);
var pendingIntent = PendingIntent.GetActivity(context, 0, intent, 0);
builder.SetContentIntent(pendingIntent);
}
return builder;
}
public void Notify(string text,
TNotificationType notificationType,
bool autoCancel,
string? title,
Type? entrance)
{
var notificationEntrance = entrance != null ?
nav.GetActivityType(entrance).GetJClass() : R.activities.entrance;
var context = AndroidApplication.Context;
var manager = NotificationManagerCompat.From(context);
var builder = BuildNotify(context, manager, text, notificationType,
autoCancel, title, entrance: notificationEntrance);
var notifyId = GetNotifyId(notificationType);
manager.Notify(notifyId, builder.Build());
}
public Progress NotifyDownload(
string text,
TNotificationType notificationType,
string? title)
{
var context = AndroidApplication.Context;
var manager = NotificationManagerCompat.From(context);
var builder = BuildNotify(context, manager,
text: string.Format(text, 0),
notificationType,
title: title);
// 进度单位说明
// 通用层采用 float 浮点数作为进度值,范围从0~100,保留两位小数
// 平台层采用 int 整型作为进度值,范围从0~10000,转换需要 乘 100
const int unit_convert_multiple = 100; // 从float到int转换单位倍数
const int PROGRESS_MAX = 100 * unit_convert_multiple; // 进度条满值
// 发出零进度的初始通知
// Issue the initial notification with zero progress
builder.SetProgress(PROGRESS_MAX, 0, false);
var notifyId = GetNotifyId(notificationType);
manager.Notify(notifyId, builder.Build());
// 在这里完成跟踪进度的工作。
// Do the job here that tracks the progress.
// 通常,这应该在一个
// Usually, this should be in a
// 工作线程
// worker thread
// 要显示进度,请更新PROGRESS_CURRENT并使用以下命令更新通知:
// To show progress, update PROGRESS_CURRENT and update the notification with:
// builder.setProgress(PROGRESS_MAX, PROGRESS_CURRENT, false);
// notificationManager.notify(notificationId, builder.build());
// 完成后,再次更新通知以删除进度条
// When done, update the notification one more time to remove the progress bar
void Handler(float current)
{
var currentInt32 = (current * unit_convert_multiple).ToInt32();
if (currentInt32 >= PROGRESS_MAX)
{
// 这将使100%时直接取消通知,并不会在UI上显示
// 报告进度值满的操作应当是幂等的
if (manager != null)
{
manager.Cancel(notifyId);
manager = null;
}
// 手动释放相关资源
context = null;
builder = null;
}
else
{
// 在报告进度值满后不可再更改进度
if (builder == null) throw new ArgumentNullException(nameof(builder));
if (manager == null) throw new ArgumentNullException(nameof(manager));
builder.SetProgress(PROGRESS_MAX, currentInt32, false);
builder.SetContentText(string.Format(text, currentInt32));
manager.Notify(notifyId, builder.Build());
}
}
return new Progress(Handler);
}
}
}