MediaSession show media controls on Android

This commit is contained in:
Fernando Jiménez Moreno 2019-10-18 12:02:47 +02:00
parent 761f21fc8b
commit dd63ba425f
11 changed files with 183 additions and 26 deletions

View file

@ -224,9 +224,9 @@ pub struct MediaMetadata {
pub enum MediaSessionPlaybackState {
/// The browsing context does not specify whether its playing or paused.
None_ = 1,
/// The browsing context has paused media and it can be resumed.
Playing,
/// The browsing context is currently playing media and it can be paused.
Playing,
/// The browsing context has paused media and it can be resumed.
Paused,
}

View file

@ -1804,9 +1804,15 @@ impl HTMLMediaElement {
PlaybackState::Playing => {
media_session_playback_state = MediaSessionPlaybackState::Playing;
},
PlaybackState::Buffering => {
// Do not send the media session playback state change event
// in this case as a None_ state is expected to clean up the
// session.
return;
},
_ => {},
};
println!(
debug!(
"Sending media session event playback state changed to {:?}",
media_session_playback_state
);

View file

@ -131,12 +131,7 @@ pub trait HostTrait {
/// Sets system clipboard contents.
fn set_clipboard_contents(&self, contents: String);
/// Called when we get the media session metadata/
fn on_media_session_metadata(
&self,
title: String,
artist: Option<String>,
album: Option<String>,
);
fn on_media_session_metadata(&self, title: String, artist: String, album: String);
/// Called when the media sessoin playback state changes.
fn on_media_session_playback_state_change(&self, state: i32);
}
@ -595,8 +590,8 @@ impl ServoGlue {
MediaSessionEvent::SetMetadata(metadata) => {
self.callbacks.host_callbacks.on_media_session_metadata(
metadata.title,
metadata.artist,
metadata.album,
metadata.artist.unwrap_or("".to_owned()),
metadata.album.unwrap_or("".to_owned()),
)
},
MediaSessionEvent::PlaybackStateChange(state) => self

View file

@ -509,15 +509,47 @@ impl HostTrait for HostCallbacks {
fn set_clipboard_contents(&self, _contents: String) {}
fn on_media_session_metadata(
&self,
_title: String,
_artist: Option<String>,
_album: Option<String>,
) {
fn on_media_session_metadata(&self, title: String, artist: String, album: String) {
info!("on_media_session_metadata");
let env = self.jvm.get_env().unwrap();
let title = match new_string(&env, &title) {
Ok(s) => s,
Err(_) => return,
};
let title = JValue::Object(JObject::from(title));
let artist = match new_string(&env, &artist) {
Ok(s) => s,
Err(_) => return,
};
let artist = JValue::Object(JObject::from(artist));
let album = match new_string(&env, &album) {
Ok(s) => s,
Err(_) => return,
};
let album = JValue::Object(JObject::from(album));
env.call_method(
self.callbacks.as_obj(),
"onMediaSessionMetadata",
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V",
&[title, artist, album],
)
.unwrap();
}
fn on_media_session_playback_state_change(&self, state: i32) {}
fn on_media_session_playback_state_change(&self, state: i32) {
info!("on_media_session_playback_state_change {:?}", state);
let env = self.jvm.get_env().unwrap();
let state = JValue::Int(state as jint);
env.call_method(
self.callbacks.as_obj(),
"onMediaSessionPlaybackStateChange",
"(I)V",
&[state],
)
.unwrap();
}
}
fn initialize_android_glue(env: &JNIEnv, activity: JObject) {

View file

@ -7,9 +7,16 @@ package org.mozilla.servo;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.system.ErrnoException;
import android.system.Os;
@ -23,8 +30,6 @@ import android.widget.ProgressBar;
import android.widget.TextView;
import android.util.Log;
//import org.mozilla.servo.MediaSessionCallback;
import org.mozilla.servoview.ServoView;
import org.mozilla.servoview.Servo;
@ -32,7 +37,24 @@ import java.io.File;
public class MainActivity extends Activity implements Servo.Client {
private class NotificationID {
private int lastID = 0;
public int getNext() {
lastID++;
return lastID;
}
public int get() {
return lastID;
}
}
private static final String LOGTAG = "MainActivity";
private static final String MEDIA_CHANNEL_ID = "MediaNotificationChannel";
private static final String KEY_MEDIA_PAUSE = "org.mozilla.servoview.MainActivity.pause";
private static final String KEY_MEDIA_PREV = "org.mozilla.servoview.MainActivity.prev";
private static final String KEY_MEDIA_NEXT = "org.mozilla.servoview.MainActivity.next";
private static final String KEY_MEDIA_STOP = "org.mozilla.servoview.MainActivity.stop";
ServoView mServoView;
Button mBackButton;
@ -43,7 +65,8 @@ public class MainActivity extends Activity implements Servo.Client {
ProgressBar mProgressBar;
TextView mIdleText;
boolean mCanGoBack;
// MediaSession mSession;
NotificationID mNotificationID;
BroadcastReceiver mMediaSessionActionReceiver;
@Override
protected void onCreate(Bundle savedInstanceState) {
@ -86,6 +109,16 @@ public class MainActivity extends Activity implements Servo.Client {
mServoView.loadUri(intent.getData());
}
setupUrlField();
mNotificationID = new NotificationID();
createMediaNotificationChannel();
}
@Override
protected void onDestroy() {
Log.d("SERVOMEDIA", "onDestroy");
super.onDestroy();
hideMediaSessionControls();
}
private void setupUrlField() {
@ -206,6 +239,7 @@ public class MainActivity extends Activity implements Servo.Client {
mServoView.onPause();
super.onPause();
}
@Override
public void onResume() {
mServoView.onResume();
@ -228,10 +262,98 @@ public class MainActivity extends Activity implements Servo.Client {
@Override
public void onMediaSessionPlaybackStateChange(int state) {
Log.d("SERVOMEDIA", "PLAYBACK STATE CHANGED");
Log.d("SERVOMEDIA", "PLAYBACK STATE CHANGED " + state);
if (state == 1 /* none */) {
hideMediaSessionControls();
return;
}
if (state == 2 /* playing */) {
showMediaSessionControls();
return;
}
}
/* private void createMediaSession() {
mSession = new MediaSession(this, "ServoMediaSession");
}*/
private void createMediaNotificationChannel() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
CharSequence name = getString(R.string.media_channel_name);
String description = getString(R.string.media_channel_description);
int importance = NotificationManager.IMPORTANCE_DEFAULT;
NotificationChannel channel = new NotificationChannel(MEDIA_CHANNEL_ID, name, importance);
channel.setDescription(description);
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.createNotificationChannel(channel);
}
}
private void showMediaSessionControls() {
Context context = getApplicationContext();
IntentFilter filter = new IntentFilter();
filter.addAction(KEY_MEDIA_PAUSE);
filter.addAction(KEY_MEDIA_STOP);
filter.addAction(KEY_MEDIA_PREV);
filter.addAction(KEY_MEDIA_NEXT);
mMediaSessionActionReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(KEY_MEDIA_PAUSE)) {
Log.d("SERVOMEDIA", "PAUSE");
} else if (intent.getAction().equals(KEY_MEDIA_STOP)) {
Log.d("SERVOMEDIA", "STOP");
} else if (intent.getAction().equals(KEY_MEDIA_NEXT)) {
Log.d("SERVOMEDIA", "NEXT");
} else if (intent.getAction().equals(KEY_MEDIA_PREV)) {
Log.d("SERVOMEDIA", "PREV");
}
}
};
registerReceiver(mMediaSessionActionReceiver, filter);
Intent pauseIntent = new Intent(KEY_MEDIA_PAUSE);
Notification.Action pauseAction =
new Notification.Action(R.drawable.media_session_pause, "Pause",
PendingIntent.getBroadcast(context, 0, pauseIntent, 0));
Intent nextIntent = new Intent(KEY_MEDIA_NEXT);
Notification.Action nextAction =
new Notification.Action(R.drawable.media_session_next, "Next",
PendingIntent.getBroadcast(context, 0, nextIntent, 0));
Intent prevIntent = new Intent(KEY_MEDIA_PREV);
Notification.Action prevAction =
new Notification.Action(R.drawable.media_session_prev, "Previous",
PendingIntent.getBroadcast(context, 0, prevIntent, 0));
Intent stopIntent = new Intent(KEY_MEDIA_STOP);
Notification.Action stopAction =
new Notification.Action(R.drawable.media_session_stop, "Stop",
PendingIntent.getBroadcast(context, 0, stopIntent, 0));
Notification.Builder builder = new Notification.Builder(context, this.MEDIA_CHANNEL_ID);
builder
.setSmallIcon(R.drawable.media_session_icon)
.setContentTitle("This is the notification title")
.setVisibility(Notification.VISIBILITY_PUBLIC)
.addAction(prevAction)
.addAction(pauseAction)
.addAction(nextAction)
.addAction(stopAction)
.setStyle(new Notification.MediaStyle()
.setShowActionsInCompactView(1 /* pause button */ )
.setShowActionsInCompactView(3 /* stop button */));
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.notify(mNotificationID.getNext(), builder.build());
}
private void hideMediaSessionControls() {
Log.d("SERVOMEDIA", "hideMediaSessionControls");
NotificationManager notificationManager = getSystemService(NotificationManager.class);
notificationManager.cancel(mNotificationID.get());
unregisterReceiver(mMediaSessionActionReceiver);
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 203 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 114 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 205 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 106 B

View file

@ -1,3 +1,5 @@
<resources>
<string name="app_name">Servo</string>
<string name="app_name">Servo</string>
<string name="media_channel_name">ServoMedia</string>
<string name="media_channel_description">Notication channel for multimedia activity</string>
</resources>