プロソフト ロゴ

有限会社プロソフト

家庭用ゲーム(コンシューマ)、ソーシャルゲーム(ネイティブアプリ/サーバー)の開発会社


ローカル通知を行う

文責:black

ローカル通知を行う

■ローカル通知を行う ローカル通知を行う機能に関する備忘録。

■ローカル通知とは?
Android、iOSの通知欄に届く、テキストなどの集合をプッシュ通知と呼びますが、外部のサーバから通知されるものをリモート通知、アプリケーションが自身自身に通知するものをローカル通知と呼びます。
ゲーム関連だと、行動力が時間経過で回復するゲームで、行動力が最大値まで回復したかどうかを通知する際に、外部のサーバ側の計算で行動力が最大値まで回復したタイミングで通知するのはリモート通知。
行動力が最大値になる時間を計算して、アプリ側から「この時刻に通知を行う」という予約をいれておいて通知するのはローカル通知となります。

■ローカル通知のメリットは?
リモート通知は、通信状況が悪いと予定時刻に届かないケースなどもありますが、ローカル通知はアプリケーションが自身自身に通知するため、外部との通信状況に依存せずに通知を受け取る事ができます。
また、リモート通知と比べて実装が容易です。

※リモート通知でしか実現できない通知もあるため、どちらの機能も実装するケースの方が多いとは思います。

■実装(Android)

---- Activity.java ----
import java.util.Calendar;
import android.app.AlarmManager;
import android.app.PendingIntent;

/**
 * 通知を予約する
 * @param[in] id     通知ID (同一の通知IDは上書きされる)
 * @param[in] year   通知タイミング (年:西暦)
 * @param[in] month  通知タイミング (月)
 * @param[in] day    通知タイミング (日)
 * @param[in] hour   通知タイミング (時)
 * @param[in] minute 通知タイミング (分)
 * @param[in] second 通知タイミング (秒)
 * @param[in] title  展開メッセージのタイトル
 * @param[in] text   展開メッセージの詳細メッセージ
 */
public void reserveNotification(int id, int year, int month, int day, int hour, int minute, int second, String title, String text) {
	Calendar calendar = Calendar.getInstance();
	long now = calendar.getTimeInMillis();
	calendar.set(year, month - 1, day, hour, minute, second);
	long utc = calendar.getTimeInMillis();
	
	// 現在より前の通知は予約しない
	if (utc <= now) {
		return;
	}
	
	// 通知を予約
	Intent bootIntent = new Intent(this, AlarmBroadcastReceiver.class);
	bootIntent.putExtra("notificationId", id);
	bootIntent.putExtra("notifiTitle", title);
	bootIntent.putExtra("notifiText", text);
	PendingIntent alarmIntent = PendingIntent.getBroadcast(this, id, bootIntent, PendingIntent.FLAG_UPDATE_CURRENT);
	
	AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
	alarm.set(AlarmManager.RTC_WAKEUP, utc, alarmIntent);
}

/**
 * 通知をキャンセルする
 * @param[in] id     キャンセル対象の通知ID
 */
public void cancelNotification(int id) {
	Intent bootIntent = new Intent(this, AlarmBroadcastReceiver.class);
	PendingIntent alarmIntent = PendingIntent.getBroadcast(this, id, bootIntent, PendingIntent.FLAG_UPDATE_CURRENT);
	AlarmManager alarm = (AlarmManager)getSystemService(Context.ALARM_SERVICE);
	alarm.cancel(alarmIntent);
}

---- AlarmBroadcastReceiver.java ----
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.support.v4.app.NotificationCompat;

/**
 * アラームを利用した通知
 */
public class AlarmBroadcastReceiver extends BroadcastReceiver {
 	
	private Context alarmReceiverContext;
	
	@Override
	public void onReceive(Context context, Intent receivedIntent) {
		alarmReceiverContext = context;
		int notificationId = receivedIntent.getIntExtra("notificationId", 0);
		String title = receivedIntent.getStringExtra("notifiTitle");
		String text = receivedIntent.getStringExtra("notifiText");
		
		// 本文が空の通知は予約しない
		if(text == null) {
			return;
		}
		
		NotificationManager myNotification = (NotificationManager) context.getSystemService(Context.NOTIFICATION_SERVICE);
		
		Intent bootIntent = new Intent(alarmReceiverContext, MainActivity.class);
		PendingIntent contentIntent = PendingIntent.getActivity(alarmReceiverContext, notificatId, bootIntent, 0);
		Notification notification = new NotificationCompat.Builder(context)
			.setSmallIcon(R.drawable.icon)				// アイコン
			.setWhen(System.currentTimeMillis())		// 表示時間
			.setContentTitle(title)						// 展開メッセージのタイトル
			.setContentText(text)						// 展開メッセージの詳細メッセージ
			.setDefaults(Notification.DEFAULT_SOUND)	// デフォルトサウンドを再生
			.setAutoCancel(true)						// タップすると消えるかどうか
			.setContentIntent(contentIntent)
			.build();
		
		myNotification.notify(notificationId, notification);
	}
}

---- AndroidManifest.xml ----




■実装(iOS 8)※iOS 9 以降は別の実装にする必要がある
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
	if ([application respondsToSelector:@selector(registerUserNotificationSettings:)]) {
		// registerUserNotificationSettings を使用する
		UIUserNotificationType types = UIUserNotificationTypeSound | UIUserNotificationTypeAlert;
		UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:types categories:nil];
		[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
	} else {
		// registerForRemoteNotificationTypes を使用する
		[application registerForRemoteNotificationTypes:UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert];
	}
	return YES;
}

/**
 * 通知を予約する
 * @param[in] id       通知ID (同一の通知IDは上書きされる)
 * @param[in] year     通知タイミング (年:西暦)
 * @param[in] month    通知タイミング (月)
 * @param[in] day      通知タイミング (日)
 * @param[in] hour     通知タイミング (時)
 * @param[in] minute   通知タイミング (分)
 * @param[in] second   通知タイミング (秒)
 * @param[in] p_text   展開メッセージの詳細メッセージ
 */
void reserveNotification(int id, int year, int month, int day, int hour, int minute, int second, const char* p_text) {
	@autoreleasepool
	{
		NSString *text_str = nil;
		NSDate *fireDate = nil;
		
		// 本文が空の通知は予約しない
		if( p_text == NULL ) {
			return;
		}
		text_str = [[NSString alloc] initWithCString:p_text encoding:NSUTF8StringEncoding];
		
		// 日付変換
		NSCalendar *calendar = [NSCalendar currentCalendar];
		NSDateComponents *comps = [[NSDateComponents alloc] init];
		[comps setYear:year];
		[comps setMonth:month];
		[comps setDay:day];
		[comps setHour:hour];
		[comps setMinute:minute];
		[comps setSecond:second];
		fireDate = [calendar dateFromComponents:comps];
		
	    // 現在より前の通知は予約しない
	    if (fireDate == nil || [fireDate timeIntervalSinceNow] <= 0) {
	        return;
	    }
	    
		UILocalNotification *localNotification = [[UILocalNotification alloc] init];
		if (localNotification == nil) {
			return;
		}
        localNotification.fireDate = fireDate;
	    localNotification.alertBody = text_str;
        localNotification.timeZone = [NSTimeZone localTimeZone];
	    localNotification.soundName = UILocalNotificationDefaultSoundName;
		localNotification.applicationIconBadgeNumber = 1;
        [[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
	}
}


/**
 * 通知をキャンセルする
 */
void cancelNotification() {
	@autoreleasepool
	{
		[[UIApplication sharedApplication] cancelAllLocalNotifications];
	}
}