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:
@@ -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"
|
||||
|
||||
25
tests/OneMedia/res/drawable/ic_fast_forward.xml
Normal file
25
tests/OneMedia/res/drawable/ic_fast_forward.xml
Normal 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>
|
||||
24
tests/OneMedia/res/drawable/ic_fast_rewind.xml
Normal file
24
tests/OneMedia/res/drawable/ic_fast_rewind.xml
Normal 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>
|
||||
25
tests/OneMedia/res/drawable/ic_pause.xml
Normal file
25
tests/OneMedia/res/drawable/ic_pause.xml
Normal 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>
|
||||
25
tests/OneMedia/res/drawable/ic_play_arrow.xml
Normal file
25
tests/OneMedia/res/drawable/ic_play_arrow.xml
Normal 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>
|
||||
25
tests/OneMedia/res/drawable/ic_skip_next.xml
Normal file
25
tests/OneMedia/res/drawable/ic_skip_next.xml
Normal 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>
|
||||
28
tests/OneMedia/res/drawable/ic_skip_previous.xml
Normal file
28
tests/OneMedia/res/drawable/ic_skip_previous.xml
Normal 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>
|
||||
25
tests/OneMedia/res/drawable/ic_stop.xml
Normal file
25
tests/OneMedia/res/drawable/ic_stop.xml
Normal 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>
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
234
tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
Normal file
234
tests/OneMedia/src/com/android/onemedia/NotificationHelper.java
Normal 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);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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";
|
||||
|
||||
Reference in New Issue
Block a user