Add metadata and notifications to OneMedia

This wires up a notification and some basic metadata for testing
in OneMedia.

Change-Id: I0f2e922536c85caa63f66dae7deb55ffe94fe231
This commit is contained in:
Daniel Sandler
2014-06-05 02:54:13 -04:00
committed by RoboErik
parent 1d2a1c917f
commit 45f7ee8201
17 changed files with 568 additions and 11 deletions

View File

@@ -23,7 +23,7 @@
</activity>
<service
android:name="com.android.onemedia.OnePlayerService"
android:exported="false"
android:exported="true"
android:process="com.android.onemedia.service" />
<service
android:name=".provider.OneMediaRouteProvider"

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24" >
<path
android:fillColor="#FFFFFFFF"
android:pathData="M4.0,18.0l8.5,-6.0L4.0,6.0L4.0,18.0zM13.0,6.0l0.0,12.0l8.5,-6.0L13.0,6.0z"/>
</vector>

View File

@@ -0,0 +1,24 @@
<!--
Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24" >
<path
android:fillColor="#FFFFFFFF"
android:pathData="M11.0,18.0L11.0,6.0l-8.5,6.0L11.0,18.0zM11.5,12.0l8.5,6.0L20.0,6.0L11.5,12.0z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24" >
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.0,19.0l4.0,0.0L10.0,5.0L6.0,5.0L6.0,19.0zM14.0,5.0l0.0,14.0l4.0,0.0L18.0,5.0L14.0,5.0z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24" >
<path
android:fillColor="#FFFFFFFF"
android:pathData="M8.0,5.0l0.0,14.0 11.0,-7.0z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24" >
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.0,18.0l8.5,-6.0L6.0,6.0L6.0,18.0zM16.0,6.0l0.0,12.0l2.0,0.0L18.0,6.0L16.0,6.0z"/>
</vector>

View File

@@ -0,0 +1,28 @@
<!--
Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24" >
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.0,6.0l2.0,0.0l0.0,12.0l-2.0,0.0z"/>
<path
android:fillColor="#FFFFFFFF"
android:pathData="M9.5,12.0l8.5,6.0 0.0,-12.0z"/>
</vector>

View File

@@ -0,0 +1,25 @@
<!--
Copyright (C) 2014 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="36dp"
android:height="36dp"
android:viewportWidth="24"
android:viewportHeight="24" >
<path
android:fillColor="#FFFFFFFF"
android:pathData="M6.0,6.0l12.0,0.0l0.0,12.0l-12.0,0.0z"/>
</vector>

View File

@@ -33,6 +33,19 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/has_video" />
<ImageView
android:id="@+id/art"
android:layout_width="match_parent"
android:layout_height="96dp"
android:scaleType="centerCrop"
android:visibility="gone"
/>
<Button
android:id="@+id/art_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/art_picker"
/>
<LinearLayout
android:id="@+id/controls"
android:layout_width="match_parent"

View File

@@ -12,5 +12,5 @@
<string name="media_next_hint">Next content</string>
<string name="has_video">Is video</string>
<string name="has_duration">Has duration</string>
<string name="art_picker">Choose artwork</string>
</resources>

View File

@@ -15,6 +15,7 @@
package com.android.onemedia;
import android.graphics.Bitmap;
import android.media.session.MediaSession;
import android.os.Bundle;
@@ -26,4 +27,5 @@ interface IPlayerService {
void registerCallback(in IPlayerCallback cb);
void unregisterCallback(in IPlayerCallback cb);
void sendRequest(String action, in Bundle params, in IRequestCallback cb);
void setIcon(in Bitmap icon);
}

View File

@@ -0,0 +1,234 @@
package com.android.onemedia;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
import android.media.session.MediaController;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.text.format.DateUtils;
import android.util.Log;
import android.util.SparseArray;
import com.android.onemedia.playback.RequestUtils;
/**
* Keeps track of a notification and updates it automatically for a given
* MediaSession.
*/
public class NotificationHelper extends BroadcastReceiver {
private static final String TAG = "NotificationHelper";
private static final int NOTIFICATION_ID = 433; // John Cage, 1952
private final Service mService;
private final MediaSession mSession;
private final MediaController mController;
private final MediaController.TransportControls mTransportControls;
private final SparseArray<PendingIntent> mIntents = new SparseArray<PendingIntent>();
private PlaybackState mPlaybackState;
private MediaMetadata mMetadata;
private boolean mStarted = false;
public NotificationHelper(Service service, MediaSession session) {
mService = service;
mSession = session;
mController = session.getController();
mTransportControls = mController.getTransportControls();
String pkg = mService.getPackageName();
mIntents.put(R.drawable.ic_pause, PendingIntent.getBroadcast(mService, 100, new Intent(
com.android.onemedia.playback.RequestUtils.ACTION_PAUSE).setPackage(pkg),
PendingIntent.FLAG_CANCEL_CURRENT));
mIntents.put(R.drawable.ic_play_arrow, PendingIntent.getBroadcast(mService, 100,
new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PLAY).setPackage(pkg),
PendingIntent.FLAG_CANCEL_CURRENT));
mIntents.put(R.drawable.ic_skip_previous, PendingIntent.getBroadcast(mService, 100,
new Intent(com.android.onemedia.playback.RequestUtils.ACTION_PREV).setPackage(pkg),
PendingIntent.FLAG_CANCEL_CURRENT));
mIntents.put(R.drawable.ic_skip_next, PendingIntent.getBroadcast(mService, 100,
new Intent(com.android.onemedia.playback.RequestUtils.ACTION_NEXT).setPackage(pkg),
PendingIntent.FLAG_CANCEL_CURRENT));
mIntents.put(R.drawable.ic_fast_rewind, PendingIntent.getBroadcast(mService, 100,
new Intent(com.android.onemedia.playback.RequestUtils.ACTION_REW).setPackage(pkg),
PendingIntent.FLAG_CANCEL_CURRENT));
mIntents.put(R.drawable.ic_fast_forward, PendingIntent.getBroadcast(mService, 100,
new Intent(com.android.onemedia.playback.RequestUtils.ACTION_FFWD).setPackage(pkg),
PendingIntent.FLAG_CANCEL_CURRENT));
}
/**
* Posts the notification and starts tracking the session to keep it
* updated. The notification will automatically be removed if the session is
* destroyed before {@link #onStop} is called.
*/
public void onStart() {
mController.addCallback(mCb);
IntentFilter filter = new IntentFilter();
filter.addAction(RequestUtils.ACTION_FFWD);
filter.addAction(RequestUtils.ACTION_NEXT);
filter.addAction(RequestUtils.ACTION_PAUSE);
filter.addAction(RequestUtils.ACTION_PLAY);
filter.addAction(RequestUtils.ACTION_PREV);
filter.addAction(RequestUtils.ACTION_REW);
mService.registerReceiver(this, filter);
mMetadata = mController.getMetadata();
mPlaybackState = mController.getPlaybackState();
mStarted = true;
// The notification must be updated after setting started to true
updateNotification();
}
/**
* Removes the notification and stops tracking the session. If the session
* was destroyed this has no effect.
*/
public void onStop() {
mStarted = false;
mController.removeCallback(mCb);
mService.unregisterReceiver(this);
updateNotification();
}
@Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
Log.d(TAG, "Received intent with action " + action);
if (RequestUtils.ACTION_PAUSE.equals(action)) {
mTransportControls.pause();
} else if (RequestUtils.ACTION_PLAY.equals(action)) {
mTransportControls.play();
} else if (RequestUtils.ACTION_NEXT.equals(action)) {
mTransportControls.skipToNext();
} else if (RequestUtils.ACTION_PREV.equals(action)) {
mTransportControls.skipToPrevious();
} else if (RequestUtils.ACTION_REW.equals(action)) {
mTransportControls.rewind();
} else if (RequestUtils.ACTION_FFWD.equals(action)) {
mTransportControls.fastForward();
}
}
private final MediaController.Callback mCb = new MediaController.Callback() {
@Override
public void onPlaybackStateChanged(PlaybackState state) {
mPlaybackState = state;
Log.d(TAG, "Received new playback state" + state);
updateNotification();
}
@Override
public void onMetadataChanged(MediaMetadata metadata) {
mMetadata = metadata;
Log.d(TAG, "Received new metadata " + metadata);
updateNotification();
}
};
NotificationManager mNoMan = null;
private void updateNotification() {
if (mNoMan == null) {
mNoMan = (NotificationManager) mService.getSystemService(Context.NOTIFICATION_SERVICE);
}
if (mPlaybackState == null) {
mNoMan.cancel(NOTIFICATION_ID);
return;
}
if (!mStarted) {
mNoMan.cancel(NOTIFICATION_ID);
return;
}
String status;
final int state = mPlaybackState.getState();
switch (state) {
case PlaybackState.STATE_PLAYING:
status = "PLAYING: ";
break;
case PlaybackState.STATE_PAUSED:
status = "PAUSED: ";
break;
case PlaybackState.STATE_STOPPED:
status = "STOPPED: ";
break;
case PlaybackState.STATE_ERROR:
status = "ERROR: ";
break;
case PlaybackState.STATE_BUFFERING:
status = "BUFFERING: ";
break;
case PlaybackState.STATE_NONE:
default:
status = "";
break;
}
CharSequence title, text;
Bitmap art;
if (mMetadata == null) {
title = status;
text = "Empty metadata!";
art = null;
} else {
MediaMetadata.Description description = mMetadata.getDescription();
title = description.getTitle();
text = description.getSubtitle();
art = description.getIcon();
}
String playPauseLabel = "";
int playPauseIcon;
if (state == PlaybackState.STATE_PLAYING) {
playPauseLabel = "Pause";
playPauseIcon = R.drawable.ic_pause;
} else {
playPauseLabel = "Play";
playPauseIcon = R.drawable.ic_play_arrow;
}
final long pos = mPlaybackState.getPosition();
final long end = mMetadata == null ? 0 : mMetadata
.getLong(MediaMetadata.METADATA_KEY_DURATION);
Notification notification = new Notification.Builder(mService)
.setSmallIcon(android.R.drawable.stat_notify_chat)
.setContentTitle(title)
.setContentText(text)
.setShowWhen(false)
.setContentInfo(DateUtils.formatElapsedTime(pos))
.setProgress((int) end, (int) pos, false)
.setLargeIcon(art)
.addAction(R.drawable.ic_skip_previous, "Previous",
mIntents.get(R.drawable.ic_skip_previous))
.addAction(R.drawable.ic_fast_rewind, "Rewind",
mIntents.get(R.drawable.ic_fast_rewind))
.addAction(playPauseIcon, playPauseLabel,
mIntents.get(playPauseIcon))
.addAction(R.drawable.ic_fast_forward, "Fast Forward",
mIntents.get(R.drawable.ic_fast_forward))
.addAction(R.drawable.ic_skip_next, "Next",
mIntents.get(R.drawable.ic_skip_next))
.setStyle(new Notification.MediaStyle()
.setShowActionsInCompactView(2)
.setMediaSession(mSession.getSessionToken()))
.setColor(0xFFDB4437)
.build();
mService.startForeground(NOTIFICATION_ID, notification);
}
}

View File

@@ -17,20 +17,35 @@ package com.android.onemedia;
import android.app.Activity;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.ComponentName;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.MediaMetadata;
import android.media.session.PlaybackState;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.text.format.DateUtils;
import android.util.Log;
import android.view.Menu;
import android.view.View;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import java.io.IOException;
public class OnePlayerActivity extends Activity {
private static final String TAG = "OnePlayerActivity";
private static final int READ_REQUEST_CODE = 42;
protected PlayerController mPlayer;
private Button mStartButton;
@@ -41,8 +56,10 @@ public class OnePlayerActivity extends Activity {
private EditText mContentText;
private EditText mNextContentText;
private CheckBox mHasVideo;
private ImageView mArtView;
private int mPlaybackState;
private PlaybackState mPlaybackState;
private Bitmap mAlbumArtBitmap;
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -58,6 +75,10 @@ public class OnePlayerActivity extends Activity {
mContentText = (EditText) findViewById(R.id.content);
mNextContentText = (EditText) findViewById(R.id.next_content);
mHasVideo = (CheckBox) findViewById(R.id.has_video);
mArtView = (ImageView) findViewById(R.id.art);
final Button artPicker = (Button) findViewById(R.id.art_picker);
artPicker.setOnClickListener(mButtonListener);
mStartButton.setOnClickListener(mButtonListener);
mPlayButton.setOnClickListener(mButtonListener);
@@ -86,6 +107,31 @@ public class OnePlayerActivity extends Activity {
super.onPause();
}
@Override
public void onActivityResult(int requestCode, int resultCode,
Intent resultData) {
if (requestCode == READ_REQUEST_CODE && resultCode == Activity.RESULT_OK) {
Uri uri = null;
if (resultData != null) {
uri = resultData.getData();
Log.i(TAG, "Uri: " + uri.toString());
mAlbumArtBitmap = null;
try {
mAlbumArtBitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(), uri);
} catch (IOException e) {
Log.v(TAG, "Couldn't load album art", e);
}
mArtView.setImageBitmap(mAlbumArtBitmap);
if (mAlbumArtBitmap != null) {
mArtView.setVisibility(View.VISIBLE);
} else {
mArtView.setVisibility(View.GONE);
}
mPlayer.setArt(mAlbumArtBitmap);
}
}
}
private void setControlsEnabled(boolean enabled) {
mStartButton.setEnabled(enabled);
mPlayButton.setEnabled(enabled);
@@ -94,36 +140,46 @@ public class OnePlayerActivity extends Activity {
private View.OnClickListener mButtonListener = new View.OnClickListener() {
@Override
public void onClick(View v) {
final int state = mPlaybackState.getState();
switch (v.getId()) {
case R.id.play_button:
Log.d(TAG, "Play button pressed, in state " + mPlaybackState);
if (mPlaybackState == PlaybackState.STATE_PAUSED
|| mPlaybackState == PlaybackState.STATE_STOPPED) {
Log.d(TAG, "Play button pressed, in state " + state);
if (state == PlaybackState.STATE_PAUSED
|| state == PlaybackState.STATE_STOPPED) {
mPlayer.play();
} else if (mPlaybackState == PlaybackState.STATE_PLAYING) {
} else if (state == PlaybackState.STATE_PLAYING) {
mPlayer.pause();
}
break;
case R.id.start_button:
Log.d(TAG, "Start button pressed, in state " + mPlaybackState);
Log.d(TAG, "Start button pressed, in state " + state);
mPlayer.setContent(mContentText.getText().toString());
break;
case R.id.route_button:
mPlayer.showRoutePicker();
break;
case R.id.art_picker:
Intent intent = new Intent(Intent.ACTION_OPEN_DOCUMENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/*");
startActivityForResult(intent, READ_REQUEST_CODE);
break;
}
}
};
private PlayerController.Listener mListener = new PlayerController.Listener() {
public MediaMetadata mMetadata;
@Override
public void onPlaybackStateChange(PlaybackState state) {
mPlaybackState = state.getState();
mPlaybackState = state;
boolean enablePlay = false;
boolean enableControls = true;
StringBuilder statusBuilder = new StringBuilder();
switch (mPlaybackState) {
switch (mPlaybackState.getState()) {
case PlaybackState.STATE_PLAYING:
statusBuilder.append("playing");
mPlayButton.setText("Pause");
@@ -172,7 +228,7 @@ public class OnePlayerActivity extends Activity {
@Override
public void onMetadataChange(MediaMetadata metadata) {
Log.d(TAG, "Metadata update! Title: " + metadata);
mMetadata = metadata;
}
};
}

View File

@@ -30,6 +30,7 @@ import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.graphics.Bitmap;
import android.util.Log;
import com.android.onemedia.playback.RequestUtils;
@@ -52,6 +53,7 @@ public class PlayerController {
private Handler mHandler = new Handler();
private boolean mResumed;
private Bitmap mArt;
public PlayerController(Activity context, Intent serviceIntent) {
mContext = context;
@@ -89,6 +91,16 @@ public class PlayerController {
unbindFromService();
}
public void setArt(Bitmap art) {
mArt = art;
if (mBinder != null) {
try {
mBinder.setIcon(art);
} catch (RemoteException e) {
}
}
}
public void play() {
if (mTransportControls != null) {
mTransportControls.play();
@@ -125,6 +137,16 @@ public class PlayerController {
// TODO
}
public MediaSession.Token getSessionToken() {
if (mBinder != null) {
try {
return mBinder.getSessionToken();
} catch (RemoteException e) {
}
}
return null;
}
private void unbindFromService() {
mContext.unbindService(mServiceConnection);
}
@@ -165,6 +187,9 @@ public class PlayerController {
mContext.setMediaController(mController);
mController.addCallback(mControllerCb, mHandler);
mTransportControls = mController.getTransportControls();
if (mArt != null) {
setArt(mArt);
}
Log.d(TAG, "Ready to use PlayerService");
if (mListener != null) {
@@ -194,6 +219,9 @@ public class PlayerController {
return;
}
Log.d(TAG, "Received metadata change, " + metadata.getDescription());
if (mListener != null) {
mListener.onMetadataChange(metadata);
}
}
}

View File

@@ -17,6 +17,7 @@ package com.android.onemedia;
import android.app.Service;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.session.MediaSession;
import android.media.session.PlaybackState;
import android.os.Bundle;
@@ -34,6 +35,7 @@ public class PlayerService extends Service {
private PlayerBinder mBinder;
private PlayerSession mSession;
private NotificationHelper mNotifyHelper;
private Intent mIntent;
private boolean mStarted = false;
@@ -47,6 +49,7 @@ public class PlayerService extends Service {
mSession = onCreatePlayerController();
mSession.createSession();
mSession.setListener(mPlayerListener);
mNotifyHelper = new NotificationHelper(this, mSession.mSession);
}
}
@@ -75,6 +78,7 @@ public class PlayerService extends Service {
if (!mStarted) {
Log.d(TAG, "Starting self");
startService(onCreateServiceIntent());
mNotifyHelper.onStart();
mStarted = true;
}
}
@@ -82,6 +86,7 @@ public class PlayerService extends Service {
public void onPlaybackEnded() {
if (mStarted) {
Log.d(TAG, "Stopping self");
mNotifyHelper.onStop();
stopSelf();
mStarted = false;
}
@@ -150,8 +155,17 @@ public class PlayerService extends Service {
@Override
public MediaSession.Token getSessionToken() throws RemoteException {
if (mSession == null) {
Log.e(TAG, "Error in PlayerService: mSession=null in getSessionToken()");
return null;
}
return mSession.getSessionToken();
}
@Override
public void setIcon(Bitmap icon) {
mSession.setIcon(icon);
}
}
}

View File

@@ -17,6 +17,8 @@ package com.android.onemedia;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.media.MediaMetadata;
import android.media.routing.MediaRouteSelector;
import android.media.routing.MediaRouter;
import android.media.routing.MediaRouter.ConnectionRequest;
@@ -50,6 +52,7 @@ public class PlayerSession {
protected Renderer mRenderer;
protected MediaSession.Callback mCallback;
protected Renderer.Listener mRenderListener;
protected MediaMetadata.Builder mMetadataBuilder;
protected PlaybackState mPlaybackState;
protected Listener mListener;
@@ -66,6 +69,8 @@ public class PlayerSession {
mPlaybackState = psBob.build();
mRenderer.registerListener(mRenderListener);
initMetadata();
}
public void createSession() {
@@ -92,6 +97,7 @@ public class PlayerSession {
| MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
mSession.setMediaRouter(mRouter);
mSession.setActive(true);
updateMetadata();
}
public void onDestroy() {
@@ -130,6 +136,19 @@ public class PlayerSession {
mRenderer.setNextContent(request);
}
public void setIcon(Bitmap icon) {
mMetadataBuilder.putBitmap(MediaMetadata.METADATA_KEY_DISPLAY_ICON, icon);
updateMetadata();
}
private void updateMetadata() {
// This is a mild abuse of metadata and shouldn't be duplicated in real
// code
if (mSession != null && mSession.isActive()) {
mSession.setMetadata(mMetadataBuilder.build());
}
}
private void updateState(int newState) {
float rate = newState == PlaybackState.STATE_PLAYING ? 1 : 0;
long position = mRenderer == null ? -1 : mRenderer.getSeekPosition();
@@ -140,6 +159,14 @@ public class PlayerSession {
mSession.setPlaybackState(mPlaybackState);
}
private void initMetadata() {
mMetadataBuilder = new MediaMetadata.Builder();
mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_TITLE,
"OneMedia display title");
mMetadataBuilder.putString(MediaMetadata.METADATA_KEY_DISPLAY_SUBTITLE,
"OneMedia display subtitle");
}
public interface Listener {
public void onPlayStateChanged(PlaybackState state);
}

View File

@@ -26,6 +26,12 @@ import java.util.Map;
public class RequestUtils {
public static final String ACTION_SET_CONTENT = "set_content";
public static final String ACTION_SET_NEXT_CONTENT = "set_next_content";
public static final String ACTION_PAUSE = "com.android.onemedia.pause";
public static final String ACTION_PLAY = "com.android.onemedia.play";
public static final String ACTION_REW = "com.android.onemedia.rew";
public static final String ACTION_FFWD = "com.android.onemedia.ffwd";
public static final String ACTION_PREV = "com.android.onemedia.prev";
public static final String ACTION_NEXT = "com.android.onemedia.next";
public static final String EXTRA_KEY_SOURCE = "source";
public static final String EXTRA_KEY_METADATA = "metadata";