summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDaniel Sandler <dsandler@android.com>2014-06-05 02:54:13 -0400
committerRoboErik <epastern@google.com>2014-08-14 16:18:15 -0700
commit45f7ee8201efbda59b57b1fe637a1b9ffef25bb6 (patch)
tree3cae0465b42aeca12ed8ae7864592c3d40cbe01b
parent1d2a1c917f46b6854e91f9867a20abb76ecb794d (diff)
downloadframeworks_base-45f7ee8201efbda59b57b1fe637a1b9ffef25bb6.zip
frameworks_base-45f7ee8201efbda59b57b1fe637a1b9ffef25bb6.tar.gz
frameworks_base-45f7ee8201efbda59b57b1fe637a1b9ffef25bb6.tar.bz2
Add metadata and notifications to OneMedia
This wires up a notification and some basic metadata for testing in OneMedia. Change-Id: I0f2e922536c85caa63f66dae7deb55ffe94fe231
-rw-r--r--tests/OneMedia/AndroidManifest.xml2
-rw-r--r--tests/OneMedia/res/drawable/ic_fast_forward.xml25
-rw-r--r--tests/OneMedia/res/drawable/ic_fast_rewind.xml24
-rw-r--r--tests/OneMedia/res/drawable/ic_pause.xml25
-rw-r--r--tests/OneMedia/res/drawable/ic_play_arrow.xml25
-rw-r--r--tests/OneMedia/res/drawable/ic_skip_next.xml25
-rw-r--r--tests/OneMedia/res/drawable/ic_skip_previous.xml28
-rw-r--r--tests/OneMedia/res/drawable/ic_stop.xml25
-rw-r--r--tests/OneMedia/res/layout/activity_one_player.xml13
-rw-r--r--tests/OneMedia/res/values/strings.xml2
-rw-r--r--tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl2
-rw-r--r--tests/OneMedia/src/com/android/onemedia/NotificationHelper.java234
-rw-r--r--tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java74
-rw-r--r--tests/OneMedia/src/com/android/onemedia/PlayerController.java28
-rw-r--r--tests/OneMedia/src/com/android/onemedia/PlayerService.java14
-rw-r--r--tests/OneMedia/src/com/android/onemedia/PlayerSession.java27
-rw-r--r--tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java6
17 files changed, 568 insertions, 11 deletions
diff --git a/tests/OneMedia/AndroidManifest.xml b/tests/OneMedia/AndroidManifest.xml
index 9d78ca5..95072a4 100644
--- a/tests/OneMedia/AndroidManifest.xml
+++ b/tests/OneMedia/AndroidManifest.xml
@@ -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"
diff --git a/tests/OneMedia/res/drawable/ic_fast_forward.xml b/tests/OneMedia/res/drawable/ic_fast_forward.xml
new file mode 100644
index 0000000..8daf07d
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_fast_forward.xml
@@ -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>
diff --git a/tests/OneMedia/res/drawable/ic_fast_rewind.xml b/tests/OneMedia/res/drawable/ic_fast_rewind.xml
new file mode 100644
index 0000000..4ed1f54
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_fast_rewind.xml
@@ -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>
diff --git a/tests/OneMedia/res/drawable/ic_pause.xml b/tests/OneMedia/res/drawable/ic_pause.xml
new file mode 100644
index 0000000..15d0756
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_pause.xml
@@ -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>
diff --git a/tests/OneMedia/res/drawable/ic_play_arrow.xml b/tests/OneMedia/res/drawable/ic_play_arrow.xml
new file mode 100644
index 0000000..49d766d
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_play_arrow.xml
@@ -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>
diff --git a/tests/OneMedia/res/drawable/ic_skip_next.xml b/tests/OneMedia/res/drawable/ic_skip_next.xml
new file mode 100644
index 0000000..8a6ceca
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_skip_next.xml
@@ -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>
diff --git a/tests/OneMedia/res/drawable/ic_skip_previous.xml b/tests/OneMedia/res/drawable/ic_skip_previous.xml
new file mode 100644
index 0000000..c5d07db
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_skip_previous.xml
@@ -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>
diff --git a/tests/OneMedia/res/drawable/ic_stop.xml b/tests/OneMedia/res/drawable/ic_stop.xml
new file mode 100644
index 0000000..6043fdb
--- /dev/null
+++ b/tests/OneMedia/res/drawable/ic_stop.xml
@@ -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>
diff --git a/tests/OneMedia/res/layout/activity_one_player.xml b/tests/OneMedia/res/layout/activity_one_player.xml
index 516562f..ce2d641 100644
--- a/tests/OneMedia/res/layout/activity_one_player.xml
+++ b/tests/OneMedia/res/layout/activity_one_player.xml
@@ -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"
diff --git a/tests/OneMedia/res/values/strings.xml b/tests/OneMedia/res/values/strings.xml
index 3735c8d..86657fd 100644
--- a/tests/OneMedia/res/values/strings.xml
+++ b/tests/OneMedia/res/values/strings.xml
@@ -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>
diff --git a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
index d4df4c5..f53eac0 100644
--- a/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
+++ b/tests/OneMedia/src/com/android/onemedia/IPlayerService.aidl
@@ -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);
}
diff --git a/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
new file mode 100644
index 0000000..a5bcda5
--- /dev/null
+++ b/tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
@@ -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);
+ }
+
+}
diff --git a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
index 894377b..2ff3e20 100644
--- a/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
+++ b/tests/OneMedia/src/com/android/onemedia/OnePlayerActivity.java
@@ -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;
}
};
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerController.java b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
index c0799fc..c8d72ca 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerController.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerController.java
@@ -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);
+ }
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerService.java b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
index 58ee4a1..9967c99 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerService.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerService.java
@@ -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);
+ }
}
}
diff --git a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
index 890d68d..9afcf24 100644
--- a/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
+++ b/tests/OneMedia/src/com/android/onemedia/PlayerSession.java
@@ -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);
}
diff --git a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
index 3778c5f..1688395 100644
--- a/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
+++ b/tests/OneMedia/src/com/android/onemedia/playback/RequestUtils.java
@@ -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";