unity3d 之本地推送
参考:http://blog.csdn.net/azhou_hui/article/details/50790870
1. 本地推送主要包括在android和ios上,下面所有的代码都是本人写的,经过测试是没有问题的,已经运用到项目中了。首先是接口INotification:
using System; public interface INotification : IDisposable { /// <summary> /// 注册一个推送 /// </summary> /// <param name="id">消息唯一标识</param> /// <param name="name">消息弹出一时在手机屏幕最上方显示的文字,对苹果无效</param> /// <param name="title">通知栏中消息的标题</param> /// <param name="content">通知栏中消息的正文</param> /// <param name="triggerTime">触发的时间</param> /// <param name="repeat">是否要每日重复触发</param> void Register(int id, string name, string title, string content, DateTime triggerTime, bool repeat); /// <summary> /// 取消一个推送 /// </summary> /// <param name="id">消息唯一标识</param> void Unregister(int id); /// <summary> /// 取消所有推送 /// </summary> void ClearAll(); }
2. android的实现:
#if UNITY_ANDROID using System; using UnityEngine; public class AndroidNotification : INotification { AndroidJavaObject m_javaObj = new AndroidJavaObject("com.example.localpush.AlarmReceiver"); public void Register(int id, string name, string title, string content, DateTime triggerTime, bool repeat) { int secondsFromNow = (int)(triggerTime - DateTime.Now).TotalSeconds; m_javaObj.CallStatic("Register", new object[6] { id, name, title, content, secondsFromNow, repeat }); } public void Unregister(int id) { m_javaObj.CallStatic("Unregister", id); } public void ClearAll() { var types = Enum.GetValues(typeof(NotificationType)); for (int i = 0; i < types.Length * 100; i++) Unregister(i); } #region IDisposable public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) { if (disposing) { m_javaObj.Dispose(); m_javaObj = null; } } ~AndroidNotification() { Dispose(false); } #endregion } #endif
其中 "com.example.localpush.AlarmReceiver" 中的java代码如下:
package com.example.localpush; import java.util.ArrayList; import java.util.Calendar; import android.app.Activity; import android.app.AlarmManager; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.util.Log; import com.unity3d.player.UnityPlayer; public class AlarmReceiver extends BroadcastReceiver { static final int NotificationCount = 10; static final int RepeatInterval = 24 * 60 * 60 * 1000;// 86400000 // Inner class static class BleachNotification { public int Id; public String Name; public String Title; public String Content; public long TriggerMillis; public boolean Repeat; public BleachNotification(int id, String name, String title, String content, long triggerMillis, boolean repeat) { Id = id; Name=name; Title = title; Content = content; TriggerMillis = repeat?GetNextRepeatTime(triggerMillis):triggerMillis; Repeat = repeat; } private long GetNextRepeatTime(long time) { long now = System.currentTimeMillis(); while(time<now) time+=RepeatInterval; return time; } } @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d("Unity", "Bleach: onReceive......................" + action); if (action!=null && action.equals("android.intent.action.BOOT_COMPLETED")) { for (BleachNotification bn : LoadAllNotification()) Register(bn, false); } else { if (!intent.getBooleanExtra("repeat", true)) { int id = intent.getIntExtra("id", -1); if (id != -1) DisableNotification(context, id); } NotificationManager nm=(NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); ApplicationInfo applicationInfo=null; try { final PackageManager pm=context.getPackageManager(); applicationInfo = pm.getApplicationInfo(context.getPackageName(),PackageManager.GET_META_DATA); } catch (NameNotFoundException e) { Log.d("Unity","Bleach: onReceive failed, reason: get applicationInfo failed."); } Notification notification=new Notification(applicationInfo.icon,intent.getStringExtra("name"),System.currentTimeMillis()); Intent notificationIntent = context.getPackageManager().getLaunchIntentForPackage(context.getPackageName()); notificationIntent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP); PendingIntent pi = PendingIntent.getActivity(context, 0, notificationIntent, 0); notification.setLatestEventInfo(context, intent.getStringExtra("title"), intent.getStringExtra("content"), pi); // Notify nm.notify(intent.getIntExtra("id",0),notification); Log.d("Unity","Bleach: notify succeed!"); } } public static void Register(int id, String name, String title, String content, int secondsFromNow, boolean repeat) { Calendar calendar=Calendar.getInstance(); calendar.setTimeInMillis(System.currentTimeMillis()); calendar.add(Calendar.SECOND, secondsFromNow); long time=calendar.getTimeInMillis(); Register(new BleachNotification(id,name,title,content,time,repeat),true); } private static void Register(BleachNotification bn,boolean isSave) { Activity activity =UnityPlayer.currentActivity; Intent intent =new Intent(activity, AlarmReceiver.class); intent.putExtra("name", bn.Name); intent.putExtra("title", bn.Title); intent.putExtra("content", bn.Content); intent.putExtra("id", bn.Id); intent.putExtra("repeat",bn.Repeat); PendingIntent pi=PendingIntent.getBroadcast(activity, bn.Id, intent, PendingIntent.FLAG_CANCEL_CURRENT); // Schedule the alarm! AlarmManager am = (AlarmManager)activity.getSystemService(Context.ALARM_SERVICE); if(bn.Repeat) am.setRepeating(AlarmManager.RTC_WAKEUP, bn.TriggerMillis, RepeatInterval, pi); else am.set(AlarmManager.RTC_WAKEUP,bn.TriggerMillis, pi); if(isSave) SaveNotification(bn); Log.d("Unity", "Bleach: Start Alarm...,id: "+bn.Id+ " name: "+bn.Name+ " title: "+bn.Title+ " content: "+bn.Content+ " triggerMillis: "+bn.TriggerMillis+ " currentTime:"+System.currentTimeMillis()+ " repeat: "+bn.Repeat); } public static void Unregister(int id) { Activity activity = UnityPlayer.currentActivity; Intent intent = new Intent(activity, AlarmReceiver.class); PendingIntent pi = PendingIntent.getBroadcast(activity, id, intent, PendingIntent.FLAG_CANCEL_CURRENT); AlarmManager am = (AlarmManager)activity.getSystemService(Context.ALARM_SERVICE); am.cancel(pi); NotificationManager mNM = (NotificationManager)activity.getSystemService(Context.NOTIFICATION_SERVICE); mNM.cancel(id); DisableNotification(activity, id); Log.d("Unity","Bleach: Cancel, id: "+id); } private static void DisableNotification(Context context,int id) { SharedPreferences sp = context.getSharedPreferences("Notification", Context.MODE_APPEND); Editor edit = sp.edit(); String sid = String.valueOf(id); edit.putInt(sid, -1); edit.commit(); } private static void SaveNotification(BleachNotification notification) { SharedPreferences sp = UnityPlayer.currentActivity.getSharedPreferences("Notification", Context.MODE_APPEND); Editor edit = sp.edit(); String sid = String.valueOf(notification.Id); edit.putInt(sid, 1); edit.putString(sid + "name", notification.Name); edit.putString(sid + "title", notification.Title); edit.putString(sid + "content", notification.Content); edit.putLong(sid + "triggerTime", notification.TriggerMillis); edit.putBoolean(sid + "repeat", notification.Repeat); edit.commit(); } private static ArrayList<BleachNotification> LoadAllNotification() { ArrayList<BleachNotification> l = new ArrayList<BleachNotification>(); SharedPreferences sp = UnityPlayer.currentActivity.getSharedPreferences("Notification", Context.MODE_APPEND); int index = 0; while (index < NotificationCount) { String id = String.valueOf(index); if (sp.getInt(id, -1) != -1) { BleachNotification bn=new BleachNotification( index, sp.getString(id + "name", ""), sp.getString(id + "title", ""), sp.getString(id + "content",""), sp.getLong(id + "triggerTime", 0), sp.getBoolean(id + "repeat", false) ); l.add(bn); } index++; } return l; } }
manifest配置为:
<!-- Add by tj[--> <receiver android:name="com.example.localpush.AlarmReceiver"> <intent-filter> <action android:name="android.intent.action.BOOT_COMPLETED" /> </intent-filter> </receiver> <activity android:name="com.unity3d.player.UnityPlayerProxyActivity" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" > </activity> <activity android:name="com.unity3d.player.UnityPlayerActivity" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" > </activity> <activity android:name="com.unity3d.player.UnityPlayerNativeActivity" android:label="@string/app_name" android:configChanges="fontScale|keyboard|keyboardHidden|locale|mnc|mcc|navigation|orientation|screenLayout|screenSize|smallestScreenSize|uiMode|touchscreen" > <meta-data android:name="android.app.lib_name" android:value="unity" /> <meta-data android:name="unityplayer.ForwardNativeEventsToDalvik" android:value="true" /> </activity> <!-- ] -->
3. ios 直接调用 unity3d 的 api,实现如下:
#if UNITY_IPHONE using System; using UnityEngine; public class IOSNotification : INotification { static IOSNotification() { Clear(); NotificationServices.RegisterForLocalNotificationTypes(LocalNotificationType.Alert); } public void Register(int id, string name, string title, string content, DateTime triggerTime, bool repeat) { var iosNotification = new LocalNotification() { alertBody = content, hasAction = false, applicationIconBadgeNumber = 1, fireDate = triggerTime, soundName = LocalNotification.defaultSoundName, }; if (repeat) { iosNotification.repeatCalendar = CalendarIdentifier.ChineseCalendar; iosNotification.repeatInterval = CalendarUnit.Day; } NotificationServices.ScheduleLocalNotification(iosNotification); } public void Unregister(int id) { } public void ClearAll() { Clear(); } static void Clear() { var ln = new LocalNotification() { applicationIconBadgeNumber = -1 }; NotificationServices.PresentLocalNotificationNow(ln); NotificationServices.CancelAllLocalNotifications(); NotificationServices.ClearLocalNotifications(); } #region INotification public void Dispose() { } #endregion } #endif
4. 最后是一个与上层交互的接口:
using System; using UnityEngine; // 本地推送的类型,注意:id不能重复 public enum NotificationType { SevenDaysNoLogin = 1, // 7日登陆 SpecialTrain = 100, // 紧急特训 SpaTime = 200, // 温泉 LoginNextDay = 300, // 次日登陆 PuYuanShopRefresh = 400, // 浦原商店刷新 WorldBossOpen = 500, // 世界boss开启 } public class NotificationManager : IDisposable { INotification m_notification; #region Singleton static NotificationManager s_instance; public static NotificationManager Instance { get { if (s_instance == null) s_instance = new NotificationManager(); return s_instance; } } #endregion #region DefaultNotification class DefaultNotification : INotification { public void Register(int id, string name, string title, string content, DateTime triggerTime, bool repeat) { } public void Unregister(int id) { } public void ClearAll() { } public void Dispose() { } } #endregion private NotificationManager() { m_notification = CreateObj(); } INotification CreateObj() { INotification obj; #if UNITY_EDITOR obj = new DefaultNotification(); #elif UNITY_ANDROID obj = new AndroidNotification(); #elif UNITY_IPHONE obj = new IOSNotification(); #else obj = new DefaultNotification(); Debugger.LogWarning("Local push not support this platform"); #endif return obj; } public void Register(int id, string content, DateTime triggerTime, bool repeat) { if (string.IsNullOrEmpty(content)) throw new ArgumentException("content"); string title = MLocalization.Get("194260041"); // 游戏名 if (string.IsNullOrEmpty(title)) throw new ArgumentException("title"); m_notification.Register(id, content, title, content, triggerTime, repeat); Debug.Log(string.Format("local push, id: {0}, title: {1}, content: {2}, Time: {3} repeat: {4}", id, title, content, triggerTime, repeat)); } public void Register(NotificationType type, string content, DateTime triggerTime, bool repeat) { Register((int)type, content, triggerTime, repeat); } public void Unregister(int id) { m_notification.Unregister(id); } public void Unregister(NotificationType type) { Unregister((int)type); } public void ClearAll() { m_notification.ClearAll(); } /// <summary> /// 登陆完成后更新本地推送 /// </summary> public void UpdateWhenLoginComplete() { UpdateSevenDaysNoLogin(); //UpdateLoginNextDay(); UpdatePuYuanShopRefresh(); UpdateWorldBossOpen(); } #region Special notification // 七天未登陆 void UpdateSevenDaysNoLogin() { string content = MLocalization.Get("194260031"); var time = PlayerInfo.Instance().GetServerNow().AddDays(7); Unregister(NotificationType.SevenDaysNoLogin); Register(NotificationType.SevenDaysNoLogin, content, time, false); } // 紧急特训 public void RegisterSpecialTrain(TrainingSpecially tr) { string content = MLocalization.Get("194260021"); Register(NotificationType.SpecialTrain, content, tr.begin_time, true); } // 温泉 public void UpdateSpaTime() { string content = null; // spaTime just notify once //for (int i = 0; i < ActivityInfoValue.Instance().spaStartTimeList.Count; i++) //{ int notiId = (int)NotificationType.SpaTime + 0; Unregister(notiId); if (SystemSettingModel.Instance.NotifyHotSpring) // 系统设置 { ulong spaStartTime = ActivityInfoValue.Instance().spaStartTimeList[0]; DateTime dateTimeStart = TimeZone.CurrentTimeZone.ToLocalTime(new DateTime(1970, 1, 1)); DateTime dateTimeTrigger = dateTimeStart.AddSeconds(spaStartTime).ToLocalTime(); if (content == null) content = MLocalization.Get("194260011"); Register(notiId, content, dateTimeTrigger, true); } //} } public void ClearSpaTime() { if (ActivityInfoValue.Instance().spaStartTimeList == null) return; for (int i = 0; i < ActivityInfoValue.Instance().spaStartTimeList.Count; i++) { int notiId = (int)NotificationType.SpaTime + i; Unregister(notiId); } } /* * 当玩家首次打开客户端进入游戏时,设置一条推送,在第二天的20点进行推送 * 当玩家第二天20点之前,登录过游戏,则取消此条推送。 * 推送文字为“你今天还没有登录死神哦,登录即可入手斩月。“(本文字为语言包文字) */ void UpdateLoginNextDay() { string prefKey = string.Format("LocalPush.{0}.{1}", PlayerInfo.Instance().roleID, NotificationType.LoginNextDay); int prefValue = PlayerPrefs.GetInt(prefKey); Unregister(NotificationType.LoginNextDay); if (prefValue <= 0) { DateTime serverNow = PlayerInfo.Instance().GetServerNow(); string timeString = GameOption.Instance().NotifyBladeTime; DateTime time = DateTime.Parse(timeString); DateTime triggerTime = new DateTime(serverNow.Year, serverNow.Month, serverNow.Day, time.Hour, time.Minute, 0).AddDays(1); string content = MLocalization.Get("194260051"); Register(NotificationType.LoginNextDay, content, triggerTime, false); PlayerPrefs.SetInt(prefKey, 1); } } // 浦原商店刷新 public void UpdatePuYuanShopRefresh() { Unregister(NotificationType.PuYuanShopRefresh); if (SystemSettingModel.Instance.NotifyShopRefresh) { DateTime time = DateTime.Parse(GameOption.Instance().refreshShopResetTime[0]); // 只需要每天9点推送 DateTime serverNow = PlayerInfo.Instance().GetServerNow(); DateTime triggerTime = new DateTime(serverNow.Year, serverNow.Month, serverNow.Day, time.Hour, time.Minute, 0); string content = MLocalization.Get("194260071"); Register(NotificationType.PuYuanShopRefresh, content, triggerTime, true); } } // 世界 boss 开启 public void UpdateWorldBossOpen() { Unregister(NotificationType.WorldBossOpen); bool isOpen = PlayerInfo.Instance().openedSystemID.Contains((uint)SystemEnum.WorldBoss); if (isOpen && SystemSettingModel.Instance.NotifyWorldBossOpen) { string content = MLocalization.Get("194260061"); DateTime serverNow = PlayerInfo.Instance().GetServerNow(); string timeString = GameOption.Instance().bossYammyNotifyTime; DateTime time = DateTime.Parse(timeString); DateTime triggerTime = new DateTime(serverNow.Year, serverNow.Month, serverNow.Day, time.Hour, time.Minute, 0); Register(NotificationType.WorldBossOpen, content, triggerTime, true); } } #endregion public void Dispose() { m_notification.Dispose(); } }