Set lock screen background in Android (like Spotify do) - android

I know that this topic has been already discussed here, here and here, and the answer seems to be that it is not possible.
But I recently installed Spotify in my Nexus 4 (4.4.2), and it seems to be possible. When I listen a song in Spotify the lock screen background change with the cover of the album that I'm listening (see screenshots).
My theory was:
when the phone is locked they change the phone wallpaper with the album cover in order to change also the lock screen background, then they set back the previous one when the phone is unlocked. But this is not how they do it, because in the permissions list of Spotify there is no "android.permission.SET_WALLPAPER"... :(
How do they do it? Some theory?

Edit:
The solution below only works for applications that have registered itself as a media controller, so apps that don't play audio can't/shouldn't use this mechanism to change the lockscreen wallpaper.
It can be done using RemoteControlClient, part of Android since ICS. If you want a working example, download VLC for Android and check out org.videolan.vlc.AudioService:
This part of the code is to intercept media controls.
/**
* Set up the remote control and tell the system we want to be the default receiver for the MEDIA buttons
* #see http://android-developers.blogspot.fr/2010/06/allowing-applications-to-play-nicer.html
*/
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void setUpRemoteControlClient() {
Context context = VLCApplication.getAppContext();
AudioManager audioManager = (AudioManager)context.getSystemService(AUDIO_SERVICE);
if(Util.isICSOrLater()) {
audioManager.registerMediaButtonEventReceiver(mRemoteControlClientReceiverComponent);
if (mRemoteControlClient == null) {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(mRemoteControlClientReceiverComponent);
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(context, 0, mediaButtonIntent, 0);
// create and register the remote control client
mRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
audioManager.registerRemoteControlClient(mRemoteControlClient);
}
mRemoteControlClient.setTransportControlFlags(
RemoteControlClient.FLAG_KEY_MEDIA_PLAY |
RemoteControlClient.FLAG_KEY_MEDIA_PAUSE |
RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS |
RemoteControlClient.FLAG_KEY_MEDIA_NEXT |
RemoteControlClient.FLAG_KEY_MEDIA_STOP);
} else if (Util.isFroyoOrLater()) {
audioManager.registerMediaButtonEventReceiver(mRemoteControlClientReceiverComponent);
}
}
This part is to update artwork, among other info:
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void updateRemoteControlClientMetadata() {
if(!Util.isICSOrLater()) // NOP check
return;
if (mRemoteControlClient != null) {
MetadataEditor editor = mRemoteControlClient.editMetadata(true);
editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getCurrentMedia().getAlbum());
editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getCurrentMedia().getArtist());
editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE, getCurrentMedia().getGenre());
editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getCurrentMedia().getTitle());
editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, getCurrentMedia().getLength());
editor.putBitmap(MetadataEditor.BITMAP_KEY_ARTWORK, getCover());
editor.apply();
}
}

For me, the most instructive example was Random Music Player, mentioned in documentation about Android 4.0 APIs:
"For a sample implementation, see the Random Music Player, which provides compatibility logic such that it enables the remote control client on Android 4.0 devices while continuing to support devices back to Android 2.1."
In addition, I converted text to bitmap to have text as album art.

Well, after trying some ways, I have a simple code here;
Try using this method;
private void updateMetaData() {
mediaSession =new MediaSessionCompat(context,"BXPlayer");
Bitmap cover = BitmapFactory.decodeResource(context.getResources(),
R.drawable.cover2);
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, cover)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, mSelectedSong.getArtist())
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, mSelectedSong.getAlbum())
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, mSelectedSong.getTitle())
.build());
}
then in your notification you need to set style to android.support.v4.media.app.NotificationCompat.MediaStyle() and set the media session token to use the current metadata.
Check this snippet below;
builder.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setShowActionsInCompactView(0, 1, 2)
.setMediaSession(mediaSession.getSessionToken()));
return builder.build();
To work, you must include implementation "com.android.support:support-v4:$latest_version" in your app build.gradle
And boom! you are good to go.

So here is the new "official docs"
At the bottom it describes the lock screen details
https://developer.android.com/guide/topics/media-apps/working-with-a-media-session.html#maintain-state
As an alternative, once I understood all the terms and jargon, this tutorial helped me outline the general structure for the MediaSessionCompat services.
https://code.tutsplus.com/tutorials/background-audio-in-android-with-mediasessioncompat--cms-27030
Finally, there is an API for lock screen wallpaper in Nougat and greater.
Why this is not support lib is beyond me at this time.

as explained here the key is to to pass a MediaMetadata object to your MediaSession. If these terms seems alien to you it's best to start the linked tutorial from the top.
I found the .putBitmap(MediaMetadata.METADATA_KEY_ART, bitmap) line to be the one that is taken to load the image to the lockscreen background. But be sure to populate .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap)as well.

I know this is late but perfect answer is still required. So to set lock screen background in Android (like Spotify do) we have to perform following steps.
1. set media session active
mSession.setActive(true). if session is not active so it is not gona show.
2. set playback state
playBackStateBuilder = new PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
| PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
| PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_PLAY_PAUSE);`
`mSession.setPlaybackState(playBackStateBuilder.setState(PlaybakStateCompate.STATE_PLAYING, 0, 0).build());
Note: lock screen image is showed when first playback state is set to playing then it can be switch to other states
3. set meta data
mSession.setMetadata(new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
.build());
here KEY_ALBUM_ART is required because this is the image which is shown on lock screen.
By setting above three things it had showed on my galaxy device but not on pixels devices so for that follow last point.
4. show notification with media style
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID);
builder.setStyle(
new androidx.media.app.NotificationCompat.MediaStyle()
);
mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(121, builder.build());

Related

isActivityStartAllowedOnDisplay() method

I'm trying to launch a second activity on a secondary display. This works fine on the emulators, but I need the secondary display to handle touch events.
With Android Pie, launching an activity on some second displays would throw a security exception. Google recently pushed out this new API for Android Q - isActivityStartAllowedOnDisplay() (https://developer.android.com/reference/android/app/ActivityManager.html#isActivityStartAllowedOnDisplay(android.content.Context,%2520int,%2520android.content.Intent)) - to be able to tell if the second display has this security exception or not.
This new API is helpful, BUT, is there any way around it? Maybe I've misunderstood the documentation, but it seems like if the device doesn't support it, then there's no way around it. Does anyone know of any displays that will NOT throw this security exception?
In order to get touch events to register on the secondary display (GeChic Touch Monitor), I had a DisplayLink device connected between the Android device and touch display. At this point, it was mirroring the view on the phone/tablet but would handle touch events. So, I wrote an app that would attempt to launch a second activity on the second display using this code on Android Pie OS:
DisplayManager mgr = (DisplayManager) this.getBaseContext().getSystemService(Context.DISPLAY_SERVICE);
if (mgr != null) {
Display[] displays = mgr.getDisplays();
for (int i = 0; i < displays.length; i++) {
Display display = displays[i];
Point point = new Point();
display.getSize(point);
if (point.y == PX_HEIGHT_OF_SECONDARY_DISPLAY || point.x == PX_HEIGHT_OF_SECONDARY_DISPLAY) {
Context displayContext = createDisplayContext(display);
Intent newIntent = new Intent(displayContext, ActivityCID.class);
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(display.getDisplayId());
newIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent, options.toBundle());
return;
}
}
}
Note that I did not use display.getDisplayId() and did a hacky way with the point.y and point.x values with a pixel width or height that did not match the pixel width or height of the Android phone/tablet. The displayId() was not always a consistent value which "should" be stable in Android Q. This is where the app would crash and the second activity would fail with a security permissions error. So, I used Android Q Beta to test the new isActivityStartAllowedOnDisplay() API. I ran this through Android Studio onto the phone (which was on Android Q Beta OS) to run it and to no surprise, the secondary display came back false. See code below:
public void launchOnSecondaryDisplay(Display display) {
Context displayContext = createDisplayContext(display);
Intent newIntent = new Intent(displayContext, ActivityTest.class);
ActivityManager activityManager = (ActivityManager) getApplicationContext().getSystemService(Activity.ACTIVITY_SERVICE);
if (activityManager != null) {
boolean allowsDisplay = activityManager.isActivityStartAllowedOnDisplay(displayContext, display.getDisplayId(), newIntent);
if (allowsDisplay) {
ActivityOptions options = ActivityOptions.makeBasic();
options.setLaunchDisplayId(display.getDisplayId());
newIntent.addFlags(Intent.FLAG_ACTIVITY_MULTIPLE_TASK | Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(newIntent, options.toBundle());
} else {
Toast.makeText(this, "Cannot start activity on that display as it is not supported.", Toast.LENGTH_LONG).show();
}
}
}
I decided to try this through the command line. After networking the physical device to match my Mac's connected network, I was able to connect to the phone wirelessly and was able to make changes in adb. Using an adb command, I was able to get a secondary activity on the secondary display! It seemed to be working! But no, it was not... Touch events still continued to act like the device was being mirrored so this was still a problem and was not going to work.
I discussed this with the Googler as well and was explained that adb root can override these permissions. However, there was still no way to get the touch events to map to the second activity on the secondary display.
At the moment of writing this, the only supported way to test multi touch displays is to use a physical device running Android Q Beta and follow these steps:
enable developer options,
in developer options, enable these 4 options: Force All Activities to be Resizeable, Freeform Windows, Force Desktop, and Simulate Secondary Display (doesn't matter which option picked for simulate secondary display),
reboot the device,
connect a mouse to the device. The mouse will show up and be stuck inside the overlaying window that is "simulating the secondary display". This will handle touch events.
In the future, there will be emulators that have multiple displays to better test multi display applications but this is not available at the moment.

Android O+: Some phones seem to lack the option to change sound type for notifications

Again about notification sound on Android O+.
There are some phones where the "notification settings" window doesn't show the sound selection button (and not even the vibration button).
Here are a couple examples:
Samsung A5
Huawei Honor View 10
(not minor brands... I would say)
They were tested with Gmail app (menu -> Settings -> account -> Notification settings) on Android 8.
Here Android O - Notification Channels - Change Vibration Pattern or Sound Type is a solution to avoid the "standard" window, but why should we reinvent the wheel?
Is there any other option that I'm missing?
Thank you,
Max
P.S.
Here is a screenshot from a Honor 9 / Android 8.0.0.
Channel name is "Mail" ("Posta" in Italian). For sound ("Suoneria" in Italian) there is only an On/Off switch.
It's a mess. You need to add workarounds for the different brands/devices. This is the flow we're using to deal with it:
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && !isDeviceWithWorkaround()) {
// Send to notification channel settings (See https://developer.android.com/training/notify-user/channels#UpdateChannel)
}else{
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TYPE, RingtoneManager.TYPE_NOTIFICATION);
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Sound");
intent.putExtra(RingtoneManager.EXTRA_RINGTONE_EXISTING_URI, Uri.parse(someExistingRingTone));
if (isDeviceWithWorkaround()) {
intent.setPackage("com.android.providers.media");
}
try {
startActivityForResult(intent, reqCode);
} catch (ActivityNotFoundException e) {
if (isDeviceWithWorkaround()) {
Log.i(TAG, "Failed to find preferred package [" + intent.getPackage() + "]. Trying again without package.");
intent.setPackage(null);
startActivity(intent);
}
}
}
So what's happening is that if it's a device with a known issue as you talk about we send them to the good old ringtone picker.
I believe that the package com.android.providers.media doesn't have an activity to start on stock Android, but on Huawei it then opens the Media Store where we get back a ringtone URI that can be used as notification sound. (We don't want the user to end up in some other ringtone picker that might not work. We have always recommended our users to use https://play.google.com/store/apps/details?id=com.angryredplanet.android.rings_extended but it won't work with Huawei on Android 8).

Android "O" (Oreo, 8) and higher media buttons issue

The code for handling media buttons from headsets that I use in my Text-to-Speech app works great under Android API 22 through 25 (in older versions of Android they are handled by other, now depreciated means). However under Android 8 "Oreo", both public beta and final release, it does not work. Here is the relevant code:
When the service starts, I create MediaSessionCompact object:
mSession = new MediaSessionCompat(getApplicationContext(), "my.package.name._player_session");
mSession.setFlags(MediaSessionCompat.FLAG_HANDLES_MEDIA_BUTTONS | MediaSessionCompat.FLAG_HANDLES_TRANSPORT_CONTROLS);
mSession.setActive(true);
mSession.setCallback(myMediaSessionCallback);
PlaybackStateCompat state = new PlaybackStateCompat.Builder()
.setActions(ACTION_PLAY_PAUSE | ACTION_PLAY | ACTION_PAUSE |
ACTION_SKIP_TO_NEXT | ACTION_SKIP_TO_PREVIOUS |
ACTION_FAST_FORWARD | ACTION_REWIND
)
.setState(PlaybackStateCompat.STATE_PAUSED, 0 /*PlaybackStateCompat.PLAYBACK_POSITION_UNKNOWN*/, 1f)
.build();
mSession.setPlaybackState(state);
There is of course session media callback defined:
private MediaSessionCompat.Callback myMediaSessionCallback = new MediaSessionCompat.Callback() {
#Override
public boolean onMediaButtonEvent(Intent mediaButtonIntent) {
// The log output below never appears on "Oreo", nothing comes here.
Log.d(TAG, "callback onMediaButtonEvent() Compat");
MediaButtonIntentReceiver.handleIntent(mediaButtonIntent.getAction(), (KeyEvent) mediaButtonIntent.getParcelableExtra(Intent.EXTRA_KEY_EVENT));
return true;
}
#Override
public void onSkipToNext() {
//...
}
// etc. other overrides
};
I also experimented with PendingIntent, using MediaButtonReceiver.buildMediaButtonPendingIntent() and set mSession.setMediaButtonReceiver(pendingIntent) for all the actions I'm interested in, then in my service onStartCommand() I call MediaButtonReceiver.handleIntent(mSession, intent):
// still in the same service:
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PLAY));
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PAUSE));
mSession.setMediaButtonReceiver(
MediaButtonReceiver.buildMediaButtonPendingIntent(
this,
mMediaButtonReceiverComponentName,
ACTION_PLAY_PAUSE));
and in the service onStartCommand():
#Override
public int onStartCommand(Intent intent, int flags, int startId) {
// ...
if (intent != null) {
MediaButtonReceiver.handleIntent(mSession, intent);
// ...
}
return START_NOT_STICKY;
}
Nothing, it's completely dumb to media buttons press events. What's wrong with "O" or my code there??? I'm completely baffled.
Update 8/32/2017
I also created a trivial but working app project that demonstrates the problem, please see: https://github.com/gregko/PlayerServiceSample. This project displays LogCat output when a media button is pressed on a headset under Android 5.x to 7.x, but fails completely under Android 8 "Oreo".
Update 9/1/2017
There is now an open issue on Android Issue Tracker about this, which I submitted, at https://issuetracker.google.com/issues/65175978. Still the media buttons work in several music player apps I tested on Oreo, I just can't figure out what do they do differently to make them work... The context of my app is not playing music, but reading aloud text with Text to Speech service, so a lot of code from Music Player examples does not apply.
Solved. On "Android 8.0 Behavior Changes" Google page we find this text:
In Android 8.0 (API level 26) the handling of media button events is different:
The handling of media buttons in a UI activity has not changed: foreground activities still get priority in handling media button events.
If the foreground activity does not handle the media button event, the system routes the event to the app that most recently played audio locally. The active status, flags, and playback state of a media session are not considered when determining which app receives media button events.
If the app's media session has been released, the system sends the media button event to the app's MediaButtonReceiver if it has one.
For every other case, the system discards the media button event.
All I had to do to make my trivial sample work was to play some sound with MediaPlayer. Apparently playing sound with Text-to-Speech API does not qualify, which in my opinion is a bug.
Here is the code I added to my trivial sample to make it work, playing a very brief and silent WAV file from Raw resources directory:
final MediaPlayer mMediaPlayer;
mMediaPlayer = MediaPlayer.create(this, R.raw.silent_sound);
mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
#Override
public void onCompletion(MediaPlayer mediaPlayer) {
mMediaPlayer.release();
}
});
mMediaPlayer.start();
Update
Submitted the bug report to Android issue tracker at https://issuetracker.google.com/issues/65344811
Update 2, Oct. 10, 2017
Google now says that Oreo behavior in this respect is "by design" and won't fix it. Read the reply near the end of the issue tracker post above. I must say I'm disappointed.

Setting sound configuration for Socket device

Using the following code to attempt setting the sound on/off for a Socket 8ci...not quite working for me. Can you suggest a proper command? As you can see in the code I set the Sound frequency based on a preference boolean. Thanks!
DeviceInfo device = (DeviceInfo) _scanApiHelper.getDevicesList().lastElement();
short[] soundConfig = new short[3];
// default the sound to On
if(getBRSharedPreferenceBoolean(PreferencesActivity.PREF_SOCKET_SCANNER_BEEP, true)) {
soundConfig[0] = ISktScanProperty.values.soundFrequency.kSktScanSoundFrequencyHigh;
} else {
soundConfig[0] = ISktScanProperty.values.soundFrequency.kSktScanSoundFrequencyNone;
}
soundConfig[1] = 200;
soundConfig[2] = 100;
// set the scanner sound config
_scanApiHelper.postSetSoundConfigDevice(
device,
ISktScanProperty.values.soundActionType.kSktScanSoundActionTypeGoodScan,
soundConfig,
_onSetScanApiConfiguration);
The Problem
Sound Config Device
The sound config allows you to set 4 different "actions": kSktScanSoundActionTypeGoodScan, kSktScanSoundActionTypeGoodScanLocal, kSktScanSoundActionTypeBadScan, kSktScanSoundActionTypeBadScanLocal. The difference between GoodScan and BadScan is self-explanatory, but the difference between GoodScan and GoodScanLocal isn't very clear.
GoodScanLocal, by default, is the sound emitted when a barcode is scanned
GoodScan is only emitted when the host (e.g. Android, iOS, Windows) sends the scanner a GoodScan or BadScan notification (via kSktScanPropIdDataConfirmationDevice)
Note: If you are using GoodScan/BadScan to verify decoded data, you probably want to change the confirmation mode (see kSktScanPropIdDataConfirmationMode in the docs). Otherwise the scanner will beep/flash/vibrate twice per scan
The code snippet your snippet is based on uses the latter to demonstrate that the tone is both configurable and can be triggered by the host.
You select a tone, hit the confirm button and the scanner emits that tone. It's not clear at first glance, but if you change the tone using the dropdown in SingleEntry and hit confirm, the three tones are very distinct. However, if you change the tone using that same dropdown, the tone you hear when you scan a barcode should not change.
The Solution
The best and simplest way to achieve what you are trying to achieve is to set the Local Decode Action with the beep disabled
Local Decode Action
// import static com.socketmobile.scanapi.ISktScanProperty.values.localDecodeAction.*;
DeviceInfo device = (DeviceInfo) _scanApiHelper.getDevicesList().lastElement();
int decodeAction = kSktScanLocalDecodeActionFlash | kSktScanLocalDecodeActionRumble;
if(getBRSharedPreferenceBoolean(PreferencesActivity.PREF_SOCKET_SCANNER_BEEP, true)) {
decodeAction |= kSktScanLocalDecodeActionBeep;
}
_scanApiHelper.postSetDecodeAction(device, decodeAction)
For completeness sake, to achieve a similar result using the code you posted, you'd only need to change kSktScanSoundActionTypeGoodScan to kSktScanSoundActionTypeGoodScanLocal. Although I would not recommend it.

Changing lock screen background programmatically [duplicate]

I know that this topic has been already discussed here, here and here, and the answer seems to be that it is not possible.
But I recently installed Spotify in my Nexus 4 (4.4.2), and it seems to be possible. When I listen a song in Spotify the lock screen background change with the cover of the album that I'm listening (see screenshots).
My theory was:
when the phone is locked they change the phone wallpaper with the album cover in order to change also the lock screen background, then they set back the previous one when the phone is unlocked. But this is not how they do it, because in the permissions list of Spotify there is no "android.permission.SET_WALLPAPER"... :(
How do they do it? Some theory?
Edit:
The solution below only works for applications that have registered itself as a media controller, so apps that don't play audio can't/shouldn't use this mechanism to change the lockscreen wallpaper.
It can be done using RemoteControlClient, part of Android since ICS. If you want a working example, download VLC for Android and check out org.videolan.vlc.AudioService:
This part of the code is to intercept media controls.
/**
* Set up the remote control and tell the system we want to be the default receiver for the MEDIA buttons
* #see http://android-developers.blogspot.fr/2010/06/allowing-applications-to-play-nicer.html
*/
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
public void setUpRemoteControlClient() {
Context context = VLCApplication.getAppContext();
AudioManager audioManager = (AudioManager)context.getSystemService(AUDIO_SERVICE);
if(Util.isICSOrLater()) {
audioManager.registerMediaButtonEventReceiver(mRemoteControlClientReceiverComponent);
if (mRemoteControlClient == null) {
Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
mediaButtonIntent.setComponent(mRemoteControlClientReceiverComponent);
PendingIntent mediaPendingIntent = PendingIntent.getBroadcast(context, 0, mediaButtonIntent, 0);
// create and register the remote control client
mRemoteControlClient = new RemoteControlClient(mediaPendingIntent);
audioManager.registerRemoteControlClient(mRemoteControlClient);
}
mRemoteControlClient.setTransportControlFlags(
RemoteControlClient.FLAG_KEY_MEDIA_PLAY |
RemoteControlClient.FLAG_KEY_MEDIA_PAUSE |
RemoteControlClient.FLAG_KEY_MEDIA_PREVIOUS |
RemoteControlClient.FLAG_KEY_MEDIA_NEXT |
RemoteControlClient.FLAG_KEY_MEDIA_STOP);
} else if (Util.isFroyoOrLater()) {
audioManager.registerMediaButtonEventReceiver(mRemoteControlClientReceiverComponent);
}
}
This part is to update artwork, among other info:
#TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
private void updateRemoteControlClientMetadata() {
if(!Util.isICSOrLater()) // NOP check
return;
if (mRemoteControlClient != null) {
MetadataEditor editor = mRemoteControlClient.editMetadata(true);
editor.putString(MediaMetadataRetriever.METADATA_KEY_ALBUM, getCurrentMedia().getAlbum());
editor.putString(MediaMetadataRetriever.METADATA_KEY_ARTIST, getCurrentMedia().getArtist());
editor.putString(MediaMetadataRetriever.METADATA_KEY_GENRE, getCurrentMedia().getGenre());
editor.putString(MediaMetadataRetriever.METADATA_KEY_TITLE, getCurrentMedia().getTitle());
editor.putLong(MediaMetadataRetriever.METADATA_KEY_DURATION, getCurrentMedia().getLength());
editor.putBitmap(MetadataEditor.BITMAP_KEY_ARTWORK, getCover());
editor.apply();
}
}
For me, the most instructive example was Random Music Player, mentioned in documentation about Android 4.0 APIs:
"For a sample implementation, see the Random Music Player, which provides compatibility logic such that it enables the remote control client on Android 4.0 devices while continuing to support devices back to Android 2.1."
In addition, I converted text to bitmap to have text as album art.
Well, after trying some ways, I have a simple code here;
Try using this method;
private void updateMetaData() {
mediaSession =new MediaSessionCompat(context,"BXPlayer");
Bitmap cover = BitmapFactory.decodeResource(context.getResources(),
R.drawable.cover2);
mediaSession.setMetadata(new MediaMetadataCompat.Builder()
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, cover)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, mSelectedSong.getArtist())
.putString(MediaMetadataCompat.METADATA_KEY_ALBUM, mSelectedSong.getAlbum())
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, mSelectedSong.getTitle())
.build());
}
then in your notification you need to set style to android.support.v4.media.app.NotificationCompat.MediaStyle() and set the media session token to use the current metadata.
Check this snippet below;
builder.setStyle(new android.support.v4.media.app.NotificationCompat.MediaStyle()
.setShowActionsInCompactView(0, 1, 2)
.setMediaSession(mediaSession.getSessionToken()));
return builder.build();
To work, you must include implementation "com.android.support:support-v4:$latest_version" in your app build.gradle
And boom! you are good to go.
So here is the new "official docs"
At the bottom it describes the lock screen details
https://developer.android.com/guide/topics/media-apps/working-with-a-media-session.html#maintain-state
As an alternative, once I understood all the terms and jargon, this tutorial helped me outline the general structure for the MediaSessionCompat services.
https://code.tutsplus.com/tutorials/background-audio-in-android-with-mediasessioncompat--cms-27030
Finally, there is an API for lock screen wallpaper in Nougat and greater.
Why this is not support lib is beyond me at this time.
as explained here the key is to to pass a MediaMetadata object to your MediaSession. If these terms seems alien to you it's best to start the linked tutorial from the top.
I found the .putBitmap(MediaMetadata.METADATA_KEY_ART, bitmap) line to be the one that is taken to load the image to the lockscreen background. But be sure to populate .putBitmap(MediaMetadata.METADATA_KEY_ALBUM_ART, bitmap)as well.
I know this is late but perfect answer is still required. So to set lock screen background in Android (like Spotify do) we have to perform following steps.
1. set media session active
mSession.setActive(true). if session is not active so it is not gona show.
2. set playback state
playBackStateBuilder = new PlaybackStateCompat.Builder()
.setActions(PlaybackStateCompat.ACTION_PLAY | PlaybackStateCompat.ACTION_SKIP_TO_NEXT
| PlaybackStateCompat.ACTION_PAUSE | PlaybackStateCompat.ACTION_SKIP_TO_PREVIOUS
| PlaybackStateCompat.ACTION_STOP | PlaybackStateCompat.ACTION_PLAY_PAUSE);`
`mSession.setPlaybackState(playBackStateBuilder.setState(PlaybakStateCompate.STATE_PLAYING, 0, 0).build());
Note: lock screen image is showed when first playback state is set to playing then it can be switch to other states
3. set meta data
mSession.setMetadata(new MediaMetadataCompat.Builder()
.putString(MediaMetadataCompat.METADATA_KEY_TITLE, title)
.putString(MediaMetadataCompat.METADATA_KEY_ARTIST, artist)
.putBitmap(MediaMetadataCompat.METADATA_KEY_ALBUM_ART, bitmap)
.build());
here KEY_ALBUM_ART is required because this is the image which is shown on lock screen.
By setting above three things it had showed on my galaxy device but not on pixels devices so for that follow last point.
4. show notification with media style
NotificationCompat.Builder builder = new NotificationCompat.Builder(mContext, CHANNEL_ID);
builder.setStyle(
new androidx.media.app.NotificationCompat.MediaStyle()
);
mNotificationManager = (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
mNotificationManager.notify(121, builder.build());

Categories

Resources