Media Session API: use MediaMetadata in the browser process.
This is adding a Java counterpart to the content::MediaMetadata added in Java with a proxy to pass the object from C++ to Java. The MediaMetadata information are only used to replace the title from the media notification and set some Android metadata. Other UI changes will require UI review. BUG=497735,581728 Review URL: https://codereview.chromium.org/1458703003 Cr-Commit-Position: refs/heads/master@{#381697}
This commit is contained in:
chrome/android
java
src
org
chromium
chrome
javatests
src
org
chromium
chrome
browser
content
browser
android
media
web_contents
common
content_common.gypicontent_jni.gypipublic
android
java
src
org
chromium
content
browser
webcontents
content_public
browser
@ -16,6 +16,7 @@ import org.chromium.chrome.browser.media.ui.MediaNotificationInfo;
|
||||
import org.chromium.chrome.browser.media.ui.MediaNotificationListener;
|
||||
import org.chromium.chrome.browser.media.ui.MediaNotificationManager;
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
import org.chromium.content_public.common.MediaMetadata;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@ -117,7 +118,7 @@ public class CastNotificationControl implements MediaRouteController.UiListener,
|
||||
}
|
||||
|
||||
private void updateNotification() {
|
||||
mNotificationBuilder.setTitle(mTitle);
|
||||
mNotificationBuilder.setMetadata(new MediaMetadata(mTitle, "", ""));
|
||||
if (mState == PlayerState.PAUSED || mState == PlayerState.PLAYING) {
|
||||
mNotificationBuilder.setPaused(mState != PlayerState.PLAYING);
|
||||
mNotificationBuilder.setActions(MediaNotificationInfo.ACTION_STOP
|
||||
@ -208,4 +209,4 @@ public class CastNotificationControl implements MediaRouteController.UiListener,
|
||||
boolean isShowingForTests() {
|
||||
return mIsShowing;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -22,6 +22,8 @@ import org.chromium.chrome.browser.media.ui.MediaNotificationInfo;
|
||||
import org.chromium.chrome.browser.media.ui.MediaNotificationListener;
|
||||
import org.chromium.chrome.browser.media.ui.MediaNotificationManager;
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
import org.chromium.content_public.common.MediaMetadata;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
@ -128,7 +130,7 @@ public class CastSessionImpl implements MediaNotificationListener, CastSession {
|
||||
}
|
||||
|
||||
mNotificationBuilder = new MediaNotificationInfo.Builder()
|
||||
.setTitle(mCastDevice.getFriendlyName())
|
||||
.setMetadata(new MediaMetadata(mCastDevice.getFriendlyName(), "", ""))
|
||||
.setPaused(false)
|
||||
.setOrigin(origin)
|
||||
// TODO(avayvod): the same session might have more than one tab id. Should we track
|
||||
|
@ -9,6 +9,7 @@ import android.graphics.Bitmap;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.chromium.chrome.browser.tab.Tab;
|
||||
import org.chromium.content_public.common.MediaMetadata;
|
||||
|
||||
/**
|
||||
* Exposes information about the current media notification to the external clients.
|
||||
@ -42,7 +43,7 @@ public class MediaNotificationInfo {
|
||||
*/
|
||||
public static final class Builder {
|
||||
|
||||
private String mTitle = "";
|
||||
private MediaMetadata mMetadata;
|
||||
private boolean mIsPaused = false;
|
||||
private String mOrigin = "";
|
||||
private int mTabId = Tab.INVALID_TAB_ID;
|
||||
@ -61,12 +62,12 @@ public class MediaNotificationInfo {
|
||||
}
|
||||
|
||||
public MediaNotificationInfo build() {
|
||||
assert mTitle != null;
|
||||
assert mMetadata != null;
|
||||
assert mOrigin != null;
|
||||
assert mListener != null;
|
||||
|
||||
return new MediaNotificationInfo(
|
||||
mTitle,
|
||||
mMetadata,
|
||||
mIsPaused,
|
||||
mOrigin,
|
||||
mTabId,
|
||||
@ -79,8 +80,8 @@ public class MediaNotificationInfo {
|
||||
mListener);
|
||||
}
|
||||
|
||||
public Builder setTitle(String title) {
|
||||
mTitle = title;
|
||||
public Builder setMetadata(MediaMetadata metadata) {
|
||||
mMetadata = metadata;
|
||||
return this;
|
||||
}
|
||||
|
||||
@ -141,9 +142,9 @@ public class MediaNotificationInfo {
|
||||
private final int mActions;
|
||||
|
||||
/**
|
||||
* The title of the media.
|
||||
* The metadata associated with the media.
|
||||
*/
|
||||
public final String title;
|
||||
public final MediaMetadata metadata;
|
||||
|
||||
/**
|
||||
* The current state of the media, paused or not.
|
||||
@ -213,7 +214,7 @@ public class MediaNotificationInfo {
|
||||
|
||||
/**
|
||||
* Create a new MediaNotificationInfo.
|
||||
* @param title The title of the media.
|
||||
* @param metadata The metadata associated with the media.
|
||||
* @param isPaused The current state of the media, paused or not.
|
||||
* @param origin The origin of the tab containing the media.
|
||||
* @param tabId The id of the tab containing the media.
|
||||
@ -223,7 +224,7 @@ public class MediaNotificationInfo {
|
||||
* @param listener The listener for the control events.
|
||||
*/
|
||||
private MediaNotificationInfo(
|
||||
String title,
|
||||
MediaMetadata metadata,
|
||||
boolean isPaused,
|
||||
String origin,
|
||||
int tabId,
|
||||
@ -234,7 +235,7 @@ public class MediaNotificationInfo {
|
||||
Bitmap image,
|
||||
Intent contentIntent,
|
||||
MediaNotificationListener listener) {
|
||||
this.title = title;
|
||||
this.metadata = metadata;
|
||||
this.isPaused = isPaused;
|
||||
this.origin = origin;
|
||||
this.tabId = tabId;
|
||||
@ -259,7 +260,7 @@ public class MediaNotificationInfo {
|
||||
&& icon == other.icon
|
||||
&& mActions == other.mActions
|
||||
&& id == other.id
|
||||
&& TextUtils.equals(title, other.title)
|
||||
&& metadata.equals(other.metadata)
|
||||
&& TextUtils.equals(origin, other.origin)
|
||||
&& image == other.image || (image != null && image.sameAs(other.image))
|
||||
&& contentIntent.equals(other.contentIntent)
|
||||
@ -270,7 +271,7 @@ public class MediaNotificationInfo {
|
||||
public int hashCode() {
|
||||
int result = isPaused ? 1 : 0;
|
||||
result = 31 * result + (isPrivate ? 1 : 0);
|
||||
result = 31 * result + (title == null ? 0 : title.hashCode());
|
||||
result = 31 * result + (metadata == null ? 0 : metadata.hashCode());
|
||||
result = 31 * result + (origin == null ? 0 : origin.hashCode());
|
||||
result = 31 * result + (image == null ? 0 : image.hashCode());
|
||||
result = 31 * result + (contentIntent == null ? 0 : contentIntent.hashCode());
|
||||
|
@ -24,6 +24,7 @@ import android.support.v4.media.MediaMetadataCompat;
|
||||
import android.support.v4.media.session.MediaSessionCompat;
|
||||
import android.support.v4.media.session.PlaybackStateCompat;
|
||||
import android.support.v7.media.MediaRouter;
|
||||
import android.text.TextUtils;
|
||||
import android.util.SparseArray;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.View;
|
||||
@ -502,7 +503,7 @@ public class MediaNotificationManager {
|
||||
playPauseButtonId = R.id.button2;
|
||||
}
|
||||
|
||||
contentView.setTextViewText(R.id.title, mMediaNotificationInfo.title);
|
||||
contentView.setTextViewText(R.id.title, mMediaNotificationInfo.metadata.getTitle());
|
||||
contentView.setTextViewText(R.id.status, mMediaNotificationInfo.origin);
|
||||
if (mNotificationIcon != null) {
|
||||
contentView.setImageViewBitmap(R.id.icon, mNotificationIcon);
|
||||
@ -541,7 +542,7 @@ public class MediaNotificationManager {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_TITLE,
|
||||
mMediaNotificationInfo.title);
|
||||
mMediaNotificationInfo.metadata.getTitle());
|
||||
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_DISPLAY_SUBTITLE,
|
||||
mMediaNotificationInfo.origin);
|
||||
metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_DISPLAY_ICON,
|
||||
@ -554,12 +555,21 @@ public class MediaNotificationManager {
|
||||
}
|
||||
} else {
|
||||
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_TITLE,
|
||||
mMediaNotificationInfo.title);
|
||||
mMediaNotificationInfo.metadata.getTitle());
|
||||
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST,
|
||||
mMediaNotificationInfo.origin);
|
||||
metadataBuilder.putBitmap(MediaMetadataCompat.METADATA_KEY_ART, mediaSessionImage);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getArtist())) {
|
||||
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ARTIST,
|
||||
mMediaNotificationInfo.metadata.getArtist());
|
||||
}
|
||||
if (!TextUtils.isEmpty(mMediaNotificationInfo.metadata.getAlbum())) {
|
||||
metadataBuilder.putString(MediaMetadataCompat.METADATA_KEY_ALBUM,
|
||||
mMediaNotificationInfo.metadata.getAlbum());
|
||||
}
|
||||
|
||||
return metadataBuilder.build();
|
||||
}
|
||||
|
||||
|
@ -6,6 +6,7 @@ package org.chromium.chrome.browser.media.ui;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.media.AudioManager;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.chromium.base.ApplicationStatus;
|
||||
import org.chromium.base.Log;
|
||||
@ -17,6 +18,7 @@ import org.chromium.chrome.browser.tab.TabObserver;
|
||||
import org.chromium.chrome.browser.util.UrlUtilities;
|
||||
import org.chromium.content_public.browser.WebContents;
|
||||
import org.chromium.content_public.browser.WebContentsObserver;
|
||||
import org.chromium.content_public.common.MediaMetadata;
|
||||
import org.chromium.ui.base.WindowAndroid;
|
||||
|
||||
import java.net.URI;
|
||||
@ -36,6 +38,7 @@ public class MediaSessionTabHelper {
|
||||
private WebContentsObserver mWebContentsObserver;
|
||||
private int mPreviousVolumeControlStream = AudioManager.USE_DEFAULT_STREAM_TYPE;
|
||||
private MediaNotificationInfo.Builder mNotificationInfoBuilder = null;
|
||||
private MediaMetadata mFallbackMetadata;
|
||||
|
||||
private MediaNotificationListener mControlsListener = new MediaNotificationListener() {
|
||||
@Override
|
||||
@ -84,7 +87,8 @@ public class MediaSessionTabHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void mediaSessionStateChanged(boolean isControllable, boolean isPaused) {
|
||||
public void mediaSessionStateChanged(boolean isControllable, boolean isPaused,
|
||||
MediaMetadata metadata) {
|
||||
if (!isControllable) {
|
||||
hideNotification();
|
||||
return;
|
||||
@ -97,8 +101,20 @@ public class MediaSessionTabHelper {
|
||||
+ "Showing the full URL instead.");
|
||||
}
|
||||
|
||||
mFallbackMetadata = null;
|
||||
|
||||
// The page's title is used as a placeholder if no title is specified in the
|
||||
// metadata.
|
||||
if (TextUtils.isEmpty(metadata.getTitle())) {
|
||||
mFallbackMetadata = new MediaMetadata(
|
||||
sanitizeMediaTitle(mTab.getTitle()),
|
||||
metadata.getArtist(),
|
||||
metadata.getAlbum());
|
||||
metadata = mFallbackMetadata;
|
||||
}
|
||||
|
||||
mNotificationInfoBuilder = new MediaNotificationInfo.Builder()
|
||||
.setTitle(sanitizeMediaTitle(mTab.getTitle()))
|
||||
.setMetadata(metadata)
|
||||
.setPaused(isPaused)
|
||||
.setOrigin(origin)
|
||||
.setTabId(mTab.getId())
|
||||
@ -145,9 +161,11 @@ public class MediaSessionTabHelper {
|
||||
@Override
|
||||
public void onTitleUpdated(Tab tab) {
|
||||
assert tab == mTab;
|
||||
if (mNotificationInfoBuilder == null) return;
|
||||
if (mNotificationInfoBuilder == null || mFallbackMetadata == null) return;
|
||||
|
||||
mFallbackMetadata.setTitle(sanitizeMediaTitle(mTab.getTitle()));
|
||||
mNotificationInfoBuilder.setMetadata(mFallbackMetadata);
|
||||
|
||||
mNotificationInfoBuilder.setTitle(sanitizeMediaTitle(mTab.getTitle()));
|
||||
MediaNotificationManager.show(ApplicationStatus.getApplicationContext(),
|
||||
mNotificationInfoBuilder.build());
|
||||
}
|
||||
|
5
chrome/android/javatests/src/org/chromium/chrome/browser/media/ui/NotificationTitleUpdatedTest.java
5
chrome/android/javatests/src/org/chromium/chrome/browser/media/ui/NotificationTitleUpdatedTest.java
@ -19,6 +19,7 @@ import org.chromium.chrome.test.util.ChromeRestriction;
|
||||
import org.chromium.chrome.test.util.browser.TabTitleObserver;
|
||||
import org.chromium.content.browser.test.util.JavaScriptUtils;
|
||||
import org.chromium.content_public.browser.WebContentsObserver;
|
||||
import org.chromium.content_public.common.MediaMetadata;
|
||||
|
||||
/**
|
||||
* Test of media notifications to see whether the text updates when the tab title changes
|
||||
@ -102,7 +103,7 @@ public class NotificationTitleUpdatedTest extends ChromeActivityTestCaseBase<Chr
|
||||
tab.getWebContents().getObserversForTesting();
|
||||
while (observers.hasNext()) {
|
||||
observers.next().mediaSessionStateChanged(
|
||||
isControllable, isSuspended);
|
||||
isControllable, isSuspended, new MediaMetadata("", "", ""));
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -125,7 +126,7 @@ public class NotificationTitleUpdatedTest extends ChromeActivityTestCaseBase<Chr
|
||||
@Override
|
||||
public void run() {
|
||||
assertEquals(title, MediaNotificationManager
|
||||
.getNotificationInfoForTesting(NOTIFICATION_ID).title);
|
||||
.getNotificationInfoForTesting(NOTIFICATION_ID).metadata.getTitle());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
@ -11,6 +11,7 @@
|
||||
#include "base/android/scoped_java_ref.h"
|
||||
#include "content/browser/renderer_host/render_widget_host_impl.h"
|
||||
#include "content/browser/web_contents/web_contents_impl.h"
|
||||
#include "content/common/android/media_metadata_android.h"
|
||||
#include "content/public/browser/navigation_details.h"
|
||||
#include "content/public/browser/navigation_entry.h"
|
||||
#include "content/public/browser/navigation_handle.h"
|
||||
@ -307,14 +308,18 @@ void WebContentsObserverProxy::DidStartNavigationToPendingEntry(
|
||||
env, obj.obj(), jstring_url.obj());
|
||||
}
|
||||
|
||||
void WebContentsObserverProxy::MediaSessionStateChanged(bool is_controllable,
|
||||
bool is_suspended) {
|
||||
void WebContentsObserverProxy::MediaSessionStateChanged(
|
||||
bool is_controllable,
|
||||
bool is_suspended,
|
||||
const MediaMetadata& metadata) {
|
||||
JNIEnv* env = AttachCurrentThread();
|
||||
|
||||
ScopedJavaLocalRef<jobject> obj(java_observer_);
|
||||
ScopedJavaLocalRef<jobject> j_metadata =
|
||||
MediaMetadataAndroid::CreateJavaObject(env, metadata);
|
||||
|
||||
Java_WebContentsObserverProxy_mediaSessionStateChanged(
|
||||
env, obj.obj(), is_controllable, is_suspended);
|
||||
env, obj.obj(), is_controllable, is_suspended, j_metadata.obj());
|
||||
}
|
||||
|
||||
void WebContentsObserverProxy::SetToBaseURLForDataURLIfNeeded(
|
||||
|
@ -73,7 +73,8 @@ class WebContentsObserverProxy : public WebContentsObserver {
|
||||
const GURL& url,
|
||||
NavigationController::ReloadType reload_type) override;
|
||||
void MediaSessionStateChanged(bool is_controllable,
|
||||
bool is_suspended) override;
|
||||
bool is_suspended,
|
||||
const MediaMetadata& metadata) override;
|
||||
void SetToBaseURLForDataURLIfNeeded(std::string* url);
|
||||
|
||||
void DidFailLoadInternal(bool is_provisional_load,
|
||||
|
@ -13,6 +13,7 @@
|
||||
#include "content/common/content_export.h"
|
||||
#include "content/public/browser/web_contents_observer.h"
|
||||
#include "content/public/browser/web_contents_user_data.h"
|
||||
#include "content/public/common/media_metadata.h"
|
||||
|
||||
class MediaSessionBrowserTest;
|
||||
|
||||
@ -58,6 +59,11 @@ class MediaSession : public WebContentsObserver,
|
||||
|
||||
~MediaSession() override;
|
||||
|
||||
void setMetadata(const MediaMetadata& metadata) {
|
||||
metadata_ = metadata;
|
||||
}
|
||||
const MediaMetadata& metadata() const { return metadata_; }
|
||||
|
||||
// Adds the given player to the current media session. Returns whether the
|
||||
// player was successfully added. If it returns false, AddPlayer() should be
|
||||
// called again later.
|
||||
@ -184,6 +190,8 @@ class MediaSession : public WebContentsObserver,
|
||||
// multiply their volume with this multiplier to get the effective volume.
|
||||
double volume_multiplier_;
|
||||
|
||||
MediaMetadata metadata_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(MediaSession);
|
||||
};
|
||||
|
||||
|
@ -48,8 +48,9 @@ class MockWebContentsObserver : public WebContentsObserver {
|
||||
MockWebContentsObserver(WebContents* web_contents)
|
||||
: WebContentsObserver(web_contents) {}
|
||||
|
||||
MOCK_METHOD2(MediaSessionStateChanged,
|
||||
void(bool is_controllable, bool is_suspended));
|
||||
MOCK_METHOD3(MediaSessionStateChanged,
|
||||
void(bool is_controllable, bool is_suspended,
|
||||
const content::MediaMetadata& metadata));
|
||||
};
|
||||
|
||||
} // namespace
|
||||
@ -488,7 +489,7 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, MediaSessionType) {
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsShowForContent) {
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
new MockMediaSessionObserver);
|
||||
@ -502,7 +503,7 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsShowForContent) {
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsNoShowForTransient) {
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(false, false));
|
||||
MediaSessionStateChanged(false, false, testing::_));
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
new MockMediaSessionObserver);
|
||||
@ -516,9 +517,9 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsNoShowForTransient) {
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsHideWhenStopped) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(false, true))
|
||||
MediaSessionStateChanged(false, true, testing::_))
|
||||
.After(showControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -534,7 +535,7 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsHideWhenStopped) {
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsShownAcceptTransient) {
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
new MockMediaSessionObserver);
|
||||
@ -550,10 +551,10 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsShownAcceptTransient) {
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsShownAfterContentAdded) {
|
||||
Expectation dontShowControls = EXPECT_CALL(
|
||||
*mock_web_contents_observer(), MediaSessionStateChanged(false, false));
|
||||
Expectation dontShowControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(false, false, testing::_));
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false))
|
||||
MediaSessionStateChanged(true, false, testing::_))
|
||||
.After(dontShowControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -571,7 +572,7 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsStayIfOnlyOnePlayerHasBeenPaused) {
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
new MockMediaSessionObserver);
|
||||
@ -590,9 +591,9 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsHideWhenTheLastPlayerIsRemoved) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(false, true))
|
||||
MediaSessionStateChanged(false, true, testing::_))
|
||||
.After(showControls);
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
new MockMediaSessionObserver);
|
||||
@ -614,9 +615,9 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsHideWhenAllThePlayersAreRemoved) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(false, true))
|
||||
MediaSessionStateChanged(false, true, testing::_))
|
||||
.After(showControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -634,9 +635,9 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsNotHideWhenTheLastPlayerIsPaused) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true))
|
||||
MediaSessionStateChanged(true, true, testing::_))
|
||||
.After(showControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -659,9 +660,9 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
SuspendTemporaryUpdatesControls) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true))
|
||||
MediaSessionStateChanged(true, true, testing::_))
|
||||
.After(showControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -677,12 +678,11 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsUpdatedWhenResumed) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
Expectation pauseControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true))
|
||||
.After(showControls);
|
||||
MediaSessionStateChanged(true, true, testing::_)).After(showControls);
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false))
|
||||
MediaSessionStateChanged(true, false, testing::_))
|
||||
.After(pauseControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -699,9 +699,9 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest, ControlsUpdatedWhenResumed) {
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsHideWhenSessionSuspendedPermanently) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(false, true))
|
||||
MediaSessionStateChanged(false, true, testing::_))
|
||||
.After(showControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -718,12 +718,11 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ConstrolsHideWhenSessionStops) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
Expectation pauseControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true))
|
||||
.After(showControls);
|
||||
MediaSessionStateChanged(true, true, testing::_)).After(showControls);
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(false, true))
|
||||
MediaSessionStateChanged(false, true, testing::_))
|
||||
.After(pauseControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -740,12 +739,11 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsHideWhenSessionChangesFromContentToTransient) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
Expectation pauseControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true))
|
||||
.After(showControls);
|
||||
MediaSessionStateChanged(true, true, testing::_)).After(showControls);
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(false, false))
|
||||
MediaSessionStateChanged(false, false, testing::_))
|
||||
.After(pauseControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -765,12 +763,11 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsUpdatedWhenNewPlayerResetsSession) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
Expectation pauseControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true))
|
||||
.After(showControls);
|
||||
MediaSessionStateChanged(true, true, testing::_)).After(showControls);
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false))
|
||||
MediaSessionStateChanged(true, false, testing::_))
|
||||
.After(pauseControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -789,12 +786,11 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsResumedWhenPlayerIsResumed) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
Expectation pauseControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true))
|
||||
.After(showControls);
|
||||
MediaSessionStateChanged(true, true, testing::_)).After(showControls);
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false))
|
||||
MediaSessionStateChanged(true, false, testing::_))
|
||||
.After(pauseControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
@ -813,9 +809,10 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsUpdatedDueToResumeSessionAction) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true)).After(showControls);
|
||||
MediaSessionStateChanged(true, true, testing::_))
|
||||
.After(showControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
new MockMediaSessionObserver);
|
||||
@ -830,12 +827,11 @@ IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
IN_PROC_BROWSER_TEST_F(MediaSessionBrowserTest,
|
||||
ControlsUpdatedDueToSuspendSessionAction) {
|
||||
Expectation showControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false));
|
||||
MediaSessionStateChanged(true, false, testing::_));
|
||||
Expectation pauseControls = EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, true))
|
||||
.After(showControls);
|
||||
MediaSessionStateChanged(true, true, testing::_)).After(showControls);
|
||||
EXPECT_CALL(*mock_web_contents_observer(),
|
||||
MediaSessionStateChanged(true, false))
|
||||
MediaSessionStateChanged(true, false, testing::_))
|
||||
.After(pauseControls);
|
||||
|
||||
scoped_ptr<MockMediaSessionObserver> media_session_observer(
|
||||
|
@ -3586,7 +3586,8 @@ void WebContentsImpl::OnMediaSessionStateChanged() {
|
||||
MediaSession* session = MediaSession::Get(this);
|
||||
FOR_EACH_OBSERVER(WebContentsObserver, observers_,
|
||||
MediaSessionStateChanged(session->IsControllable(),
|
||||
session->IsSuspended()));
|
||||
session->IsSuspended(),
|
||||
session->metadata()));
|
||||
}
|
||||
|
||||
void WebContentsImpl::ResumeMediaSession() {
|
||||
|
@ -8,10 +8,12 @@
|
||||
#include "base/android/jni_registrar.h"
|
||||
#include "base/macros.h"
|
||||
#include "content/common/android/hash_set.h"
|
||||
#include "content/common/android/media_metadata_android.h"
|
||||
|
||||
namespace {
|
||||
base::android::RegistrationMethod kContentRegisteredMethods[] = {
|
||||
{ "HashSet", content::RegisterHashSet },
|
||||
{ "MediaMetadataAndroid", content::MediaMetadataAndroid::Register },
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
33
content/common/android/media_metadata_android.cc
Normal file
33
content/common/android/media_metadata_android.cc
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#include "content/common/android/media_metadata_android.h"
|
||||
|
||||
#include "base/android/jni_string.h"
|
||||
#include "content/public/common/media_metadata.h"
|
||||
#include "jni/MediaMetadata_jni.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
// static
|
||||
base::android::ScopedJavaLocalRef<jobject>
|
||||
MediaMetadataAndroid::CreateJavaObject(
|
||||
JNIEnv* env, const MediaMetadata& metadata) {
|
||||
ScopedJavaLocalRef<jstring> j_title(
|
||||
base::android::ConvertUTF16ToJavaString(env, metadata.title));
|
||||
ScopedJavaLocalRef<jstring> j_artist(
|
||||
base::android::ConvertUTF16ToJavaString(env, metadata.artist));
|
||||
ScopedJavaLocalRef<jstring> j_album(
|
||||
base::android::ConvertUTF16ToJavaString(env, metadata.album));
|
||||
|
||||
return Java_MediaMetadata_create(
|
||||
env, j_title.obj(), j_artist.obj(), j_album.obj());
|
||||
}
|
||||
|
||||
// static
|
||||
bool MediaMetadataAndroid::Register(JNIEnv* env) {
|
||||
return RegisterNativesImpl(env);
|
||||
}
|
||||
|
||||
} // namespace content
|
29
content/common/android/media_metadata_android.h
Normal file
29
content/common/android/media_metadata_android.h
Normal file
@ -0,0 +1,29 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef CONTENT_COMMON_ANDROID_MEDIA_METADATA_ANDROID_H_
|
||||
#define CONTENT_COMMON_ANDROID_MEDIA_METADATA_ANDROID_H_
|
||||
|
||||
#include <jni.h>
|
||||
|
||||
#include "base/android/scoped_java_ref.h"
|
||||
|
||||
namespace content {
|
||||
|
||||
struct MediaMetadata;
|
||||
|
||||
class MediaMetadataAndroid {
|
||||
public:
|
||||
static base::android::ScopedJavaLocalRef<jobject> CreateJavaObject(
|
||||
JNIEnv* env, const MediaMetadata& metadata);
|
||||
|
||||
static bool Register(JNIEnv* env);
|
||||
|
||||
private:
|
||||
DISALLOW_IMPLICIT_CONSTRUCTORS(MediaMetadataAndroid);
|
||||
};
|
||||
|
||||
} // namespace content
|
||||
|
||||
#endif // CONTENT_COMMON_ANDROID_MEDIA_METADATA_ANDROID_H_
|
@ -234,6 +234,8 @@
|
||||
'common/android/gin_java_bridge_value.h',
|
||||
'common/android/hash_set.cc',
|
||||
'common/android/hash_set.h',
|
||||
'common/android/media_metadata_android.cc',
|
||||
'common/android/media_metadata_android.h',
|
||||
'common/android/sync_compositor_messages.cc',
|
||||
'common/android/sync_compositor_messages.h',
|
||||
'common/appcache_interfaces.cc',
|
||||
|
@ -42,6 +42,7 @@
|
||||
'public/android/java/src/org/chromium/content/browser/webcontents/WebContentsImpl.java',
|
||||
'public/android/java/src/org/chromium/content/browser/webcontents/WebContentsObserverProxy.java',
|
||||
'public/android/java/src/org/chromium/content_public/browser/LoadUrlParams.java',
|
||||
'public/android/java/src/org/chromium/content_public/common/MediaMetadata.java',
|
||||
],
|
||||
'variables': {
|
||||
'jni_gen_package': 'content',
|
||||
|
@ -12,6 +12,7 @@ import org.chromium.base.annotations.CalledByNative;
|
||||
import org.chromium.base.annotations.JNINamespace;
|
||||
import org.chromium.base.annotations.MainDex;
|
||||
import org.chromium.content_public.browser.WebContentsObserver;
|
||||
import org.chromium.content_public.common.MediaMetadata;
|
||||
|
||||
/**
|
||||
* Serves as a compound observer proxy for dispatching WebContentsObserver callbacks,
|
||||
@ -233,9 +234,11 @@ class WebContentsObserverProxy extends WebContentsObserver {
|
||||
|
||||
@Override
|
||||
@CalledByNative
|
||||
public void mediaSessionStateChanged(boolean isControllable, boolean isSuspended) {
|
||||
public void mediaSessionStateChanged(
|
||||
boolean isControllable, boolean isSuspended, MediaMetadata metadata) {
|
||||
for (mObserversIterator.rewind(); mObserversIterator.hasNext();) {
|
||||
mObserversIterator.next().mediaSessionStateChanged(isControllable, isSuspended);
|
||||
mObserversIterator.next().mediaSessionStateChanged(
|
||||
isControllable, isSuspended, metadata);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -4,6 +4,8 @@
|
||||
|
||||
package org.chromium.content_public.browser;
|
||||
|
||||
import org.chromium.content_public.common.MediaMetadata;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
|
||||
/**
|
||||
@ -163,9 +165,11 @@ public abstract class WebContentsObserver {
|
||||
/**
|
||||
* Called when the media session state changed.
|
||||
* @param isControllable if the session can be resumed or suspended.
|
||||
* @param isSuspended if the session currently suspended or not
|
||||
* @param isSuspended if the session currently suspended or not.
|
||||
* @param metadata of the media session.
|
||||
*/
|
||||
public void mediaSessionStateChanged(boolean isControllable, boolean isSuspended) {}
|
||||
public void mediaSessionStateChanged(
|
||||
boolean isControllable, boolean isSuspended, MediaMetadata metadata) {}
|
||||
|
||||
/**
|
||||
* Stop observing the web contents and clean up associated references.
|
||||
|
111
content/public/android/java/src/org/chromium/content_public/common/MediaMetadata.java
Normal file
111
content/public/android/java/src/org/chromium/content_public/common/MediaMetadata.java
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2016 The Chromium Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
package org.chromium.content_public.common;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import org.chromium.base.annotations.CalledByNative;
|
||||
import org.chromium.base.annotations.JNINamespace;
|
||||
|
||||
/**
|
||||
* The MediaMetadata class carries information related to a media session. It is
|
||||
* the Java counterpart of content::MediaMetadata.
|
||||
*/
|
||||
@JNINamespace("content")
|
||||
public class MediaMetadata {
|
||||
|
||||
@NonNull
|
||||
private String mTitle;
|
||||
|
||||
@NonNull
|
||||
private String mArtist;
|
||||
|
||||
@NonNull
|
||||
private String mAlbum;
|
||||
|
||||
/**
|
||||
* Returns the title associated with the media session.
|
||||
*/
|
||||
public String getTitle() {
|
||||
return mTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the artist name associated with the media session.
|
||||
*/
|
||||
public String getArtist() {
|
||||
return mArtist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the album name associated with the media session.
|
||||
*/
|
||||
public String getAlbum() {
|
||||
return mAlbum;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title associated with the media session.
|
||||
* @param title The title to use for the media session.
|
||||
*/
|
||||
public void setTitle(String title) {
|
||||
mTitle = title;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the arstist name associated with the media session.
|
||||
* @param arstist The artist name to use for the media session.
|
||||
*/
|
||||
public void setArtist(String artist) {
|
||||
mArtist = artist;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the album name associated with the media session.
|
||||
* @param album The album name to use for the media session.
|
||||
*/
|
||||
public void setAlbum(String album) {
|
||||
mAlbum = album;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MediaMetadata from the C++ code. This is exactly like the
|
||||
* constructor below apart that it can be called by native code.
|
||||
*/
|
||||
@CalledByNative
|
||||
private static MediaMetadata create(String title, String artist, String album) {
|
||||
return new MediaMetadata(title == null ? "" : title, artist == null ? "" : artist,
|
||||
album == null ? "" : album);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new MediaMetadata.
|
||||
*/
|
||||
public MediaMetadata(@NonNull String title, @NonNull String artist, @NonNull String album) {
|
||||
mTitle = title;
|
||||
mArtist = artist;
|
||||
mAlbum = album;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object obj) {
|
||||
if (obj == this) return true;
|
||||
if (!(obj instanceof MediaMetadata)) return false;
|
||||
|
||||
MediaMetadata other = (MediaMetadata) obj;
|
||||
return TextUtils.equals(mTitle, other.mTitle)
|
||||
&& TextUtils.equals(mArtist, other.mArtist)
|
||||
&& TextUtils.equals(mAlbum, other.mAlbum);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
int result = mTitle.hashCode();
|
||||
result = 31 * result + mArtist.hashCode();
|
||||
result = 31 * result + mAlbum.hashCode();
|
||||
return result;
|
||||
}
|
||||
}
|
@ -13,6 +13,7 @@
|
||||
#include "content/common/content_export.h"
|
||||
#include "content/public/browser/navigation_controller.h"
|
||||
#include "content/public/common/frame_navigate_params.h"
|
||||
#include "content/public/common/media_metadata.h"
|
||||
#include "content/public/common/security_style.h"
|
||||
#include "ipc/ipc_listener.h"
|
||||
#include "ipc/ipc_sender.h"
|
||||
@ -445,7 +446,8 @@ class CONTENT_EXPORT WebContentsObserver : public IPC::Listener,
|
||||
|
||||
// Invoked when media session has changed its state.
|
||||
virtual void MediaSessionStateChanged(bool is_controllable,
|
||||
bool is_suspended) {}
|
||||
bool is_suspended,
|
||||
const MediaMetadata& metadata) {}
|
||||
|
||||
// Invoked when the renderer process changes the page scale factor.
|
||||
virtual void OnPageScaleFactorChanged(float page_scale_factor) {}
|
||||
|
Reference in New Issue
Block a user