diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 3d01c29..1be335a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -26,7 +26,13 @@
+ android:exported="false" >
+
+
+
+
+
+
diff --git a/app/src/main/java/org/ntlab/amaryllis/client/entities/AmaryllisBackgroundService.java b/app/src/main/java/org/ntlab/amaryllis/client/entities/AmaryllisBackgroundService.java
index 7ef7aa6..0d1153c 100644
--- a/app/src/main/java/org/ntlab/amaryllis/client/entities/AmaryllisBackgroundService.java
+++ b/app/src/main/java/org/ntlab/amaryllis/client/entities/AmaryllisBackgroundService.java
@@ -1,18 +1,39 @@
package org.ntlab.amaryllis.client.entities;
import android.app.IntentService;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.content.ComponentName;
+import android.content.Context;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.os.Binder;
import android.os.Bundle;
+import android.os.IBinder;
+import android.support.v4.media.MediaMetadataCompat;
+import android.support.v4.media.session.MediaControllerCompat;
+import android.support.v4.media.session.MediaSessionCompat;
+import android.support.v4.media.session.PlaybackStateCompat;
+import android.util.Log;
+import android.view.KeyEvent;
+import org.ntlab.amaryllis.client.R;
import org.ntlab.amaryllis.client.resources.VoicememosRest;
import org.ntlab.amaryllis.client.voiceservice.PlayListManager;
+import org.ntlab.amaryllis.client.voiceservice.TestVoiceService;
import org.ntlab.amaryllis.client.voiceservice.VoiceMemo;
+import org.ntlab.amaryllis.client.voiceservice.VoicememoReceiver;
+import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
+import androidx.core.app.NotificationCompat;
+import androidx.media.session.MediaButtonReceiver;
import retrofit2.Call;
import retrofit2.Callback;
import retrofit2.Response;
@@ -21,20 +42,42 @@
public class AmaryllisBackgroundService extends IntentService implements LocationListener {
+ final String TAG = AmaryllisBackgroundService.class.getSimpleName();//ログ用タグ
+ public static final String ACTION_TOGGLE = "org.ntlab.amaryllis.client.ACTION_TOGGLE";
+ public static final String ACTION_PLAY = "org.ntlab.amaryllis.client.entities.ACTION_PLAY";
+ public static final String ACTION_STOP = "org.ntlab.amaryllis.client.entities.ACTION_STOP";
+ public static final String ACTION_PAUSE = "org.ntlab.amaryllis.client.entities.ACTION_PAUSE";
private ArrayList playListListeners;
private PlayListManager playListManager;
private double longitude,latitude;
private int i=0;
+ final IBinder myBinder=new MyBinder();
+
+ //音声再生に関わるメンバ変数--------
+ MediaPlayer mediaPlayer;
+ AudioManager audioManager;
+ MediaSessionCompat mediaSession;
+ ComponentName sampleReceiver;
+ enum State{PLAY,STOP,PAUSE};
+ AmaryllisBackgroundService.State mState;
+ VoiceMemo currentVoicememo;//再生中のヴォイスメモ
+ //----------------
+
+ public class MyBinder extends Binder {
+ public AmaryllisBackgroundService getService(){
+ return AmaryllisBackgroundService.this;
+ }
+ }
+
+
public AmaryllisBackgroundService() {
super("AmaryllisBackgroundService");
playListListeners=new ArrayList<>();
-
}
-// ArrayList Lat;
+
@Override
public void onCreate() {
super.onCreate();
-
Retrofit retrofit = new Retrofit.Builder()
.baseUrl("http://nitta-lab-www.is.konan-u.ac.jp/amaryllis/")
.addConverterFactory(JacksonConverterFactory.create())
@@ -65,6 +108,20 @@
}
});
+ {
+
+ mState = AmaryllisBackgroundService.State.STOP;
+ sampleReceiver=new ComponentName(this, VoicememoReceiver.class);
+ mediaPlayer=new MediaPlayer();
+ mediaSession=new MediaSessionCompat(getApplicationContext(),TAG);
+ //このMediaSessionが提供する機能を設定
+ mediaSession.setFlags(
+ MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS | //キュー系のコマンドの使用をサポート
+ MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS
+ ); //再生、停止、スキップ等のコントロールを提供
+
+ }
+
}
@Override
@@ -72,6 +129,7 @@
}
+
public void subscribe(PlayListListener playListListener){
playListListeners.add(playListListener);
}
@@ -85,14 +143,34 @@
private void callPlayEnd(){
for(PlayListListener pl:playListListeners)pl.onPlayEnd();
}
+ public MediaSessionCompat.Token getToken(){
+ return mediaSession.getSessionToken();
+ }
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ String action = intent.getAction();
+ if(action==null)return START_NOT_STICKY;
+ switch (action){
+ case ACTION_PLAY:
+ callback.onPlay();
+ break;
+ case ACTION_PAUSE:
+ callback.onPause();
+ break;
+ case ACTION_STOP:
+ break;
+ }
-// ArrayList<> 変数名 = new ArrayList();
+ // サービス開始 or 既存サービスの再利用
+ return START_NOT_STICKY;
+ }
@Override
public void onLocationChanged(Location location) {
longitude=location.getLongitude();
latitude=location.getLatitude();
+
}
@Override
@@ -108,5 +186,257 @@
public void onProviderDisabled(String s) {
}
+ public void clean() {
+ //mState = AmaryllisBackgroundService.State.STOP;
+ mediaPlayer.reset();
+ mediaPlayer.release();
+ mediaPlayer = null;
+ }
+
+ public IBinder onBind(Intent intent){
+ Log.d(TAG,"onBind");
+ return myBinder;
+ }
+
+ //MediaSession用コールバック
+ private MediaSessionCompat.Callback callback = new MediaSessionCompat.Callback() {
+
+ //曲のIDから再生する
+ //WearやAutoのブラウジング画面から曲が選択された場合もここが呼ばれる
+ @Override
+ public void onPlayFromMediaId(String mediaId, Bundle extras) {
+ //今回はAssetsフォルダに含まれる音声ファイルを再生
+ //Uriから再生する
+ //DataSource.Factory dataSourceFactory = new DefaultDataSourceFactory(getApplicationContext(), Util.getUserAgent(getApplicationContext(), "AppName"));
+ // MediaSource mediaSource = new ExtractorMediaSource.Factory(dataSourceFactory).createMediaSource(Uri.parse("file:///android_asset/" + MusicLibrary.getMusicFilename(mediaId)));
+
+ //今回は簡易的にmediaIdからインデックスを割り出す。
+
+ // onPlay();
+
+ //MediaSessionが配信する、再生中の曲の情報を設定
+ //mediaSession.setMetadata(MusicLibrary.getMetadata(getApplicationContext(), mediaId));
+ }
+
+ //再生をリクエストされたとき
+ @Override
+ public void onPlay() {
+ //オーディオフォーカスを要求
+// if (audioManager.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) {
+// //取得できたら再生を始める
+// mediaSession.setActive(true);
+// //mediaPlayer;
+// }
+ mediaSession.setActive(true);
+ switch (mState){
+ case PLAY:
+
+ break;
+ case STOP:
+ currentVoicememo=playListManager.getUnplayedList().get(0);
+ // final String url = "http://nitta-lab-www.is.konan-u.ac.jp/data/c948c729-a516-4ca2-88f3-35c0f2d20dba.3gp";
+ final String url="http://nitta-lab-www.is.konan-u.ac.jp/data/"+playListManager.getUnplayedList().get(0).getData();
+
+ mediaPlayer=new MediaPlayer();
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ try {
+ mediaPlayer.reset();
+ mediaPlayer.setDataSource(url);
+ mediaPlayer.prepare();
+ mediaPlayer.start();
+ mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mediaPlayer) {
+ mState=State.STOP;
+ onPlay();
+ }
+ });
+ CreateNotification();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+
+ break;
+ case PAUSE:
+ mediaPlayer.start();
+ mState=State.PLAY;
+ break;
+
+ }
+
+ }
+
+ //一時停止をリクエストされたとき
+ @Override
+ public void onPause() {
+// exoPlayer.setPlayWhenReady(false);
+// //オーディオフォーカスを開放
+// am.abandonAudioFocus(afChangeListener);
+ switch (mState){
+ case PLAY:
+ mediaPlayer.pause();
+ mState=State.PAUSE;
+ break;
+ case STOP:
+
+ break;
+ case PAUSE:
+
+ break;
+
+ }
+ }
+
+ //停止をリクエストされたとき
+ @Override
+ public void onStop() {
+// onPause();
+// mSession.setActive(false);
+// //オーディオフォーカスを開放
+// am.abandonAudioFocus(afChangeListener);
+ }
+
+ //シークをリクエストされたとき
+ @Override
+ public void onSeekTo(long pos) {
+// exoPlayer.seekTo(pos);
+ }
+
+ //次の曲をリクエストされたとき
+ @Override
+ public void onSkipToNext() {
+ final String url="http://nitta-lab-www.is.konan-u.ac.jp/data/"+playListManager.getUnplayedList().get(0).getData();
+
+ mediaPlayer=new MediaPlayer();
+ mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
+ try {
+ mediaPlayer.reset();
+ mediaPlayer.setDataSource(url);
+ mediaPlayer.prepare();
+ mediaPlayer.start();
+ mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ @Override
+ public void onCompletion(MediaPlayer mediaPlayer) {
+ mState=State.STOP;
+ onPlay();
+ }
+ });
+ CreateNotification();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ //前の曲をリクエストされたとき
+ @Override
+ public void onSkipToPrevious() {
+// index--;
+// if (index < 0)//インデックスが0以下になったら
+// index = queueItems.size() - 1;//最後の曲に移動する
+//
+// onPlayFromMediaId(queueItems.get(index).getDescription().getMediaId(), null);
+ }
+
+ //WearやAutoでキュー内のアイテムを選択された際にも呼び出される
+ @Override
+ public void onSkipToQueueItem(long i) {
+// onPlayFromMediaId(queueItems.get((int)i).getDescription().getMediaId(), null);
+ }
+
+ //Media Button Intentが飛んできた時に呼び出される
+ //オーバーライド不要(今回はログを吐くだけ)
+ //MediaSessionのplaybackStateのActionフラグに応じてできる操作が変わる
+ @Override
+ public boolean onMediaButtonEvent(Intent mediaButtonEvent) {
+ KeyEvent key = mediaButtonEvent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
+ Log.d(TAG, String.valueOf(key.getKeyCode()));
+ return super.onMediaButtonEvent(mediaButtonEvent);
+ }
+ };
+ private void CreateNotification() {
+
+ MediaControllerCompat controller = mediaSession.getController();
+ MediaMetadataCompat mediaMetadata = controller.getMetadata();
+
+ Notification notification;
+ //システムから通知マネージャー取得
+ NotificationManager notificationManager = (NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
+ //アプリ名をチャンネルIDとして利用
+ String chID = getString(R.string.app_name);
+
+ //アンドロイドのバージョンで振り分け
+ if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) { //APIが「26」以上の場合
+
+ //通知チャンネルIDを生成してインスタンス化
+ NotificationChannel notificationChannel = new NotificationChannel(chID, chID, NotificationManager.IMPORTANCE_DEFAULT);
+ //通知の説明のセット
+ notificationChannel.setDescription(chID);
+ //通知チャンネルの作成
+ notificationManager.createNotificationChannel(notificationChannel);
+ //通知の生成と設定とビルド
+ notification = new NotificationCompat.Builder(this, chID)
+ .setContentTitle(getString(R.string.app_name)) //通知タイトル
+ .setContentText("アプリ通知テスト26以上")//通知内容
+ .setSmallIcon(R.drawable.ic_baseline_play_circle_outline_24)//通知用アイコン
+ .setContentIntent(controller.getSessionActivity())
+ .setSound(null)
+ .addAction(new NotificationCompat.Action(
+ R.drawable.ic_baseline_pause_circle_outline_24, getString(R.string.app_name),
+ MediaButtonReceiver.buildMediaButtonPendingIntent(this,
+ PlaybackStateCompat.ACTION_PLAY_PAUSE)))
+ .addAction(new NotificationCompat.Action(
+ R.drawable.ic_baseline_play_circle_outline_24, getString(R.string.app_name),
+ MediaButtonReceiver.buildMediaButtonPendingIntent(this,
+ PlaybackStateCompat.ACTION_PLAY)))
+ .setStyle(new androidx.media.app.NotificationCompat.MediaStyle().
+ setMediaSession(mediaSession.getSessionToken())
+ .setShowActionsInCompactView(0))
+ .build(); //通知のビルド
+
+ } else {
+ //APIが「25」以下の場合
+ //通知の生成と設定とビルド
+ notification = new Notification.Builder(this)
+ .setContentTitle(getString(R.string.app_name))
+ .setContentText("アプリ通知テスト25まで")
+ .setSmallIcon(R.drawable.ic_baseline_play_circle_outline_24)
+ .build();
+ }
+
+ //通知の発行
+ notificationManager.notify(1, notification);
+ }
+
+ //一時停止処理
+ public void voicePause(){
+ if(mState==State.PAUSE)return;
+ if(mediaPlayer.isPlaying()){
+ mediaPlayer.pause();
+ System.out.println("pause");
+ mState= State.PAUSE;
+ }else {
+ //mediaPlayer.start();
+ }
+ }
+ //停止からの再生処理
+ public void voicePlay(){
+ if(mediaPlayer.isPlaying()==false){
+ mediaPlayer.start();
+ System.out.println("Restart");
+ }else {
+ // mediaPlayer.pause();
+ }
+ }
+ //完了呼び出し
+ public void completeAction(){
+// prepareAndPlay();
+// mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+// @Override
+// public void onCompletion(MediaPlayer mediaPlayer) {
+// prepareAndPlay();
+// System.out.println("Complete!!!");
+// }
+// });
+ }
}
diff --git a/app/src/main/java/org/ntlab/amaryllis/client/voiceservice/TestVoiceService.java b/app/src/main/java/org/ntlab/amaryllis/client/voiceservice/TestVoiceService.java
index 236851c..5cb2532 100644
--- a/app/src/main/java/org/ntlab/amaryllis/client/voiceservice/TestVoiceService.java
+++ b/app/src/main/java/org/ntlab/amaryllis/client/voiceservice/TestVoiceService.java
@@ -95,7 +95,6 @@
audioManager=(AudioManager) getSystemService(Context.AUDIO_SERVICE);
mState = State.STOP;
-
sampleReceiver=new ComponentName(this,VoicememoReceiver.class);
mediaPlayer=new MediaPlayer();
mediaSession=new MediaSessionCompat(getApplicationContext(),TAG);