cocos2dxでローカル通知を出す（iOS/Android）

※ (6/11) コメント頂いたgetPendingIntentを修正

# cocos2d-xでローカル通知を出したい
cocos2d-xでiOS/Androidのローカル通知（LocalNotification）を出すコードです。

# iOSの場合
iOSの場合は、実装ファイルの拡張子を*.mmに変えるだけでObjective-Cを呼び出せるので簡単。

```objc++:LocalNotification.h
#ifndef ___LocalNotificationSample__LocalNotification__
#define ___LocalNotificationSample__LocalNotification__

#include <string>

class LocalNotification {
public:
  /**
   * Show local notification
   * @param message  Message should be shown in notificatin
   * @param interval Interval in seconds
   * @param tag      Tag to specify notification
   */
  static void show(std::string message, int interval, int tag);
  
  /**
   * Cancel local notification specified by ID
   * @param tag Tag of local notification
   */
  static void cancel(int tag);
};

#endif /* defined(___LocalNotificationSample__LocalNotification__) */
```

```objc++:LocalNotification_iOS.mm
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)

#include "LocalNotification.h"

using namespace std;

void LocalNotification::show(std::string message, int interval, int tag)
{
  // 通知を作成する
  UILocalNotification *notification = [[UILocalNotification alloc] init];
  
  notification.fireDate = [[NSDate date] dateByAddingTimeInterval:interval];
  notification.timeZone = [NSTimeZone defaultTimeZone];
  notification.alertBody = [NSString stringWithCString:message.c_str()
                                              encoding:[NSString defaultCStringEncoding]];
  notification.alertAction = @"Open";
  notification.soundName = UILocalNotificationDefaultSoundName;
  
  NSNumber* tag1 = [NSNumber numberWithInteger:tag];
  NSDictionary *infoDict = [NSDictionary dictionaryWithObject:tag1 forKey:@"ID"];
  notification.userInfo = infoDict;
  
  // 通知を登録する
  [[UIApplication sharedApplication] scheduleLocalNotification:notification];
  
  [notification release];
}

void LocalNotification::cancel(int tag)
{
  for(UILocalNotification *notification in [[UIApplication sharedApplication] scheduledLocalNotifications]) {
    if([[notification.userInfo objectForKey:@"ID"] integerValue] == tag) {
      [[UIApplication sharedApplication] cancelLocalNotification:notification];
    }
  }
}

#endif // CC_TARGET_PLATFORM == CC_PLATFORM_IOS
```

# Androidの場合
iOSとは違って、ちょっとめんどくさいです。
JavaでLocalNotificationを出すメソッドを定義して、JNI経由でC++から呼び出します。
Androidの通知UIのAPIはバージョンによって差分があるので、Support Library v4に入ってるNotificationCompatクラスを使用しましょう。

```java:Cocos2dxActivity.java
package org.cocos2dx.cpp;

import java.util.Calendar;

import android.app.Activity;
import android.app.AlarmManager;
import android.app.NativeActivity;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;

public class Cocos2dxActivity extends NativeActivity{
	
	private static final String TAG = Cocos2dxActivity.class.getSimpleName();
	private static Activity sActivity;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		// TODO Auto-generated method stub
		super.onCreate(savedInstanceState);
		
		//For supports translucency
		
		//1.change "attribs" in cocos\2d\platform\android\nativeactivity.cpp
		/*const EGLint attribs[] = {
	            EGL_SURFACE_TYPE, EGL_WINDOW_BIT,
	            EGL_RENDERABLE_TYPE, EGL_OPENGL_ES2_BIT,  
	            //EGL_BLUE_SIZE, 5,   -->delete 
	            //EGL_GREEN_SIZE, 6,  -->delete 
	            //EGL_RED_SIZE, 5,    -->delete 
	            EGL_BUFFER_SIZE, 32,  //-->new field
	            EGL_DEPTH_SIZE, 16,
	            EGL_STENCIL_SIZE, 8,
	            EGL_NONE
	    };*/
		
		//2.Set the format of window
		// getWindow().setFormat(PixelFormat.TRANSLUCENT);
		
		// LocalNotification
		sActivity = this;
	}
	
	public static void showLocalNotification(String message, int interval, int tag) {
		Log.v(TAG, "showLocalNotification");
		PendingIntent sender = getPendingIntent(message, tag);
		
		Calendar calendar = Calendar.getInstance();
		calendar.setTimeInMillis(System.currentTimeMillis());
		calendar.add(Calendar.SECOND, interval);
		
		AlarmManager am = (AlarmManager)sActivity.getSystemService(ALARM_SERVICE);
		am.set(AlarmManager.RTC_WAKEUP, calendar.getTimeInMillis(), sender);
	}
	
	public static void cancelLocalNotification(int tag) {
		Log.v(TAG, "cancelLocalNotification");
		PendingIntent sender = getPendingIntent(null, tag);
		AlarmManager am = (AlarmManager)sActivity.getSystemService(ALARM_SERVICE);
		am.cancel(sender);
	}
	
	private static PendingIntent getPendingIntent(String message, int tag) {
		Intent i = new Intent(sActivity.getApplicationContext(), LocalNotificationReceiver.class);
		i.putExtra("notification_id", tag);
		i.putExtra("message", message);
		PendingIntent sender = PendingIntent.getBroadcast(sActivity,  tag, i, PendingIntent.FLAG_UPDATE_CURRENT);
		return sender;
	}
}
```

```java:LocalNotificationReceiver.java
package org.cocos2dx.cpp;

import com.MyCompany.AwesomeGame.R;

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.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.support.v4.app.NotificationCompat;

public class LocalNotificationReceiver extends BroadcastReceiver {

	@Override
	public void onReceive(Context context, Intent intent) {
		int notificationId = intent.getIntExtra("notification_id", 0);
		String message = intent.getStringExtra("message");

		Intent intent2 = new Intent(context, Cocos2dxActivity.class);
		intent2.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
		PendingIntent pendingIntent = PendingIntent.getActivity(context, 0, intent2,
				PendingIntent.FLAG_UPDATE_CURRENT);

		Bitmap largeIcon = BitmapFactory.decodeResource(context.getResources(),
				R.drawable.icon);
		NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
		builder.setContentTitle(context.getString(R.string.app_name));
		builder.setContentText(message);
		builder.setSmallIcon(R.drawable.icon);
		builder.setLargeIcon(largeIcon);
		builder.setTicker(message);
		builder.setAutoCancel(true);
		builder.setDefaults(Notification.DEFAULT_ALL);
		builder.setContentIntent(pendingIntent);

		NotificationManager manager = (NotificationManager) context
				.getSystemService(Context.NOTIFICATION_SERVICE);
		manager.notify(notificationId, builder.build());
	}

}
```

AndroidManifest.xmlに以下の1行を追加します。

```xml:AndroidManifest.xml
<!-- ローカル通知のレシーバー -->
<receiver android:name="org.cocos2dx.cpp.LocalNotificationReceiver" android:process=":remote" />
```

C++側の実装は以下です。iOS版とAPIを合わせています。

```cpp:LocalNotification_Android.cpp
#if (CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID)

#include "LocalNotification.h"
#include "platform/android/jni/JniHelper.h"
#include <jni.h>

using namespace std;

// Java class
// NativeActivityのパッケージ名・クラス名に合わせて修正してください
#define  CLASS_NAME "org/cocos2dx/cpp/Cocos2dxActivity"

static bool getJNIStaticMethodInfo(cocos2d::JniMethodInfo &methodinfo,
                                        const char *methodName,
                                        const char *paramCode) {
  return cocos2d::JniHelper::getStaticMethodInfo(methodinfo,
                                                 CLASS_NAME,
                                                 methodName,
                                                 paramCode);
}

void LocalNotification::show(std::string message, int interval, int tag)
{
  cocos2d::JniMethodInfo methodInfo;
  
  if (! getJNIStaticMethodInfo(methodInfo, "showLocalNotification", "(Ljava/lang/String;II)V")) {
    return;
  }
  
  jstring stringArg = methodInfo.env->NewStringUTF(message.c_str());
  methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, stringArg, interval, tag);
  methodInfo.env->DeleteLocalRef(stringArg);
  methodInfo.env->DeleteLocalRef(methodInfo.classID);
}

void LocalNotification::cancel(int tag)
{
  cocos2d::JniMethodInfo methodInfo;
  
  if (! getJNIStaticMethodInfo(methodInfo, "cancelLocalNotification", "(I)V")) {
    return;
  }

  methodInfo.env->CallStaticVoidMethod(methodInfo.classID, methodInfo.methodID, tag);
  methodInfo.env->DeleteLocalRef(methodInfo.classID);
}

#endif // CC_TARGET_PLATFORM == CC_PLATFORM_ANDROID
```

# 使い方
こんな感じで呼び出します。

```objc++
// 10sec後に表示
LocalNotification::show("Hello Notification!", 10, 1);

// タグ1の通知をキャンセル
LocalNotification::cancel(1);
```