アラームの作り方


これが正しい方法かどうかわかりませんが、アラームとして機能したのでメモ

Androidでアラームを鳴らすためには、

1. アラーム時に起動するサービスをPendingIntentを使って指定する
1. 指定されたタイミングで起動したサービスからsendBroadcastを利用してレシーバーを起こす
1. 起こされたレシーバーからアラーム時に起動するActivityをIntentによって指定、起動

というプロセスでいけるようです。
めんどくさいですが。

サービスとレシーバーを利用するためには権限が必要なため、Manifestに記載する
必要な部分のみ書いています。

```xml:AndroidManifest.xml

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

<application>
	<!-- MainActivityなど -->
	<receiver android:name="AlarmReceiver" >
		<intent-filter>
			<action android:name="MyAlarmAction"></action>
		</intent-filter>
	</receiver>

	<service android:name="MyAlarmService" ></service>

	<activity android:name="AlarmNotificationActivity" ></activity>
</application>
```

actionにMyAlarmActionというものをセットしていますが、
これはサービスからレシーバーを起動する際に利用する識別子のようなものです。多分。

アラームの設定自体を行うMyAlarmManager.javaは以下の通り

```java:MyAlarmManager.java
public class MyAlarmManager {
	Context c;
	AlarmManager am;
	private PendingIntent mAlarmSender;

      private static final String TAG = MyAlarmManager.class.getSimpleName();

	public MyAlarmManager(Context c){
		// 初期化
		this.c = c;
		am = (AlarmManager)c.getSystemService(Context.ALARM_SERVICE);
		Log.v(TAG,"初期化完了");
	}

	public void addAlarm(int alarmHour, int alarmMinute){
		// アラームを設定する
		mAlarmSender = this.getPendingIntent();

		// アラーム時間設定
		Calendar cal = Calendar.getInstance();
		cal.setTimeInMillis(System.currentTimeMillis());
		// 設定した時刻をカレンダーに設定
		cal.set(Calendar.HOUR_OF_DAY, alarmHour);
		cal.set(Calendar.MINUTE, alarmMinute);
		cal.set(Calendar.SECOND, 0);
		cal.set(Calendar.MILLISECOND, 0);

		// 過去だったら明日にする
		if(cal.getTimeInMillis() < System.currentTimeMillis()){
			cal.add(Calendar.DAY_OF_YEAR, 1);
		}
		Toast.makeText(c, String.format("%02d時%02d分に起こします", alarmHour, alarmMinute), Toast.LENGTH_LONG).show();	

		am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), mAlarmSender);
		Log.v(TAG, cal.getTimeInMillis()+"ms");
		Log.v(TAG, "アラームセット完了");
	}

	public void stopAlarm() {
		// アラームのキャンセル
		Log.d(TAG, "stopAlarm()");        
		am.cancel(mAlarmSender);
		spm.updateToRevival();
	}

	private PendingIntent getPendingIntent() {
		// アラーム時に起動するアプリケーションを登録 
		Intent intent = new Intent(c, MyAlarmService.class);
		PendingIntent pendingIntent = PendingIntent.getService(c, PendingIntent.FLAG_ONE_SHOT, intent, PendingIntent.FLAG_UPDATE_CURRENT);
		return pendingIntent;
	}
}

```

PendingIntentのFLAGについては[http://y-anz-m.blogspot.jp/2011/07/androidappwidget-pendingintent-putextra.html](http://y-anz-m.blogspot.jp/2011/07/androidappwidget-pendingintent-putextra.html)が詳しいです。
アラームを更新するのか、あるいは前のままにするのか等、用途に合わせて設定して下さい。

`getPendingIntent()`で定義しているように、`PendingIntent.getService`で呼び出すサービスを指定します。
MyAlarmManagerによって、アラームの時刻を設定したいActivityなどから`addAlarm(hour, minute)`で簡単にアラームをセットできます。

次にサービスを見ます。

```java:MyAlarmService.java
public class MyAlarmService extends Service {
	private static final String TAG = MyAlarmService.class.getSimpleName();

	@Override
	public IBinder onBind(Intent intent) {
		return null;
	}

	@Override
	public void onCreate() {
		Thread thr = new Thread(null, mTask, "MyAlarmServiceThread");
		thr.start();
		Log.v(TAG,"スレッド開始");
	}
	// アラーム用サービス
	Runnable mTask = new Runnable() {
		public void run() {			
			// アラームを受け取るActivityを指定
			Intent alarmBroadcast = new Intent();
			// ここでActionをセットする(Manifestに書いたものと同じであれば何でもよい)
			alarmBroadcast.setAction("MyAlarmAction");
			// レシーバーへ渡す
			sendBroadcast(alarmBroadcast);
			// 役目を終えたサービスを止める
			MyAlarmService.this.stopSelf();
			Log.v(TAG,"サービス停止");
		}
	};
}
```

サービスからのBroadCastなIntentを受け取るレシーバー

```java:AlarmReceiver.java
public class AlarmReceiver extends BroadcastReceiver {  
	@Override
	public void onReceive(Context context, Intent intent) {
		// アラームを受け取って起動するActivityを指定、起動
		Intent notification = new Intent(context, AlarmNortificationActivity.class);
		// 画面起動に必要
		notification.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
		context.startActivity(notification);
	}
}

```
だんだんコードが減ってきて、言うことが無くなってきてます。

AlarmReceiverによって起こされるActivity

```java:AlarmNotification.java
public class AlarmNortificationActivity extends Activity {
	private MediaPlayer mp;

	@Override
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.alarm_notification);

		// スクリーンロックを解除する
		// 権限が必要
		getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD |
				WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED |
				WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON |
				WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
		
		Toast.makeText(this, "アラーム！", Toast.LENGTH_SHORT).show();

	}

	@Override
	public void onStart() {
		super.onStart();
		Toast.makeText(getApplicationContext(), "アラームスタート！", Toast.LENGTH_LONG).show();
		// 音を鳴らす
		if (mp == null)
			// resのrawディレクトリにtest.mp3を置いてある
			mp = MediaPlayer.create(this, R.raw.test);
		mp.start();
	}

	@Override
	public void onDestroy() {
		super.onDestroy();
		stopAndRelaese();
	}

	private void stopAndRelaese() {
		if (mp != null) {
			mp.stop();
			mp.release();
		}
	}

	@Override
	protected void onResume() {
		super.onResume();
		alarmNowText = (TextView) findViewById(R.id.alarm_now_time);	  
		handler.sendEmptyMessage(WHAT);
		// mam.stopAlarm();
	}
}
```

これで```alarm_notification.xml```が表示され、Toastが出るはずです。

意外とめんどくさいアラームのメモでした。

__アラームを設定する際のベストプラクティスがよくわかっていないので、アドバイス頂けると嬉しいです。__