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);