Use new MediaSession API on 5.x
This commit is contained in:
parent
0e425b9198
commit
81c6527e73
@ -337,6 +337,10 @@ public final class PlaybackService extends Service
|
||||
* The SensorManager service.
|
||||
*/
|
||||
private SensorManager mSensorManager;
|
||||
/**
|
||||
* A remote control client implementation
|
||||
*/
|
||||
private RemoteControl.Client mRemoteControlClient;
|
||||
|
||||
SongTimeline mTimeline;
|
||||
private Song mCurrentSong;
|
||||
@ -495,7 +499,8 @@ public final class PlaybackService extends Service
|
||||
|
||||
getContentResolver().registerContentObserver(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, true, mObserver);
|
||||
|
||||
RemoteControl.registerRemote(this, mAudioManager);
|
||||
mRemoteControlClient = new RemoteControl().getClient(this);
|
||||
mRemoteControlClient.registerRemote(mAudioManager);
|
||||
|
||||
mLooper = thread.getLooper();
|
||||
mHandler = new Handler(mLooper, this);
|
||||
@ -614,6 +619,9 @@ public final class PlaybackService extends Service
|
||||
if (mSensorManager != null)
|
||||
mSensorManager.unregisterListener(this);
|
||||
|
||||
if (mRemoteControlClient != null)
|
||||
mRemoteControlClient.unregisterRemote();
|
||||
|
||||
super.onDestroy();
|
||||
}
|
||||
|
||||
@ -833,7 +841,7 @@ public final class PlaybackService extends Service
|
||||
} else if (PrefKeys.MEDIA_BUTTON.equals(key) || PrefKeys.MEDIA_BUTTON_BEEP.equals(key)) {
|
||||
MediaButtonReceiver.reloadPreference(this);
|
||||
} else if (PrefKeys.COVER_ON_LOCKSCREEN.equals(key)) {
|
||||
RemoteControl.reloadPreference();
|
||||
mRemoteControlClient.reloadPreference();
|
||||
} else if (PrefKeys.USE_IDLE_TIMEOUT.equals(key) || PrefKeys.IDLE_TIMEOUT.equals(key)) {
|
||||
mIdleTimeout = settings.getBoolean(PrefKeys.USE_IDLE_TIMEOUT, PrefDefaults.USE_IDLE_TIMEOUT) ? settings.getInt(PrefKeys.IDLE_TIMEOUT, PrefDefaults.IDLE_TIMEOUT) : 0;
|
||||
userActionTriggered();
|
||||
@ -1055,7 +1063,7 @@ public final class PlaybackService extends Service
|
||||
if (mReadaheadEnabled)
|
||||
triggerReadAhead();
|
||||
|
||||
RemoteControl.updateRemote(this, mCurrentSong, mState, mForceNotificationVisible);
|
||||
mRemoteControlClient.updateRemote(mCurrentSong, mState, mForceNotificationVisible);
|
||||
|
||||
if (mStockBroadcast)
|
||||
stockMusicBroadcast();
|
||||
|
@ -1,129 +1,46 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
* Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.blinkenlights.android.vanilla;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.media.RemoteControlClient;
|
||||
import android.os.Build;
|
||||
|
||||
|
||||
public class RemoteControl {
|
||||
/**
|
||||
* Used with updateRemote method.
|
||||
*/
|
||||
private static RemoteControlClient sRemote;
|
||||
/**
|
||||
* Whether the cover should be shown. 1 for yes, 0 for no, -1 for
|
||||
* uninitialized.
|
||||
*/
|
||||
private static int sShowCover = -1;
|
||||
|
||||
/**
|
||||
* Perform initialization required for RemoteControlClient.
|
||||
*
|
||||
* @param context A context to use.
|
||||
* @param am The AudioManager service.
|
||||
* Returns a RemoteControl.Client implementation
|
||||
*/
|
||||
public static void registerRemote(Context context, AudioManager am)
|
||||
{
|
||||
if (!MediaButtonReceiver.useHeadsetControls(context)) {
|
||||
// RemoteControlClient requires MEDIA_BUTTON intent
|
||||
return;
|
||||
}
|
||||
|
||||
MediaButtonReceiver.registerMediaButton(context);
|
||||
|
||||
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
mediaButtonIntent.setComponent(new ComponentName(context.getPackageName(), MediaButtonReceiver.class.getName()));
|
||||
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(context, 0, mediaButtonIntent, 0);
|
||||
RemoteControlClient remote = new RemoteControlClient(mediaPendingIntent);
|
||||
int flags = RemoteControlClient.FLAG_KEY_MEDIA_NEXT
|
||||
| RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS
|
||||
| RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
|
||||
| RemoteControlClient.FLAG_KEY_MEDIA_PLAY
|
||||
| RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
|
||||
remote.setTransportControlFlags(flags);
|
||||
am.registerRemoteControlClient(remote);
|
||||
sRemote = remote;
|
||||
public RemoteControl.Client getClient(Context context) {
|
||||
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
|
||||
new RemoteControlImplLp(context) :
|
||||
new RemoteControlImplKitKat(context) // legacy implementation, kept until we drop 4.x support
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninitializes our cached preferences, forcing a reload
|
||||
* Interface definition of our RemoteControl API
|
||||
*/
|
||||
public static void reloadPreference()
|
||||
{
|
||||
sShowCover = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the remote with new metadata.
|
||||
* {@link #registerRemote(Context, AudioManager)} must have been called
|
||||
* first.
|
||||
*
|
||||
* @param context A context to use.
|
||||
* @param song The song containing the new metadata.
|
||||
* @param state PlaybackService state, used to determine playback state.
|
||||
* @param keepPaused whether or not to keep the remote updated in paused mode
|
||||
*/
|
||||
public static void updateRemote(Context context, Song song, int state, boolean keepPaused)
|
||||
{
|
||||
RemoteControlClient remote = sRemote;
|
||||
if (remote == null)
|
||||
return;
|
||||
|
||||
boolean isPlaying = ((state & PlaybackService.FLAG_PLAYING) != 0);
|
||||
|
||||
if (sShowCover == -1) {
|
||||
SharedPreferences settings = PlaybackService.getSettings(context);
|
||||
sShowCover = settings.getBoolean(PrefKeys.COVER_ON_LOCKSCREEN, PrefDefaults.COVER_ON_LOCKSCREEN) ? 1 : 0;
|
||||
}
|
||||
|
||||
remote.setPlaybackState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED);
|
||||
RemoteControlClient.MetadataEditor editor = remote.editMetadata(true);
|
||||
if (song != null) {
|
||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, song.artist);
|
||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, song.album);
|
||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, song.title);
|
||||
Bitmap bitmap = song.getCover(context);
|
||||
if (bitmap != null && sShowCover == 1 && (isPlaying || keepPaused)) {
|
||||
// Create a copy of the cover art, since RemoteControlClient likes
|
||||
// to recycle what we give it.
|
||||
bitmap = bitmap.copy(Bitmap.Config.RGB_565, false);
|
||||
} else {
|
||||
// Some lockscreen implementations fail to clear the cover artwork
|
||||
// if we send a null bitmap. We are creating a 16x16 transparent
|
||||
// bitmap to work around this limitation.
|
||||
bitmap = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, bitmap);
|
||||
}
|
||||
editor.apply();
|
||||
public interface Client {
|
||||
public void registerRemote(AudioManager am);
|
||||
public void unregisterRemote();
|
||||
public void reloadPreference();
|
||||
public void updateRemote(Song song, int state, boolean keepPaused);
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
* Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in
|
||||
* all copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
* THE SOFTWARE.
|
||||
*/
|
||||
|
||||
package ch.blinkenlights.android.vanilla;
|
||||
|
||||
import android.app.PendingIntent;
|
||||
import android.content.ComponentName;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaMetadataRetriever;
|
||||
import android.media.RemoteControlClient;
|
||||
|
||||
|
||||
public class RemoteControlImplKitKat implements RemoteControl.Client {
|
||||
/**
|
||||
* Context of this instance
|
||||
*/
|
||||
private Context mContext;
|
||||
/**
|
||||
* Used with updateRemote method.
|
||||
*/
|
||||
private RemoteControlClient mRemote;
|
||||
/**
|
||||
* Whether the cover should be shown. 1 for yes, 0 for no, -1 for
|
||||
* uninitialized.
|
||||
*/
|
||||
private int mShowCover = -1;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param context The context to use
|
||||
*/
|
||||
public RemoteControlImplKitKat(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform initialization required for RemoteControlClient.
|
||||
*
|
||||
* @param am The AudioManager service.
|
||||
*/
|
||||
public void registerRemote(AudioManager am)
|
||||
{
|
||||
if (!MediaButtonReceiver.useHeadsetControls(mContext)) {
|
||||
// RemoteControlClient requires MEDIA_BUTTON intent
|
||||
return;
|
||||
}
|
||||
|
||||
MediaButtonReceiver.registerMediaButton(mContext);
|
||||
|
||||
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
|
||||
mediaButtonIntent.setComponent(new ComponentName(mContext.getPackageName(), MediaButtonReceiver.class.getName()));
|
||||
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(mContext, 0, mediaButtonIntent, 0);
|
||||
RemoteControlClient remote = new RemoteControlClient(mediaPendingIntent);
|
||||
int flags = RemoteControlClient.FLAG_KEY_MEDIA_NEXT
|
||||
| RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS
|
||||
| RemoteControlClient.FLAG_KEY_MEDIA_PLAY_PAUSE
|
||||
| RemoteControlClient.FLAG_KEY_MEDIA_PLAY
|
||||
| RemoteControlClient.FLAG_KEY_MEDIA_PAUSE;
|
||||
remote.setTransportControlFlags(flags);
|
||||
am.registerRemoteControlClient(remote);
|
||||
mRemote = remote;
|
||||
}
|
||||
|
||||
public void unregisterRemote() {
|
||||
// we should probably call am.unregisterRemoteControlClient but we never did and i'm not touching the legacy implementation.
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninitializes our cached preferences, forcing a reload
|
||||
*/
|
||||
public void reloadPreference()
|
||||
{
|
||||
mShowCover = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the remote with new metadata.
|
||||
* {@link #registerRemote(Context, AudioManager)} must have been called
|
||||
* first.
|
||||
*
|
||||
* @param song The song containing the new metadata.
|
||||
* @param state PlaybackService state, used to determine playback state.
|
||||
* @param keepPaused whether or not to keep the remote updated in paused mode
|
||||
*/
|
||||
public void updateRemote(Song song, int state, boolean keepPaused)
|
||||
{
|
||||
RemoteControlClient remote = mRemote;
|
||||
if (remote == null)
|
||||
return;
|
||||
|
||||
boolean isPlaying = ((state & PlaybackService.FLAG_PLAYING) != 0);
|
||||
|
||||
if (mShowCover == -1) {
|
||||
SharedPreferences settings = PlaybackService.getSettings(mContext);
|
||||
mShowCover = settings.getBoolean(PrefKeys.COVER_ON_LOCKSCREEN, PrefDefaults.COVER_ON_LOCKSCREEN) ? 1 : 0;
|
||||
}
|
||||
|
||||
remote.setPlaybackState(isPlaying ? RemoteControlClient.PLAYSTATE_PLAYING : RemoteControlClient.PLAYSTATE_PAUSED);
|
||||
RemoteControlClient.MetadataEditor editor = remote.editMetadata(true);
|
||||
if (song != null) {
|
||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, song.artist);
|
||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, song.album);
|
||||
editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, song.title);
|
||||
Bitmap bitmap = song.getCover(mContext);
|
||||
if (bitmap != null && mShowCover == 1 && (isPlaying || keepPaused)) {
|
||||
// Create a copy of the cover art, since RemoteControlClient likes
|
||||
// to recycle what we give it.
|
||||
bitmap = bitmap.copy(Bitmap.Config.RGB_565, false);
|
||||
} else {
|
||||
// Some lockscreen implementations fail to clear the cover artwork
|
||||
// if we send a null bitmap. We are creating a 16x16 transparent
|
||||
// bitmap to work around this limitation.
|
||||
bitmap = Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888);
|
||||
}
|
||||
editor.putBitmap(RemoteControlClient.MetadataEditor.BITMAP_KEY_ARTWORK, bitmap);
|
||||
}
|
||||
editor.apply();
|
||||
}
|
||||
}
|
122
src/ch/blinkenlights/android/vanilla/RemoteControlImplLp.java
Normal file
122
src/ch/blinkenlights/android/vanilla/RemoteControlImplLp.java
Normal file
@ -0,0 +1,122 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Adrian Ulrich <adrian@blinkenlights.ch>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package ch.blinkenlights.android.vanilla;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.graphics.Bitmap;
|
||||
import android.media.AudioManager;
|
||||
import android.media.MediaMetadata;
|
||||
import android.media.session.MediaSession;
|
||||
import android.media.session.PlaybackState;
|
||||
|
||||
|
||||
|
||||
public class RemoteControlImplLp implements RemoteControl.Client {
|
||||
/**
|
||||
* Context of this instance
|
||||
*/
|
||||
private Context mContext;
|
||||
/**
|
||||
* Objects MediaSession handle
|
||||
*/
|
||||
private MediaSession mMediaSession;
|
||||
/**
|
||||
* Whether the cover should be shown. 1 for yes, 0 for no, -1 for
|
||||
* uninitialized.
|
||||
*/
|
||||
private int mShowCover = -1;
|
||||
|
||||
/**
|
||||
* Creates a new instance
|
||||
*
|
||||
* @param context The context to use
|
||||
*/
|
||||
public RemoteControlImplLp(Context context) {
|
||||
mContext = context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new MediaSession on the device
|
||||
*
|
||||
* @param am The AudioManager service. (unused)
|
||||
*/
|
||||
public void registerRemote(AudioManager am) {
|
||||
mMediaSession = new MediaSession(mContext, "VanillaMusic");
|
||||
mMediaSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
|
||||
mMediaSession.setActive(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregisters a registered media session
|
||||
*/
|
||||
public void unregisterRemote() {
|
||||
mMediaSession.setActive(false);
|
||||
mMediaSession.release();
|
||||
}
|
||||
|
||||
/**
|
||||
* Uninitializes our cached preferences, forcing a reload
|
||||
*/
|
||||
public void reloadPreference() {
|
||||
mShowCover = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the remote with new metadata.
|
||||
* {@link #registerRemote(AudioManager)} must have been called
|
||||
* first.
|
||||
*
|
||||
* @param song The song containing the new metadata.
|
||||
* @param state PlaybackService state, used to determine playback state.
|
||||
* @param keepPaused whether or not to keep the remote updated in paused mode
|
||||
*/
|
||||
public void updateRemote(Song song, int state, boolean keepPaused) {
|
||||
MediaSession session = mMediaSession;
|
||||
if (session == null)
|
||||
return;
|
||||
|
||||
boolean isPlaying = ((state & PlaybackService.FLAG_PLAYING) != 0);
|
||||
|
||||
if (mShowCover == -1) {
|
||||
SharedPreferences settings = PlaybackService.getSettings(mContext);
|
||||
mShowCover = settings.getBoolean(PrefKeys.COVER_ON_LOCKSCREEN, PrefDefaults.COVER_ON_LOCKSCREEN) ? 1 : 0;
|
||||
}
|
||||
|
||||
if (song != null) {
|
||||
Bitmap bitmap = null;
|
||||
if (mShowCover == 1 && (isPlaying || keepPaused)) {
|
||||
bitmap = song.getCover(mContext);
|
||||
if (bitmap != null)
|
||||
bitmap.copy(Bitmap.Config.ARGB_8888, false);
|
||||
}
|
||||
|
||||
session.setMetadata(new MediaMetadata.Builder()
|
||||
.putString(MediaMetadata.METADATA_KEY_ARTIST, song.artist)
|
||||
.putString(MediaMetadata.METADATA_KEY_ALBUM, song.album)
|
||||
.putString(MediaMetadata.METADATA_KEY_TITLE, song.title)
|
||||
.putLong(MediaMetadata.METADATA_KEY_DURATION, song.duration)
|
||||
.putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap)
|
||||
.build());
|
||||
}
|
||||
|
||||
int playbackState = (isPlaying ? PlaybackState.STATE_PLAYING : PlaybackState.STATE_PAUSED);
|
||||
session.setPlaybackState(new PlaybackState.Builder()
|
||||
.setState(playbackState, PlaybackState.PLAYBACK_POSITION_UNKNOWN , 1.0f).build());
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user