Add ICU message format support
[chromium-blink-merge.git] / content / browser / media / android / media_session.cc
bloba8b64efec4c97877fd06bbb644fb14dc63781919
1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/browser/media/android/media_session.h"
7 #include "base/android/jni_android.h"
8 #include "content/browser/media/android/media_session_observer.h"
9 #include "content/browser/web_contents/web_contents_impl.h"
10 #include "content/public/browser/web_contents.h"
11 #include "content/public/browser/web_contents_delegate.h"
12 #include "jni/MediaSession_jni.h"
14 namespace content {
16 DEFINE_WEB_CONTENTS_USER_DATA_KEY(MediaSession);
18 MediaSession::PlayerIdentifier::PlayerIdentifier(MediaSessionObserver* observer,
19 int player_id)
20 : observer(observer),
21 player_id(player_id) {
24 bool MediaSession::PlayerIdentifier::operator==(
25 const PlayerIdentifier& other) const {
26 return this->observer == other.observer && this->player_id == other.player_id;
29 size_t MediaSession::PlayerIdentifier::Hash::operator()(
30 const PlayerIdentifier& player_identifier) const {
31 size_t hash = BASE_HASH_NAMESPACE::hash<MediaSessionObserver*>()(
32 player_identifier.observer);
33 hash += BASE_HASH_NAMESPACE::hash<int>()(player_identifier.player_id);
34 return hash;
37 // static
38 bool content::MediaSession::RegisterMediaSession(JNIEnv* env) {
39 return RegisterNativesImpl(env);
42 // static
43 MediaSession* MediaSession::Get(WebContents* web_contents) {
44 MediaSession* session = FromWebContents(web_contents);
45 if (!session) {
46 CreateForWebContents(web_contents);
47 session = FromWebContents(web_contents);
48 session->Initialize();
50 return session;
53 MediaSession::~MediaSession() {
54 DCHECK(players_.empty());
55 DCHECK(audio_focus_state_ == State::INACTIVE);
58 bool MediaSession::AddPlayer(MediaSessionObserver* observer,
59 int player_id,
60 Type type) {
61 // If the audio focus is already granted and is of type Content, there is
62 // nothing to do. If it is granted of type Transient the requested type is
63 // also transient, there is also nothing to do. Otherwise, the session needs
64 // to request audio focus again.
65 if (audio_focus_state_ == State::ACTIVE &&
66 (audio_focus_type_ == Type::Content || audio_focus_type_ == type)) {
67 players_.insert(PlayerIdentifier(observer, player_id));
68 return true;
71 State old_audio_focus_state = audio_focus_state_;
72 audio_focus_state_ = RequestSystemAudioFocus(type) ? State::ACTIVE
73 : State::INACTIVE;
74 audio_focus_type_ = type;
76 if (audio_focus_state_ != State::ACTIVE)
77 return false;
79 // The session should be reset if a player is starting while all players are
80 // suspended.
81 if (old_audio_focus_state != State::ACTIVE)
82 players_.clear();
84 players_.insert(PlayerIdentifier(observer, player_id));
85 UpdateWebContents();
87 return true;
90 void MediaSession::RemovePlayer(MediaSessionObserver* observer,
91 int player_id) {
92 auto it = players_.find(PlayerIdentifier(observer, player_id));
93 if (it != players_.end())
94 players_.erase(it);
96 AbandonSystemAudioFocusIfNeeded();
99 void MediaSession::RemovePlayers(MediaSessionObserver* observer) {
100 for (auto it = players_.begin(); it != players_.end();) {
101 if (it->observer == observer)
102 players_.erase(it++);
103 else
104 ++it;
107 AbandonSystemAudioFocusIfNeeded();
110 void MediaSession::OnSuspend(JNIEnv* env, jobject obj, jboolean temporary) {
111 if (audio_focus_state_ != State::ACTIVE)
112 return;
114 OnSuspendInternal(SuspendType::SYSTEM);
115 if (!temporary)
116 audio_focus_state_ = State::INACTIVE;
117 UpdateWebContents();
120 void MediaSession::OnResume(JNIEnv* env, jobject obj) {
121 if (audio_focus_state_ != State::SUSPENDED)
122 return;
124 OnResumeInternal(SuspendType::SYSTEM);
125 UpdateWebContents();
128 void MediaSession::Resume() {
129 DCHECK(IsSuspended());
131 // Request audio focus again in case we lost it because another app started
132 // playing while the playback was paused.
133 audio_focus_state_ = RequestSystemAudioFocus(audio_focus_type_)
134 ? State::ACTIVE
135 : State::INACTIVE;
136 if (audio_focus_state_ != State::ACTIVE)
137 return;
139 OnResumeInternal(SuspendType::UI);
142 void MediaSession::Suspend() {
143 DCHECK(!IsSuspended());
145 OnSuspendInternal(SuspendType::UI);
148 bool MediaSession::IsSuspended() const {
149 // TODO(mlamouri): should be == State::SUSPENDED.
150 return audio_focus_state_ != State::ACTIVE;
153 bool MediaSession::IsControllable() const {
154 // Only content type media session can be controllable unless it is inactive.
155 return audio_focus_state_ != State::INACTIVE &&
156 audio_focus_type_ == Type::Content;
159 void MediaSession::ResetJavaRefForTest() {
160 j_media_session_.Reset();
163 bool MediaSession::IsActiveForTest() const {
164 return audio_focus_state_ == State::ACTIVE;
167 MediaSession::Type MediaSession::audio_focus_type_for_test() const {
168 return audio_focus_type_;
171 void MediaSession::RemoveAllPlayersForTest() {
172 players_.clear();
173 AbandonSystemAudioFocusIfNeeded();
176 void MediaSession::OnSuspendInternal(SuspendType type) {
177 audio_focus_state_ = State::SUSPENDED;
178 suspend_type_ = type;
180 for (const auto& it : players_)
181 it.observer->OnSuspend(it.player_id);
184 void MediaSession::OnResumeInternal(SuspendType type) {
185 if (suspend_type_ != type && type != SuspendType::UI)
186 return;
188 audio_focus_state_ = State::ACTIVE;
190 for (const auto& it : players_)
191 it.observer->OnResume(it.player_id);
194 MediaSession::MediaSession(WebContents* web_contents)
195 : WebContentsObserver(web_contents),
196 audio_focus_state_(State::INACTIVE),
197 audio_focus_type_(Type::Transient) {}
199 void MediaSession::Initialize() {
200 JNIEnv* env = base::android::AttachCurrentThread();
201 DCHECK(env);
202 j_media_session_.Reset(Java_MediaSession_createMediaSession(
203 env,
204 base::android::GetApplicationContext(),
205 reinterpret_cast<intptr_t>(this)));
208 bool MediaSession::RequestSystemAudioFocus(Type type) {
209 // During tests, j_media_session_ might be null.
210 if (j_media_session_.is_null())
211 return true;
213 JNIEnv* env = base::android::AttachCurrentThread();
214 DCHECK(env);
215 return Java_MediaSession_requestAudioFocus(env, j_media_session_.obj(),
216 type == Type::Transient);
219 void MediaSession::AbandonSystemAudioFocusIfNeeded() {
220 if (audio_focus_state_ == State::INACTIVE || !players_.empty())
221 return;
223 // During tests, j_media_session_ might be null.
224 if (!j_media_session_.is_null()) {
225 JNIEnv* env = base::android::AttachCurrentThread();
226 DCHECK(env);
227 Java_MediaSession_abandonAudioFocus(env, j_media_session_.obj());
230 audio_focus_state_ = State::INACTIVE;
231 UpdateWebContents();
234 void MediaSession::UpdateWebContents() {
235 static_cast<WebContentsImpl*>(web_contents())->OnMediaSessionStateChanged();
238 } // namespace content