我的環境配置:Windows 10、Unity 5.0.4f1、Java JDK 1.8.0_51、Android Studio 2.1.2。

2016/09/13 補充:加入取消本地端推播的方法,修正了點擊推播訊息前往 Unity 時會錯誤的問題,並且都修改到範例裡面。

 

 

如果你已經完成了之前的文章 "Android Studio 匯出 JAR 檔"、"Android Studio 匯出 JAR 檔 - 加入 Unity classes.jar"、"Android Studio 匯出 JAR 檔 - 在 Unity 中調用 Android Function" 的話。

那現在我們可以來嘗試加入 Android 的 "本地端推播 (LocalNotification)" 囉!

所謂的本地端推播,就是利用系統內建的 "鬧鐘系統 (AlarmManager)",設定在某個時間點時,發送我們設定好的 "廣播 (Broadcast)"
而當這筆 "廣播 (Broadcast)" 被發送時,我們會在建立一個 "廣播接收器 (BroadcastReceiver)" 去接收該 "廣播 (Broadcast)"
並且解析出內容後,使用 "推播系統 (NotificationManager)" 將資訊加入使用者的 "狀態列 (Status Bar)" 裡面,如此一來,我就們完成了 "本地端推播 (LocalNotification)" 功能了!


流程如下:

步驟一、
開啟 Android Studio。
相關的專案設置這邊不會再講,若不曉得的人,可以將 "Android Studio 匯出 JAR 檔" 系列的文章,快速瀏覽過一次。
或者直接下載 "Android Studio 匯出 JAR 檔 - 在 Unity 中調用 Android Function" 文章中的範例,也是可以的。


步驟二、
建立一個 Java Class。
名稱自訂,我這邊是命名為 "LocalNotificationActivity"

image01.jpg 

image02.jpg 

 


步驟三、
該 Class 是用來跟 Unity 溝通的,所以必須繼承 UnityPlayerActivity

 
public class LocalNotificationActivity extends UnityPlayerActivity {
}
 

 

步驟四、

再建立一個 Java Class。 名稱自訂,我這邊是命名為 "LocalNotificationReceiver"

image03.jpg 



步驟五、
該 Class 是用來接收廣播的,所以必須繼承 BroadcastReceiver

 
public class LocalNotificationReceiver extends BroadcastReceiver {
  public LocalNotificationReceiver() {
  }

  @Override
  public void onReceive( Context context, Intent intent ) {
  }
}
 



步驟六、
回到 LocalNotificationActivity.java 裡,這邊我們添加兩個靜態 function,分別 Add Cancel
Add:

該函數主要是把推播需要的資源封裝起來,再建立一個鬧鐘,在鬧鐘響起時,會將封裝好的資源廣播出去,它接收的參數為 "幾秒後推播訊息"、"訊息標題"、"訊息內文"、"訊息圖示"、"訊息編號"。
PS:訊息圖示非常重要,若沒有圖示的話,推播訊息則不會顯示。


Cancel:
該函數主要是用來取消鬧鐘用的,它接收的參數只有一個 "訊息編號",這邊會去通知系統將相同編號的鬧鐘做取消的動作。
而取消的重點在於 requestCode 與 Intent 必須跟當初建立鬧鐘時一樣,而 PendingIntent 就算不同也沒關係

以下有提到 PendingIntent 以及 AlarmManager 參數,這邊做個簡單的說明。

 
// PendingIntent.FLAG_CANCEL_CURRENT = 如果已經存在 PendingIntent, 則會取消目前的 Intent 後再產生新的 Intent.
// PendingIntent.FLAG_NO_CREATE = 如果並不存在 PendingIntent, 則傳回 null.
// PendingIntent.FLAG_ONE_SHOT = 此 PendingIntent 只能使用一次.
// PendingIntent.FLAG_UPDATE_CURRENT = 如果已存在 PendingIntent, 則更新 extra data.

// AlarmManager.RTC_WAKEUP = 鬧鐘在睡眠模式底下會喚醒系統並執行功能.
// AlarmManager.RTC = 鬧鐘在睡眠模式時不會運作. 
 

 

 
public static void Add( final int second, final String title, final String content, final String iconSmall, final int requestCode ) {

  UnityPlayer.currentActivity.runOnUiThread( new Runnable() {
    public void run() {

      // 取得定時呼叫管理器
      AlarmManager alarmManager = (AlarmManager) UnityPlayer.currentActivity.getSystemService(Context.ALARM_SERVICE);

      // 封裝訊息
      Intent notificationIntent = new Intent(UnityPlayer.currentActivity, LocalNotificationReceiver.class);
      notificationIntent.putExtra("title", title);
      notificationIntent.putExtra("content", content);
      notificationIntent.putExtra("iconSmall", iconSmall);       
          notificationIntent.putExtra("requestCode", requestCode);

      // 整理定時需要的資訊
      PendingIntent broadcast = PendingIntent.getBroadcast(UnityPlayer.currentActivity, requestCode, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT);

      // 設定時間
      Calendar calendar = Calendar.getInstance();
      calendar.add(Calendar.SECOND, second );

      // 加入定時
         alarmManager.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), broadcast);
    }
  } );
}

public static void Cancel( int requestCode ) {

          // 取得定時呼叫管理器       
          AlarmManager alarmManager = (AlarmManager) UnityPlayer.currentActivity.getSystemService(Context.ALARM_SERVICE);

          // Intent 的 Receiver Class 必須跟當初建立鬧鐘時的 Receiver 一樣
          Intent notificationIntent = new Intent(UnityPlayer.currentActivity, LocalNotificationReceiver.class);

          // 取消定時, 這邊的重點在於 requestCode 與 Intent 必須跟當初建立鬧鐘時一樣,而 PendingIntent 就算不同也沒關係
          alarmManager.cancel( PendingIntent.getBroadcast(UnityPlayer.currentActivity, requestCode, notificationIntent, PendingIntent.FLAG_CANCEL_CURRENT) );
}
 

 



步驟七、
回到 LocalNotificationReceiver.java 裡面,這邊針對 onReceive 做了修改。
這邊主要是把鬧鐘廣播出去資源接收回來,之後解析出封裝好的資源後,再去建立推播。


這要注意到,因為我們需要返回 Unity 裡面,所以 Intent 必須是 new Intent(context, UnityPlayerActivity.class);

可能有人發現取得 Resources 的方式,並非為 R.drawable.xxx。
這是因為,Android 專案打包 JAR 檔時,他只會打包你專案的 Class 而已,並不會打包專案內的 Resources,甚至引用的 Libraries 也不會打包在內。
所以這些資源必須額外複製一份到 Unity > Assets > Plugins > Android 裡面。

 
@Override
public void onReceive( Context context, Intent intent ) {

  String title = intent.getStringExtra("title");
  String content = intent.getStringExtra("content");
  String iconSmall = intent.getStringExtra("iconSmall");   
  int requestCode = intent.getIntExtra("requestCode", 0);
  
  int smallIcon = context.getResources().getIdentifier(iconSmall, "drawable", context.getPackageName());

  // 這邊表示點擊推播訊息後, 要返回 Unity, 所以必須是 UnityPlayerActivity.class
  Intent newIntent = new Intent(context, UnityPlayerActivity.class);
  newIntent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

  PendingIntent pendingIntent = PendingIntent.getActivity(context, requestCode, newIntent, PendingIntent.FLAG_CANCEL_CURRENT); // 取得PendingIntent


  // 建立推播內容
  Notification.Builder builder = new Notification.Builder(context.getApplicationContext());

  // setSmallIcon 尺寸建議 32 * 32
  builder.setSmallIcon(smallIcon);
  builder.setContentTitle(title);
  builder.setContentText(content);
  builder.setContentIntent(pendingIntent);
  builder.setAutoCancel( true );
  Notification notification = builder.build();


  // 取得推播管理器, 執行推播
  NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
  notificationManager.notify(requestCode, notification);
}
 



步驟八、
在這裡,我們使用了 Notification.Builder,該 Class 是比較新的,需要 Android API 版本 16 以上才能夠使用,所以記得去 Unity 專案,在 Edit > ProjectSettings > Player > Android > Other Settings > Minimum API Level,這邊要改成 16 以上

image04.jpg 

image05.jpg 

 

Unity > Assets > Plugins > Android > AndroidManifest.xml 將 <uses-sdk android:minSdkVersion="9" /> 修改至 16 以上。
若你是用 Unity 自己的 AndroidManifest.xml,那可能會沒有 uses-sdk android:minSdkVersion,若沒有的話,該步驟可跳過。

 

Android Studio >Test > app > src > build.gradle 裡面 defaultConfig minSdkVersion 也記得要改成 16

 image06.jpg 


步驟九、
將會用到的 Resources 放到 Unity > Assets > Plugins > Android 裡面。
我簡單的製作一個小圖示來測試。
在 Unity 專案裡面建立資料夾 Unity > Assets > Plugins > Android > res > drawable。
接著把製作好的小圖示放進去,我命名為 icon.png。

icon.png 

image07.jpg 

 

這邊值得注意的是,新版 Android 似乎將狀態列的 Icon 改成只能顯示不透明與透明了,所有非透明的顏色都會以白色來顯示。
所以 Icon 設計上,盡量是以白色以及透明去製作,會比較精準一點,並且存成 PNG 檔。


步驟十、
將 Android 打包匯出 JAR,並且放入 Unity > Assets > Plugins > Android
image07.jpg 



步驟十一、
打開 Unity > Assets > Plugins > Android > AndroidManifest.xml。

在 <manifest> 裡面 <application> 的外面加入權限設定,分別為 "震動"、"手機喚醒" 功能

 
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
 

在 <application> 裡加入我們的 Activity 以及 Receiver

 
<activity android:name="com.test.tw.test.LocalNotificationActivity" />
<receiver android:name="com.test.tw.test.LocalNotificationReceiver" android:enabled="true" android:exported="true" />
 

 

步驟十二、
打開我們的測試腳本,將 OnGUI 修改如下

image08.jpg 

 
private void OnGUI()
{
  Rect rect = new Rect( 0.0f, 0.0f, 200.0f, 100.0f );
  if ( GUI.Button(rect, "本地推播") )
  {
    #if UNITY_ANDROID && !UNITY_EDITOR

    using ( AndroidJavaClass unity = new AndroidJavaClass("com.test.tw.test.LocalNotificationActivity") )
    {
      unity.CallStatic( "Add", 5, "這是標題", "這是內文", "icon", 100 );
    }

    #endif
  }

    rect = new Rect( 0.0f, 100.0f, 200.0f, 100.0f );   
    if ( GUI.Button(rect, "取消推播") )   
    {     
        #if UNITY_ANDROID && !UNITY_EDITOR     
        using ( AndroidJavaClass unity = new AndroidJavaClass("com.test.tw.test.LocalNotificationActivity") )     
        {       
            unity.CallStatic( "Cancel", 100 );     
        }     
        #endif   
     }
}
 

 

 

步驟十三、 
最後運行結果

image09.jpg 

image10.jpg 

 

 

PS:這邊有一點值得注意的是,鬧鐘似乎最低秒數為 5 秒,若低於 5 秒的話,系統似乎會以 5 秒代替。

 

範例下載:https://drive.google.com/file/d/0B0QPre3qvuw-M1JsdDU3SUFDeEk/view?usp=sharing&resourcekey=0-WqOAlZoJpmplIJpU-yyqBQ

 

該教學的一系列文章:

 

arrow
arrow

    岳 發表在 痞客邦 留言(10) 人氣()