diff --git a/components/embedder_traits/lib.rs b/components/embedder_traits/lib.rs index 94ae2493b68..f65ed514f4d 100644 --- a/components/embedder_traits/lib.rs +++ b/components/embedder_traits/lib.rs @@ -224,9 +224,9 @@ pub struct MediaMetadata { pub enum MediaSessionPlaybackState { /// The browsing context does not specify whether it’s 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, } diff --git a/components/script/dom/htmlmediaelement.rs b/components/script/dom/htmlmediaelement.rs index 57a7332713f..3204871d459 100644 --- a/components/script/dom/htmlmediaelement.rs +++ b/components/script/dom/htmlmediaelement.rs @@ -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 ); diff --git a/ports/libsimpleservo/api/src/lib.rs b/ports/libsimpleservo/api/src/lib.rs index 894928bc2b8..3eb28992427 100644 --- a/ports/libsimpleservo/api/src/lib.rs +++ b/ports/libsimpleservo/api/src/lib.rs @@ -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, - album: Option, - ); + 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 diff --git a/ports/libsimpleservo/jniapi/src/lib.rs b/ports/libsimpleservo/jniapi/src/lib.rs index ff91408244a..44b6207d3e6 100644 --- a/ports/libsimpleservo/jniapi/src/lib.rs +++ b/ports/libsimpleservo/jniapi/src/lib.rs @@ -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, - _album: Option, - ) { + 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) { diff --git a/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java b/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java index 6a2117962dd..ddff2e57b1a 100644 --- a/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java +++ b/support/android/apk/servoapp/src/main/java/org/mozilla/servo/MainActivity.java @@ -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); + } + + } diff --git a/support/android/apk/servoapp/src/main/res/drawable/media_session_icon.png b/support/android/apk/servoapp/src/main/res/drawable/media_session_icon.png new file mode 100755 index 00000000000..aafd2bbad02 Binary files /dev/null and b/support/android/apk/servoapp/src/main/res/drawable/media_session_icon.png differ diff --git a/support/android/apk/servoapp/src/main/res/drawable/media_session_next.png b/support/android/apk/servoapp/src/main/res/drawable/media_session_next.png new file mode 100755 index 00000000000..b4208472c5d Binary files /dev/null and b/support/android/apk/servoapp/src/main/res/drawable/media_session_next.png differ diff --git a/support/android/apk/servoapp/src/main/res/drawable/media_session_pause.png b/support/android/apk/servoapp/src/main/res/drawable/media_session_pause.png new file mode 100755 index 00000000000..4a33fe9b17a Binary files /dev/null and b/support/android/apk/servoapp/src/main/res/drawable/media_session_pause.png differ diff --git a/support/android/apk/servoapp/src/main/res/drawable/media_session_prev.png b/support/android/apk/servoapp/src/main/res/drawable/media_session_prev.png new file mode 100755 index 00000000000..8bd9feef195 Binary files /dev/null and b/support/android/apk/servoapp/src/main/res/drawable/media_session_prev.png differ diff --git a/support/android/apk/servoapp/src/main/res/drawable/media_session_stop.png b/support/android/apk/servoapp/src/main/res/drawable/media_session_stop.png new file mode 100755 index 00000000000..b9321def65d Binary files /dev/null and b/support/android/apk/servoapp/src/main/res/drawable/media_session_stop.png differ diff --git a/support/android/apk/servoapp/src/main/res/values/strings.xml b/support/android/apk/servoapp/src/main/res/values/strings.xml index ed1fb279213..4f3d30a0a84 100644 --- a/support/android/apk/servoapp/src/main/res/values/strings.xml +++ b/support/android/apk/servoapp/src/main/res/values/strings.xml @@ -1,3 +1,5 @@ - Servo + Servo + ServoMedia + Notication channel for multimedia activity