diff --git a/app/src/main/java/org/ntlab/amaryllis/client/TestPlayActivity.java b/app/src/main/java/org/ntlab/amaryllis/client/TestPlayActivity.java index 4c0f1b2..82783be 100644 --- a/app/src/main/java/org/ntlab/amaryllis/client/TestPlayActivity.java +++ b/app/src/main/java/org/ntlab/amaryllis/client/TestPlayActivity.java @@ -1,54 +1,47 @@ package org.ntlab.amaryllis.client; -import androidx.annotation.NonNull; import androidx.appcompat.app.AppCompatActivity; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; -import android.media.AudioManager; -import android.media.MediaPlayer; import android.os.Bundle; import android.os.IBinder; import android.os.RemoteException; -import android.support.v4.media.MediaBrowserCompat; -import android.support.v4.media.MediaMetadataCompat; import android.support.v4.media.session.MediaControllerCompat; -import android.support.v4.media.session.PlaybackStateCompat; import android.view.View; import android.widget.Button; import android.widget.TextView; -import android.widget.Toast; -import org.ntlab.amaryllis.client.entities.AmaryllisBackgroundService; import org.ntlab.amaryllis.client.voiceservice.TestVoiceService; -import java.io.IOException; -import java.util.List; - public class TestPlayActivity extends AppCompatActivity { - MediaBrowserCompat mBrowser; - MediaControllerCompat mController; + + Button button_connect; Button button_play; TextView textView_title; TextView textView_position; - MediaPlayer mediaPlayer; + TestVoiceService myService; Intent serviceIntent; - + MediaControllerCompat mediaController; Button mPlayButton; ServiceConnection serviceConnection = new ServiceConnection(){ @Override public void onServiceConnected(ComponentName name, IBinder service) { myService = ((TestVoiceService.MyBinder)service).getService(); + try { + mediaController=new MediaControllerCompat(TestPlayActivity.this,myService.getToken()); + } catch (RemoteException e) { + e.printStackTrace(); + } //myService.setMessage("Hello, IBinder!"); //startService(serviceIntent); } @Override public void onServiceDisconnected(ComponentName name) { - myService = null; } }; @@ -57,22 +50,34 @@ super.onCreate(savedInstanceState); setContentView(R.layout.activity_test_play); + button_connect =findViewById(R.id.button_connect); button_play=findViewById(R.id.button_play); + button_play.setEnabled(false); //textView_title=findViewById(R.id.textView_title); // textView_position=findViewById(R.id.textView_position); serviceIntent=new Intent( getBaseContext(),TestVoiceService.class ); - //bindService(serviceIntent,serviceConnection, Context.BIND_AUTO_CREATE); + + button_connect.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + //serviceIntent.setAction(TestVoiceService.ACTION_PLAY); + //startService(serviceIntent); + bindService(serviceIntent,serviceConnection, Context.BIND_AUTO_CREATE); + button_play.setEnabled(true); + if(myService!=null)myService.testCall(); + } + }); button_play.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { - - serviceIntent.setAction(TestVoiceService.ACTION_PLAY); - startService(serviceIntent); - //myService.testCall(); + mediaController.getTransportControls().play(); } }); + + + /* button_play.setOnClickListener(new View.OnClickListener() { @Override @@ -84,5 +89,8 @@ //mPlayButton = (Button) findViewById(R.id.play); //mPlayButton.setOnClickListener(this); + + } + } \ No newline at end of file diff --git a/app/src/main/java/org/ntlab/amaryllis/client/voiceservice/MusicService.java b/app/src/main/java/org/ntlab/amaryllis/client/voiceservice/MusicService.java new file mode 100644 index 0000000..ca097fd --- /dev/null +++ b/app/src/main/java/org/ntlab/amaryllis/client/voiceservice/MusicService.java @@ -0,0 +1,371 @@ +package org.ntlab.amaryllis.client.voiceservice; + +import android.app.PendingIntent; +import android.content.Context; +import android.content.Intent; +import android.media.AudioManager; +import android.media.MediaPlayer; +import android.os.Bundle; +import android.os.Handler; +import android.support.v4.media.MediaBrowserCompat; +import android.support.v4.media.MediaDescriptionCompat; +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.TestPlayActivity; + +import java.util.ArrayList; +import java.util.List; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.content.ContextCompat; +import androidx.media.MediaBrowserServiceCompat; +import androidx.media.session.MediaButtonReceiver; + +public class MusicService extends MediaBrowserServiceCompat { + final String TAG = MusicService.class.getSimpleName();//ログ用タグ + final String ROOT_ID = "root";//クライアントに返すID onGetRoot / onLoadChildrenで使用 + + Handler handler;//定期的に処理を回すためのHandler + + MediaSessionCompat mSession;//主役のMediaSession + AudioManager am;//AudioFoucsを扱うためのManager + MediaPlayer mediaPlayer; + + int index = 0;//再生中のインデックス + List queueItems = new ArrayList<>();//キューに使用するリスト + @Override + public void onCreate() { + super.onCreate(); + //AudioManagerを取得 + am = (AudioManager) getSystemService(Context.AUDIO_SERVICE); + //MediaSessionを初期化 + mSession = new MediaSessionCompat(getApplicationContext(), TAG); + //このMediaSessionが提供する機能を設定 + mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | //ヘッドフォン等のボタンを扱う + MediaSessionCompat.FLAG_HANDLES_QUEUE_COMMANDS | //キュー系のコマンドの使用をサポート + MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS); //再生、停止、スキップ等のコントロールを提供 + + //クライアントからの操作に応じるコールバックを設定 + mSession.setCallback(callback); + + //MediaBrowserServiceにSessionTokenを設定 + //setSessionToken(mSession.getSessionToken());//停止の原因 + + //Media Sessionのメタデータや、プレイヤーのステータスが更新されたタイミングで + //通知の作成/更新をする + mSession.getController().registerCallback(new MediaControllerCompat.Callback() { + @Override + public void onPlaybackStateChanged(PlaybackStateCompat state) { + CreateNotification(); + } + + @Override + public void onMetadataChanged(MediaMetadataCompat metadata) { + CreateNotification(); + } + }); + // CreateNotification(); + + //キューにアイテムを追加 + int i = 0; +// for (MediaBrowserCompat.MediaItem media : MusicLibrary.getMediaItems()) { +// queueItems.add(new MediaSessionCompat.QueueItem(media.getDescription(), i)); +// i++; +// } + //mSession.setQueue(queueItems);//WearやAutoにキューが表示される + + mediaPlayer =new MediaPlayer(); + handler = new Handler(); + //500msごとに再生情報を更新 + handler.postDelayed(new Runnable() { + @Override + public void run() { + //再生中にアップデート + + //再度実行 + handler.postDelayed(this, 500); + } + }, 500); + } + + //クライアント接続時に呼び出される + //パッケージ名などから接続するかどうかを決定する + //任意の文字列を返すと接続許可 + //nullで接続拒否 + //今回は全ての接続を許可 + @Override + public BrowserRoot onGetRoot(@NonNull String clientPackageName, + int clientUid, + Bundle rootHints) { + Log.d(TAG, "Connected from pkg:" + clientPackageName + " uid:" + clientUid); + + return new BrowserRoot(ROOT_ID, null); + } + + //クライアント側がsubscribeを呼び出すと呼び出される + //音楽ライブラリの内容を返す + //WearやAutoで表示される曲のリストにも使われる + //デフォルトでonGetRootで返した文字列がparentMediaIdに渡される + //ブラウザ画面で子要素を持っているMediaItemを選択した際にもそのIdが渡される + @Override + public void onLoadChildren( + @NonNull final String parentMediaId, + @NonNull final Result> result) { + + if (parentMediaId.equals(ROOT_ID)); + //曲のリストをクライアントに送信 + + else//今回はROOT_ID以外は無効 + result.sendResult(new ArrayList()); + } + @Override + public void onDestroy() { + Log.d(TAG, "onDestroy"); + mSession.setActive(false); + mSession.release(); + mediaPlayer.stop(); + mediaPlayer.release(); + } + + + //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))); + + + + // mediaPlayer.prepare(); + + mSession.setActive(true); + + onPlay(); + + //MediaSessionが配信する、再生中の曲の情報を設定 + // mSession.setMetadata(MusicLibrary.getMetadata(getApplicationContext(), mediaId)); + } + + //再生をリクエストされたとき + @Override + public void onPlay() { + //オーディオフォーカスを要求 +// if (am.requestAudioFocus(afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN) == AudioManager.AUDIOFOCUS_REQUEST_GRANTED) { +// //取得できたら再生を始める +// mSession.setActive(true); +// exoPlayer.setPlayWhenReady(true); +// } + } + + //一時停止をリクエストされたとき + @Override + public void onPause() { + // exoPlayer.setPlayWhenReady(false); + //オーディオフォーカスを開放 + // am.abandonAudioFocus(afChangeListener); + } + + //停止をリクエストされたとき + @Override + public void onStop() { + onPause(); + mSession.setActive(false); + //オーディオフォーカスを開放 + //am.abandonAudioFocus(afChangeListener); + } + + //シークをリクエストされたとき + @Override + public void onSeekTo(long pos) { + //mediaPlayer.seekTo(pos); + } + + //次の曲をリクエストされたとき + @Override + public void onSkipToNext() { + index++; + if (index >= queueItems.size())//ライブラリの最後まで再生したら + index = 0;//最初に戻す + + onPlayFromMediaId(queueItems.get(index).getDescription().getMediaId(), null); + } + + //前の曲をリクエストされたとき + @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); + } + }; + + + + //MediaSessionが配信する、現在のプレイヤーの状態を設定する + //ここには再生位置の情報も含まれるので定期的に更新する + private void UpdatePlaybackState() { +// int state = PlaybackStateCompat.STATE_NONE; +// //プレイヤーの状態からふさわしいMediaSessionのステータスを設定する +// switch (exoPlayer.getPlaybackState()) { +// case Player.STATE_IDLE: +// state = PlaybackStateCompat.STATE_NONE; +// break; +// case Player.STATE_BUFFERING: +// state = PlaybackStateCompat.STATE_BUFFERING; +// break; +// case Player.STATE_READY: +// if (exoPlayer.getPlayWhenReady()) +// state = PlaybackStateCompat.STATE_PLAYING; +// else +// state = PlaybackStateCompat.STATE_PAUSED; +// break; +// case Player.STATE_ENDED: +// state = PlaybackStateCompat.STATE_STOPPED; +// break; +// } +// +// //プレイヤーの情報、現在の再生位置などを設定する +// //また、MeidaButtonIntentでできる操作を設定する +// mSession.setPlaybackState(new PlaybackStateCompat.Builder() +// .setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_NEXT | PlaybackStateCompat.ACTION_STOP) +// .setState(state, exoPlayer.getCurrentPosition(), exoPlayer.getPlaybackParameters().speed) +// .build()); + } + //通知を作成、サービスをForegroundにする + private void CreateNotification() { + MediaControllerCompat controller = mSession.getController(); + MediaMetadataCompat mediaMetadata = controller.getMetadata(); + + if (mediaMetadata == null && !mSession.isActive()) return; + + MediaDescriptionCompat description = mediaMetadata.getDescription(); + + NotificationCompat.Builder builder = new NotificationCompat.Builder(getApplicationContext()); + + builder + //現在の曲の情報を設定 + .setContentTitle(description.getTitle()) + .setContentText(description.getSubtitle()) + .setSubText(description.getDescription()) + .setLargeIcon(description.getIconBitmap()) + + // 通知をクリックしたときのインテントを設定 + .setContentIntent(createContentIntent()) + + // 通知がスワイプして消された際のインテントを設定 + .setDeleteIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this, + PlaybackStateCompat.ACTION_STOP)) + + // 通知の範囲をpublicにしてロック画面に表示されるようにする + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + + .setSmallIcon(R.drawable.ic_baseline_play_circle_outline_24) + //通知の領域に使う色を設定 + //Androidのバージョンによってスタイルが変わり、色が適用されない場合も多い + .setColor(ContextCompat.getColor(this, R.color.colorAccent)) + + // Media Styleを利用する + .setStyle(new androidx.media.app.NotificationCompat.MediaStyle() + .setMediaSession(mSession.getSessionToken()) + //通知を小さくたたんだ時に表示されるコントロールのインデックスを設定 + .setShowActionsInCompactView(1)); + + // Android4.4以前は通知をスワイプで消せないので + //キャンセルボタンを表示することで対処 + //今回はminSDKが21なので必要ない + //.setShowCancelButton(true) + //.setCancelButtonIntent(MediaButtonReceiver.buildMediaButtonPendingIntent(this, + // PlaybackStateCompat.ACTION_STOP))); + + //通知のコントロールの設定 +// builder.addAction(new NotificationCompat.Action( +// R.drawable.exo_controls_previous, "prev", +// MediaButtonReceiver.buildMediaButtonPendingIntent(this, +// PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS))); + + //プレイヤーの状態で再生、一時停止のボタンを設定 + if (controller.getPlaybackState().getState() == PlaybackStateCompat.STATE_PLAYING) { + builder.addAction(new NotificationCompat.Action( + R.drawable.ic_baseline_pause_circle_outline_24, "pause", + MediaButtonReceiver.buildMediaButtonPendingIntent(this, + PlaybackStateCompat.ACTION_PAUSE))); + } else { + builder.addAction(new NotificationCompat.Action( + R.drawable.ic_baseline_play_circle_outline_24, "play", + MediaButtonReceiver.buildMediaButtonPendingIntent(this, + PlaybackStateCompat.ACTION_PLAY))); + } + + +// builder.addAction(new NotificationCompat.Action( +// R.drawable.exo_controls_next, "next", +// MediaButtonReceiver.buildMediaButtonPendingIntent(this, +// PlaybackStateCompat.ACTION_SKIP_TO_NEXT))); + + startForeground(1, builder.build()); + + //再生中以外ではスワイプで通知を消せるようにする + if (controller.getPlaybackState().getState() != PlaybackStateCompat.STATE_PLAYING) + stopForeground(false); + } + + //通知をクリックしてActivityを開くインテントを作成 + private PendingIntent createContentIntent() { + Intent openUI = new Intent(this, TestPlayActivity.class); + openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + return PendingIntent.getActivity( + this, 1, openUI, PendingIntent.FLAG_CANCEL_CURRENT); + } + + //オーディオフォーカスのコールバック + AudioManager.OnAudioFocusChangeListener afChangeListener = + new AudioManager.OnAudioFocusChangeListener() { + public void onAudioFocusChange(int focusChange) { + //フォーカスを完全に失ったら + if (focusChange == AudioManager.AUDIOFOCUS_LOSS) { + //止める + mSession.getController().getTransportControls().pause(); + } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT) {//一時的なフォーカスロスト + //止める + mSession.getController().getTransportControls().pause(); + } else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK) {//通知音とかによるフォーカスロスト(ボリュームを下げて再生し続けるべき) + //本来なら音量を一時的に下げるべきだが何もしない + } else if (focusChange == AudioManager.AUDIOFOCUS_GAIN) {//フォーカスを再度得た場合 + //再生 + mSession.getController().getTransportControls().play(); + } + } + }; +} 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 ab5e055..475e98f 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 @@ -2,6 +2,9 @@ import android.annotation.SuppressLint; import android.app.IntentService; +import android.app.Notification; +import android.app.NotificationChannel; +import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ComponentName; import android.content.Context; @@ -13,17 +16,27 @@ import android.media.RemoteControlClient; import android.media.RemoteController; import android.media.browse.MediaBrowser; +import android.media.session.MediaSession; import android.net.Uri; import android.os.Binder; +import android.os.Build; import android.os.Bundle; import android.os.Handler; import android.os.IBinder; +import android.os.PowerManager; import android.service.media.MediaBrowserService; +import android.support.v4.media.MediaDescriptionCompat; +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.TestPlayActivity; import java.io.IOException; import java.util.ArrayList; @@ -31,11 +44,14 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.core.app.NotificationCompat; +import androidx.core.app.NotificationManagerCompat; +import androidx.core.content.ContextCompat; +import androidx.media.MediaBrowserServiceCompat; +import androidx.media.session.MediaButtonReceiver; - -public class TestVoiceService extends IntentService implements MediaPlayer.OnPreparedListener, - AudioManager.OnAudioFocusChangeListener { +public class TestVoiceService extends IntentService implements MediaPlayer.OnPreparedListener{ final String TAG = TestVoiceService.class.getSimpleName();//ログ用タグ final String ROOT_ID = "root";//クライアントに返すID onGetRoot / onLoadChildrenで使用 @@ -43,16 +59,14 @@ public static final String ACTION_PLAY = "org.ntlab.amaryllis.client.voiceservice.ACTION_PLAY"; - Handler handler;//定期的に処理を回すためのHandler + private PlayListManager playListManager; MediaPlayer mediaPlayer; AudioManager audioManager; - RemoteControlClient mRemoteCtlClient; + MediaSessionCompat mediaSession; ComponentName sampleReceiver; - - enum State{PLAY,STOP,PAUSE}; State mState=State.STOP; @@ -76,17 +90,28 @@ super.onCreate(); audioManager=(AudioManager) getSystemService(Context.AUDIO_SERVICE); - audioManager.requestAudioFocus(this, AudioManager.STREAM_MUSIC, - AudioManager.AUDIOFOCUS_GAIN); + mState = 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 + ); //再生、停止、スキップ等のコントロールを提供 + mediaSession.setCallback(callback); + } + 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; // ロック画面で再生/一時停止ボタン押下時 if (action.equals(ACTION_TOGGLE)) { if (mState == State.PAUSE || mState == State.STOP) { @@ -97,11 +122,7 @@ mState = State.PAUSE; mediaPlayer.pause(); - if (mRemoteCtlClient != null) { - // リモートコントロールクライアントに状態をセットする - mRemoteCtlClient - .setPlaybackState(RemoteControlClient.PLAYSTATE_PAUSED); - } + } } @@ -122,10 +143,7 @@ mediaPlayer.release(); mediaPlayer = null; } - @Override - public void onAudioFocusChange(int i) { - } public void onPrepared(MediaPlayer player) { mState = State.PLAY; @@ -136,8 +154,8 @@ public IBinder onBind(Intent intent){ Log.d(TAG,"onBind"); - return null; - //return myBinder; + return myBinder; + } public void play(){ @@ -156,17 +174,7 @@ e.printStackTrace(); } } - public void switchPlayButton(){ - } - String getNextVid(){ - //return playListManager.getUnplayed().remove(0); - return null; - } - void onPlayEnd(){ - // playListManager.getUnplayed().get(0); - - } public void testCall(){ System.out.println("CALL!!!!"); } @@ -181,11 +189,6 @@ mediaPlayer.start(); } - if (mRemoteCtlClient != null) { - // リモートコントロールクライアントに状態をセットする - mRemoteCtlClient - .setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); - } } public void prepareAndPlay() { mState = State.STOP; @@ -198,7 +201,7 @@ mediaPlayer.reset(); mediaPlayer.setDataSource(url); mediaPlayer.prepare(); - //mediaPlayer.setLooping(true); +// mediaPlayer.setLooping(true); mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { @Override public void onCompletion(MediaPlayer mediaPlayer) { @@ -209,56 +212,173 @@ } catch (IOException e) { e.printStackTrace(); } - // メディアボタンレシーバーをセットする - //audioManager.registerMediaButtonEventReceiver(sampleReceiver); - - // リモートコントローラーインスタンスを生成 - if (mRemoteCtlClient == null) { - - // リモートコントローラー用のIntentを生成 - Intent intent = new Intent(Intent.ACTION_MEDIA_BUTTON); - intent.setComponent(sampleReceiver); - - // PendingIntentインスタンスを取得 - PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, - intent, 0); - - // RemoteControlClientインスタンスを生成 - mRemoteCtlClient = new RemoteControlClient(pendingIntent); - - // オーディオマネージャーにクライアントをセットする - audioManager.registerRemoteControlClient(mRemoteCtlClient); - } - - // 利用するコントロール機能を指定する(以下では再生/一時停止/停止/次曲を指定) - mRemoteCtlClient - .setTransportControlFlags(RemoteControlClient.FLAG_KEY_MEDIA_PLAY - | RemoteControlClient.FLAG_KEY_MEDIA_PAUSE - | RemoteControlClient.FLAG_KEY_MEDIA_NEXT - | RemoteControlClient.FLAG_KEY_MEDIA_STOP); - - // RemoteControlClientにメタ情報をセットする - RemoteControlClient.MetadataEditor editMeta = mRemoteCtlClient.editMetadata(true); - -// editMeta.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, -// "アーティスト名") -// .putString(MediaMetadataRetriever.METADATA_KEY_TITLE, "曲名") -// -// // 曲の再生時間(ミリ秒) -// .putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, 50000).apply(); - - mediaPlayer.start(); - mState = State.PLAY; - - // リモートコントロールクライアントに状態をセットする - mRemoteCtlClient - .setPlaybackState(RemoteControlClient.PLAYSTATE_PLAYING); } + @Override protected void onHandleIntent(@Nullable Intent intent) { } + //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); + final String url = "http://nitta-lab-www.is.konan-u.ac.jp/data/c948c729-a516-4ca2-88f3-35c0f2d20dba.3gp"; + System.out.println("PLAYだよ!"); + mediaPlayer=new MediaPlayer(); + mediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC); + try { + mediaPlayer.reset(); + mediaPlayer.setDataSource(url); + mediaPlayer.prepare(); +// mediaPlayer.setLooping(true); + + mediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() { + @Override + public void onCompletion(MediaPlayer mediaPlayer) { + + } + }); + mediaPlayer.start(); + } catch (IOException e) { + e.printStackTrace(); + } + CreateNotification(); + } + + //一時停止をリクエストされたとき + @Override + public void onPause() { +// exoPlayer.setPlayWhenReady(false); +// //オーディオフォーカスを開放 +// am.abandonAudioFocus(afChangeListener); + } + + //停止をリクエストされたとき + @Override + public void onStop() { +// onPause(); +// mSession.setActive(false); +// //オーディオフォーカスを開放 +// am.abandonAudioFocus(afChangeListener); + } + + //シークをリクエストされたとき + @Override + public void onSeekTo(long pos) { +// exoPlayer.seekTo(pos); + } + + //次の曲をリクエストされたとき + @Override + public void onSkipToNext() { +// index++; +// if (index >= MusicLibrary.getMediaItems().size())//ライブラリの最後まで再生したら +// index = 0;//最初に戻す +// +// onPlayFromMediaId(queueItems.get(index).getDescription().getMediaId(), null); + } + + //前の曲をリクエストされたとき + @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); + } + }; + + //通知を作成、サービスをForegroundにする + 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 Notification.Builder(this, chID) + .setContentTitle(getString(R.string.app_name)) //通知タイトル + .setContentText("アプリ通知テスト26以上") //通知内容 + .setSmallIcon(R.drawable.ic_baseline_play_circle_outline_24) //通知用アイコン + .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); + } + + //通知をクリックしてActivityを開くインテントを作成 + private PendingIntent createContentIntent() { + Intent openUI = new Intent(this, TestPlayActivity.class); + openUI.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP); + return PendingIntent.getActivity( + this, 1, openUI, PendingIntent.FLAG_CANCEL_CURRENT); + } }