1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
3 * You can obtain one at http://mozilla.org/MPL/2.0/. */
5 #import <MediaPlayer/MediaPlayer.h>
7 #include "MediaHardwareKeysEventSourceMacMediaCenter.h"
9 #include "mozilla/dom/MediaControlUtils.h"
10 #include "nsCocoaUtils.h"
12 using namespace mozilla::dom;
14 // avoid redefined macro in unified build
16 #define LOG(msg, ...) \
17 MOZ_LOG(gMediaControlLog, LogLevel::Debug, \
18 ("MediaHardwareKeysEventSourceMacMediaCenter=%p, " msg, this, ##__VA_ARGS__))
23 MediaCenterEventHandler MediaHardwareKeysEventSourceMacMediaCenter::CreatePlayPauseHandler() {
24 return Block_copy(^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent* event) {
25 MPNowPlayingInfoCenter* center = [MPNowPlayingInfoCenter defaultCenter];
26 center.playbackState = center.playbackState == MPNowPlayingPlaybackStatePlaying
27 ? MPNowPlayingPlaybackStatePaused
28 : MPNowPlayingPlaybackStatePlaying;
29 HandleEvent(MediaControlKey::Playpause);
30 return MPRemoteCommandHandlerStatusSuccess;
34 MediaCenterEventHandler MediaHardwareKeysEventSourceMacMediaCenter::CreateNextTrackHandler() {
35 return Block_copy(^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent* event) {
36 HandleEvent(MediaControlKey::Nexttrack);
37 return MPRemoteCommandHandlerStatusSuccess;
41 MediaCenterEventHandler MediaHardwareKeysEventSourceMacMediaCenter::CreatePreviousTrackHandler() {
42 return Block_copy(^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent* event) {
43 HandleEvent(MediaControlKey::Previoustrack);
44 return MPRemoteCommandHandlerStatusSuccess;
48 MediaCenterEventHandler MediaHardwareKeysEventSourceMacMediaCenter::CreatePlayHandler() {
49 return Block_copy(^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent* event) {
50 MPNowPlayingInfoCenter* center = [MPNowPlayingInfoCenter defaultCenter];
51 if (center.playbackState != MPNowPlayingPlaybackStatePlaying) {
52 center.playbackState = MPNowPlayingPlaybackStatePlaying;
54 HandleEvent(MediaControlKey::Play);
55 return MPRemoteCommandHandlerStatusSuccess;
59 MediaCenterEventHandler MediaHardwareKeysEventSourceMacMediaCenter::CreatePauseHandler() {
60 return Block_copy(^MPRemoteCommandHandlerStatus(MPRemoteCommandEvent* event) {
61 MPNowPlayingInfoCenter* center = [MPNowPlayingInfoCenter defaultCenter];
62 if (center.playbackState != MPNowPlayingPlaybackStatePaused) {
63 center.playbackState = MPNowPlayingPlaybackStatePaused;
65 HandleEvent(MediaControlKey::Pause);
66 return MPRemoteCommandHandlerStatusSuccess;
70 MediaHardwareKeysEventSourceMacMediaCenter::MediaHardwareKeysEventSourceMacMediaCenter() {
71 mPlayPauseHandler = CreatePlayPauseHandler();
72 mNextTrackHandler = CreateNextTrackHandler();
73 mPreviousTrackHandler = CreatePreviousTrackHandler();
74 mPlayHandler = CreatePlayHandler();
75 mPauseHandler = CreatePauseHandler();
76 LOG("Create MediaHardwareKeysEventSourceMacMediaCenter");
79 MediaHardwareKeysEventSourceMacMediaCenter::~MediaHardwareKeysEventSourceMacMediaCenter() {
80 LOG("Destroy MediaHardwareKeysEventSourceMacMediaCenter");
81 EndListeningForEvents();
82 MPNowPlayingInfoCenter* center = [MPNowPlayingInfoCenter defaultCenter];
83 center.playbackState = MPNowPlayingPlaybackStateStopped;
86 void MediaHardwareKeysEventSourceMacMediaCenter::BeginListeningForEvents() {
87 MPNowPlayingInfoCenter* center = [MPNowPlayingInfoCenter defaultCenter];
88 center.playbackState = MPNowPlayingPlaybackStatePlaying;
89 MPRemoteCommandCenter* commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
90 commandCenter.togglePlayPauseCommand.enabled = true;
91 [commandCenter.togglePlayPauseCommand addTargetWithHandler:mPlayPauseHandler];
92 commandCenter.nextTrackCommand.enabled = true;
93 [commandCenter.nextTrackCommand addTargetWithHandler:mNextTrackHandler];
94 commandCenter.previousTrackCommand.enabled = true;
95 [commandCenter.previousTrackCommand addTargetWithHandler:mPreviousTrackHandler];
96 commandCenter.playCommand.enabled = true;
97 [commandCenter.playCommand addTargetWithHandler:mPlayHandler];
98 commandCenter.pauseCommand.enabled = true;
99 [commandCenter.pauseCommand addTargetWithHandler:mPauseHandler];
102 void MediaHardwareKeysEventSourceMacMediaCenter::EndListeningForEvents() {
103 MPNowPlayingInfoCenter* center = [MPNowPlayingInfoCenter defaultCenter];
104 center.playbackState = MPNowPlayingPlaybackStatePaused;
105 center.nowPlayingInfo = nil;
106 MPRemoteCommandCenter* commandCenter = [MPRemoteCommandCenter sharedCommandCenter];
107 commandCenter.togglePlayPauseCommand.enabled = false;
108 [commandCenter.togglePlayPauseCommand removeTarget:nil];
109 commandCenter.nextTrackCommand.enabled = false;
110 [commandCenter.nextTrackCommand removeTarget:nil];
111 commandCenter.previousTrackCommand.enabled = false;
112 [commandCenter.previousTrackCommand removeTarget:nil];
113 commandCenter.playCommand.enabled = false;
114 [commandCenter.playCommand removeTarget:nil];
115 commandCenter.pauseCommand.enabled = false;
116 [commandCenter.pauseCommand removeTarget:nil];
119 bool MediaHardwareKeysEventSourceMacMediaCenter::Open() {
120 LOG("Open MediaHardwareKeysEventSourceMacMediaCenter");
122 BeginListeningForEvents();
126 void MediaHardwareKeysEventSourceMacMediaCenter::Close() {
127 LOG("Close MediaHardwareKeysEventSourceMacMediaCenter");
128 SetPlaybackState(dom::MediaSessionPlaybackState::None);
129 EndListeningForEvents();
131 MediaControlKeySource::Close();
134 bool MediaHardwareKeysEventSourceMacMediaCenter::IsOpened() const { return mOpened; }
136 void MediaHardwareKeysEventSourceMacMediaCenter::HandleEvent(MediaControlKey aEvent) {
137 for (auto iter = mListeners.begin(); iter != mListeners.end(); ++iter) {
138 (*iter)->OnActionPerformed(MediaControlAction(aEvent));
142 void MediaHardwareKeysEventSourceMacMediaCenter::SetPlaybackState(
143 MediaSessionPlaybackState aState) {
144 MPNowPlayingInfoCenter* center = [MPNowPlayingInfoCenter defaultCenter];
145 if (aState == MediaSessionPlaybackState::Playing) {
146 center.playbackState = MPNowPlayingPlaybackStatePlaying;
147 } else if (aState == MediaSessionPlaybackState::Paused) {
148 center.playbackState = MPNowPlayingPlaybackStatePaused;
149 } else if (aState == MediaSessionPlaybackState::None) {
150 center.playbackState = MPNowPlayingPlaybackStateStopped;
152 MediaControlKeySource::SetPlaybackState(aState);
155 void MediaHardwareKeysEventSourceMacMediaCenter::SetMediaMetadata(
156 const dom::MediaMetadataBase& aMetadata) {
157 NSMutableDictionary* nowPlayingInfo = [NSMutableDictionary dictionary];
158 [nowPlayingInfo setObject:nsCocoaUtils::ToNSString(aMetadata.mTitle)
159 forKey:MPMediaItemPropertyTitle];
160 [nowPlayingInfo setObject:nsCocoaUtils::ToNSString(aMetadata.mArtist)
161 forKey:MPMediaItemPropertyArtist];
162 [nowPlayingInfo setObject:nsCocoaUtils::ToNSString(aMetadata.mAlbum)
163 forKey:MPMediaItemPropertyAlbumTitle];
164 // The procedure of updating `nowPlayingInfo` is actually an async operation
165 // from our testing, Apple's documentation doesn't mention that though. So be
166 // aware that checking `nowPlayingInfo` immedately after setting it might not
167 // yield the expected result.
168 [MPNowPlayingInfoCenter defaultCenter].nowPlayingInfo = nowPlayingInfo;