From: Dawid Juszczak Date: Mon, 22 Jul 2019 15:17:19 +0000 (+0200) Subject: [mediacontroller] abilities and subscriptions X-Git-Tag: accepted/tizen/unified/20190920.065316~10^2 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=9503ee73de343c23c3613183b12559acdd90a685;p=platform%2Fcore%2Fapi%2Fwebapi-plugins.git [mediacontroller] abilities and subscriptions [ACR] http://suprem.sec.samsung.net/jira/browse/TWDAPI-227 [Verification] tested manually on chrome console tct-mediacontroller-tizen-tests 100% PASS Change-Id: I839c1f825e61caa828ad06d2c6b4a779ee3da320 Signed-off-by: Dawid Juszczak --- diff --git a/src/mediacontroller/mediacontroller_api.js b/src/mediacontroller/mediacontroller_api.js index aef8c56..9261af3 100755 --- a/src/mediacontroller/mediacontroller_api.js +++ b/src/mediacontroller/mediacontroller_api.js @@ -241,6 +241,28 @@ var ServerInfoPlaylistUpdatedListener = new ListenerManager( } ); +var ClientAbilityChangeListener = new ListenerManager( + native_, + '_ClientAbilityChangeListener', + function(msg, listener) { + if (msg.action === 'onplaybackabilitychanged') { + native_.callIfPossible( + listener[msg.action], + new MediaControllerServerInfo(msg), + new MediaControllerPlaybackAbilitiesInfo(msg.name) + ); + } + if (msg.action === 'onsimpleabilitychanged') { + native_.callIfPossible( + listener[msg.action], + new MediaControllerServerInfo(msg), + msg.type, + msg.ability + ); + } + } +); + var EditManager = function() { this.isAllowed = false; }; @@ -316,6 +338,21 @@ var MediaControllerContentType = { UNDECIDED: 'UNDECIDED' }; +var MediaControllerAbilitySupport = { + YES: 'YES', + NO: 'NO', + UNDECIDED: 'UNDECIDED' +}; + +var MediaControllerSimpleAbility = { + PLAYBACK_POSITION: 'PLAYBACK_POSITION', + SHUFFLE: 'SHUFFLE', + REPEAT: 'REPEAT', + PLAYLIST: 'PLAYLIST', + CLIENT_CUSTOM: 'CLIENT_CUSTOM', + SEARCH: 'SEARCH' +}; + function MediaControllerManager() {} MediaControllerManager.prototype.getClient = function() { @@ -575,6 +612,345 @@ var MediaControllerPlaybackInfo = function(data) { } }; +var MediaControllerAbilities = function() { + var _playbackPosition = MediaControllerAbilitySupport.UNDECIDED, + _shuffle = MediaControllerAbilitySupport.UNDECIDED, + _repeat = MediaControllerAbilitySupport.UNDECIDED, + _playlist = MediaControllerAbilitySupport.UNDECIDED, + _clientCustom = MediaControllerAbilitySupport.UNDECIDED, + _search = MediaControllerAbilitySupport.UNDECIDED; + + Object.defineProperties(this, { + playback: { + value: new MediaControllerPlaybackAbilities(), + enumerable: true + }, + playbackPosition: { + get: function() { + return _playbackPosition; + }, + set: function(val) { + setSimpleAbility(MediaControllerSimpleAbility.PLAYBACK_POSITION, val); + _playbackPosition = val; + }, + enumerable: true + }, + shuffle: { + get: function() { + return _shuffle; + }, + set: function(val) { + setSimpleAbility(MediaControllerSimpleAbility.SHUFFLE, val); + _shuffle = val; + }, + enumerable: true + }, + repeat: { + get: function() { + return _repeat; + }, + set: function(val) { + setSimpleAbility(MediaControllerSimpleAbility.REPEAT, val); + _repeat = val; + }, + enumerable: true + }, + playlist: { + get: function() { + return _playlist; + }, + set: function(val) { + setSimpleAbility(MediaControllerSimpleAbility.PLAYLIST, val); + _playlist = val; + }, + enumerable: true + }, + clientCustom: { + get: function() { + return _clientCustom; + }, + set: function(val) { + setSimpleAbility(MediaControllerSimpleAbility.CLIENT_CUSTOM, val); + _clientCustom = val; + }, + enumerable: true + }, + search: { + get: function() { + return _search; + }, + set: function(val) { + setSimpleAbility(MediaControllerSimpleAbility.SEARCH, val); + _search = val; + }, + enumerable: true + } + }); +}; + +var MediaControllerPlaybackAbilities = function() { + var _play = MediaControllerAbilitySupport.UNDECIDED, + _pause = MediaControllerAbilitySupport.UNDECIDED, + _stop = MediaControllerAbilitySupport.UNDECIDED, + _next = MediaControllerAbilitySupport.UNDECIDED, + _prev = MediaControllerAbilitySupport.UNDECIDED, + _forward = MediaControllerAbilitySupport.UNDECIDED, + _rewind = MediaControllerAbilitySupport.UNDECIDED, + _togglePlayPause = MediaControllerAbilitySupport.UNDECIDED; + + Object.defineProperties(this, { + play: { + get: function() { + return _play; + }, + set: function(val) { + checkSupportValue(val); + _play = val; + }, + enumerable: true + }, + pause: { + get: function() { + return _pause; + }, + set: function(val) { + checkSupportValue(val); + _pause = val; + }, + enumerable: true + }, + stop: { + get: function() { + return _stop; + }, + set: function(val) { + checkSupportValue(val); + _stop = val; + }, + enumerable: true + }, + next: { + get: function() { + return _next; + }, + set: function(val) { + checkSupportValue(val); + _next = val; + }, + enumerable: true + }, + prev: { + get: function() { + return _prev; + }, + set: function(val) { + checkSupportValue(val); + _prev = val; + }, + enumerable: true + }, + forward: { + get: function() { + return _forward; + }, + set: function(val) { + checkSupportValue(val); + _forward = val; + }, + enumerable: true + }, + rewind: { + get: function() { + return _rewind; + }, + set: function(val) { + checkSupportValue(val); + _rewind = val; + }, + enumerable: true + }, + togglePlayPause: { + get: function() { + return _togglePlayPause; + }, + set: function(val) { + checkSupportValue(val); + _togglePlayPause = val; + }, + enumerable: true + } + }); + + this.saveAbilities = function() { + var data = { + PLAY: _play, + PAUSE: _pause, + STOP: _stop, + NEXT: _next, + PREV: _prev, + FORWARD: _forward, + REWIND: _rewind, + TOGGLE_PLAY_PAUSE: _togglePlayPause + }; + + var result = native_.callSync( + 'MediaControllerServer_savePlaybackAbilities', + data + ); + if (native_.isFailure(result)) { + throw new native_.getErrorObject(result); + } + }; +}; + +var MediaControllerAbilitiesInfo = function(serverName) { + Object.defineProperties(this, { + playback: { + value: new MediaControllerPlaybackAbilitiesInfo(serverName), + enumerable: true, + writable: false + }, + playbackPosition: { + get: function() { + return getSimpleAbility( + serverName, + MediaControllerSimpleAbility.PLAYBACK_POSITION + ); + }, + set: function() {}, + enumerable: true + }, + shuffle: { + get: function() { + return getSimpleAbility(serverName, MediaControllerSimpleAbility.SHUFFLE); + }, + set: function() {}, + enumerable: true + }, + repeat: { + get: function() { + return getSimpleAbility(serverName, MediaControllerSimpleAbility.REPEAT); + }, + set: function() {}, + enumerable: true + }, + playlist: { + get: function() { + return getSimpleAbility( + serverName, + MediaControllerSimpleAbility.PLAYLIST + ); + }, + set: function() {}, + enumerable: true + }, + clientCustom: { + get: function() { + return getSimpleAbility( + serverName, + MediaControllerSimpleAbility.CLIENT_CUSTOM + ); + }, + set: function() {}, + enumerable: true + }, + search: { + get: function() { + return getSimpleAbility(serverName, MediaControllerSimpleAbility.SEARCH); + }, + set: function() {}, + enumerable: true + } + }); + + this.subscribe = function() { + var data = { + serverName: serverName + }; + var result = native_.callSync('MediaControllerAbilitiesInfo_subscribe', data); + if (native_.isFailure(result)) { + throw new native_.getErrorObject(result); + } + }; + + this.unsubscribe = function() { + var data = { + serverName: serverName + }; + var result = native_.callSync('MediaControllerAbilitiesInfo_unsubscribe', data); + if (native_.isFailure(result)) { + throw new native_.getErrorObject(result); + } + }; +}; + +var MediaControllerPlaybackAbilitiesInfo = function(serverName) { + Object.defineProperties(this, { + play: { + get: function() { + return getPlaybackAbility(serverName, MediaControllerPlaybackState.PLAY); + }, + set: function() {}, + enumerable: true + }, + pause: { + get: function() { + return getPlaybackAbility(serverName, MediaControllerPlaybackState.PAUSE); + }, + set: function() {}, + enumerable: true + }, + stop: { + get: function() { + return getPlaybackAbility(serverName, MediaControllerPlaybackState.STOP); + }, + set: function() {}, + enumerable: true + }, + next: { + get: function() { + return getPlaybackAbility(serverName, MediaControllerPlaybackState.NEXT); + }, + set: function() {}, + enumerable: true + }, + prev: { + get: function() { + return getPlaybackAbility(serverName, MediaControllerPlaybackState.PREV); + }, + set: function() {}, + enumerable: true + }, + forward: { + get: function() { + return getPlaybackAbility( + serverName, + MediaControllerPlaybackState.FORWARD + ); + }, + set: function() {}, + enumerable: true + }, + rewind: { + get: function() { + return getPlaybackAbility( + serverName, + MediaControllerPlaybackState.REWIND + ); + }, + set: function() {}, + enumerable: true + }, + togglePlayPause: { + get: function() { + return getPlaybackAbility(serverName, 'TOGGLE_PLAY_PAUSE'); + }, + set: function() {}, + enumerable: true + } + }); +}; + function MediaControllerServer(data) { var _iconURI = null; @@ -592,6 +968,10 @@ function MediaControllerServer(data) { _iconURI = edit_.isAllowed ? (v ? v : null) : _iconURI; }, enumerable: true + }, + abilities: { + value: new MediaControllerAbilities(), + enumerable: true } }); } @@ -1094,6 +1474,83 @@ MediaControllerServer.prototype.unsetSearchRequestListener = function() { native_.removeListener('SearchRequestListener'); }; +var getPlaybackAbility = function(serverName, action) { + var data = { + serverName: serverName, + action: action + }; + + var result = native_.callSync('MediaControllerClient_getPlaybackAbility', data); + if (native_.isFailure(result)) { + throw new native_.getErrorObject(result); + } + var ability = native_.getResultObject(result); + return ability.value; +}; + +var getSimpleAbility = function(name, type) { + var data = { + serverName: name, + abilityType: type + }; + var result = native_.callSync('MediaControllerClient_getSimpleAbility', data); + if (native_.isFailure(result)) { + throw new native_.getErrorObject(result); + } + var ability = native_.getResultObject(result); + return ability.value; +}; + +var setSimpleAbility = function(abilityType, support) { + var args = validator_.validateArgs(arguments, [ + { + name: 'abilityType', + type: types_.ENUM, + values: Object.keys(MediaControllerSimpleAbility) + }, + { + name: 'support', + type: types_.ENUM, + values: Object.keys(MediaControllerAbilitySupport) + } + ]); + + if (MediaControllerAbilitySupport.UNDECIDED === args.support) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Cannot set ability value to ' + args.support + ); + } + + var data = { + abilityType: args.abilityType, + support: args.support + }; + + var result = native_.callSync('MediaControllerServer_setSimpleAbility', data); + + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } +}; + +var checkSupportValue = function(support) { + var args = validator_.validateArgs(arguments, [ + { + name: 'support', + type: types_.ENUM, + values: Object.keys(MediaControllerAbilitySupport) + } + ]); + + if (MediaControllerAbilitySupport.UNDECIDED === args.support) { + throw new WebAPIException( + WebAPIException.INVALID_VALUES_ERR, + 'Cannot set ability value to ' + args.support + ); + } +}; + function MediaControllerClient() {} MediaControllerClient.prototype.findServers = function(successCallback, errorCallback) { @@ -1261,6 +1718,85 @@ function SearchFilter(contentType, category, keyword, extraData) { }); } +MediaControllerClient.prototype.addAbilityChangeListener = function(callback) { + var args = validator_.validateArgs(arguments, [ + { + name: 'callback', + type: types_.LISTENER, + values: ['onplaybackabilitychanged', 'onsimpleabilitychanged'] + } + ]); + + var nativeData = { + listenerId: ClientAbilityChangeListener.listenerName + }; + + if (type_.isEmptyObject(ClientAbilityChangeListener.listeners)) { + var result = native_.callSync( + 'MediaControllerClient_addAbilityChangeListener', + nativeData + ); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + } + + return ClientAbilityChangeListener.addListener(args.callback); +}; + +MediaControllerClient.prototype.removeAbilityChangeListener = function(callbackId) { + var args = validator_.validateArgs(arguments, [ + { + name: 'callbackId', + type: types_.LONG + } + ]); + + ClientAbilityChangeListener.removeListener(args.callbackId); + + if (type_.isEmptyObject(ClientAbilityChangeListener.listeners)) { + var result = native_.callSync( + 'MediaControllerClient_removeAbilityChangeListener' + ); + if (native_.isFailure(result)) { + throw native_.getErrorObject(result); + } + } +}; + +MediaControllerClient.prototype.findSubscribedServers = function( + successCallback, + errorCallback +) { + var args = validator_.validateArgs(arguments, [ + { + name: 'successCallback', + type: types_.FUNCTION + }, + { + name: 'errorCallback', + type: types_.FUNCTION, + optional: true, + nullable: true + } + ]); + + var callback = function(result) { + if (native_.isFailure(result)) { + native_.callIfPossible(args.errorCallback, native_.getErrorObject(result)); + return; + } + var info = []; + var data = native_.getResultObject(result); + for (var i = 0; i < data.length; i++) { + info.push(new MediaControllerServerInfo(data[i])); + } + native_.callIfPossible(args.successCallback, info); + }; + + native_.call('MediaControllerClient_findSubscribedServers', {}, callback); +}; + function MediaControllerServerInfo(data) { Object.defineProperties(this, { name: { @@ -1304,6 +1840,11 @@ function MediaControllerServerInfo(data) { }.bind(this), set: function() {}, enumerable: true + }, + abilities: { + value: new MediaControllerAbilitiesInfo(data.name), + enumerable: true, + writable: false } }); } @@ -1738,7 +2279,6 @@ MediaControllerServerInfo.prototype.removePlaylistUpdatedListener = function(wat var result = native_.callSync( 'MediaControllerServerInfo_removePlaylistUpdatedListener' ); - if (native_.isFailure(result)) { throw native_.getErrorObject(result); } diff --git a/src/mediacontroller/mediacontroller_client.cc b/src/mediacontroller/mediacontroller_client.cc index cce9ce4..7cf7cf3 100644 --- a/src/mediacontroller/mediacontroller_client.cc +++ b/src/mediacontroller/mediacontroller_client.cc @@ -34,7 +34,7 @@ using common::tools::ReportError; using common::tools::ReportSuccess; using common::JsonToBundle; -MediaControllerClient::MediaControllerClient() : handle_(nullptr) { +MediaControllerClient::MediaControllerClient() : handle_(nullptr), subscribed_servers{} { ScopeLogger(); } @@ -59,6 +59,10 @@ MediaControllerClient::~MediaControllerClient() { LoggerE("Failed to unset playlist update listener"); } + if (nullptr != ability_listener_ && !UnsetAbilityChangeListener()) { + LoggerE("Failed to unset ability listener"); + } + if (nullptr != handle_ && MEDIA_CONTROLLER_ERROR_NONE != mc_client_destroy(handle_)) { LoggerE("Unable to destroy media controller client"); } @@ -173,6 +177,74 @@ PlatformResult MediaControllerClient::GetLatestServerInfo(picojson::value* serve return PlatformResult(ErrorCode::NO_ERROR); } +PlatformResult MediaControllerClient::GetPlaybackAbility(const std::string& server_name, + const std::string& action, + picojson::value* abilities) { + ScopeLogger(); + mc_playback_ability_h ability_h = nullptr; + + SCOPE_EXIT { + mc_playback_ability_destroy(ability_h); + }; + + int ret = mc_client_get_server_playback_ability(handle_, server_name.c_str(), &ability_h); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting playback ability", + ("mc_client_get_server_playback_ability() error: %d, message: %s", + ret, get_error_message(ret))); + } + + std::string ability_str; + PlatformResult result = types::ConvertPlaybackAbility(ability_h, action, &ability_str); + if (!result) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error getting playback ability", + ("types::ConvertPlaybackAbility() error: %d, message: %s", ret, get_error_message(ret))); + } + + picojson::object& abilities_obj = abilities->get(); + abilities_obj["value"] = picojson::value(ability_str); + + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult MediaControllerClient::GetSimpleAbility(const std::string& server_name, + const std::string& ability_type, + picojson::value* ability_val) { + ScopeLogger(); + mc_ability_support_e support_e = MC_ABILITY_SUPPORTED_YES; + mc_ability_e ability_e = MC_ABILITY_SHUFFLE; + PlatformResult result = + types::MediaControllerSimpleAbilityEnum.getValue(ability_type, &ability_e); + if (!result) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of ability", + ("MediaControllerSimpleAbilityEnum.getValue() failed, error: %s", + result.message().c_str())); + } + + int ret = + mc_client_get_server_ability_support(handle_, server_name.c_str(), ability_e, &support_e); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error getting ability", + ("mc_client_get_ability_support() error: %d, message: %s", ret, get_error_message(ret))); + } + + std::string ability_str; + result = types::MediaControllerAbilitySupportEnum.getName(support_e, &ability_str); + if (!result) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting name of ability", + ("MediaControllerAbilitySupportEnum.getName() failed, error: %s", + result.message().c_str())); + } + + *ability_val = picojson::value(picojson::object()); + picojson::object& obj = ability_val->get(); + obj["value"] = picojson::value(ability_str); + + return PlatformResult(ErrorCode::NO_ERROR); +} + PlatformResult MediaControllerClient::GetPlaybackInfo(const std::string& server_name, picojson::object* playback_info) { ScopeLogger(); @@ -443,6 +515,156 @@ PlatformResult MediaControllerClient::UnsetPlaybackInfoListener() { return PlatformResult(ErrorCode::NO_ERROR); } +PlatformResult MediaControllerClient::SetAbilityChangeListener(const JsonCallback& callback) { + ScopeLogger(); + int failed_setters = 0; + SCOPE_EXIT { + int (*unsetters[])(mc_client_h) = {mc_client_unset_playback_ability_updated_cb, + mc_client_unset_ability_support_updated_cb}; + + for (int i = 0; i < failed_setters; ++i) { + auto ret = unsetters[i](handle_); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + LoggerE("Fail (%d) returned by the [%d] unsetter", ret, i); + } + } + }; + + int ret = mc_client_set_playback_ability_updated_cb(handle_, OnPlaybackAbilityUpdate, this); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, + "Unable to register playback ability listener", + ("mc_client_set_playback_ability_updated_cb() error: %d, message: %s", + ret, get_error_message(ret))); + } + + ret = mc_client_set_ability_support_updated_cb(handle_, OnSimpleAbilityUpdate, this); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + failed_setters = 1; + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Unable to register simple ability listener", + ("mc_client_set_ability_support_updated_cb() error: %d, message: %s", + ret, get_error_message(ret))); + } + + ability_listener_ = callback; + return PlatformResult(ErrorCode::NO_ERROR); +} + +void UnsubscribeAllTypes(mc_client_h handle_, const std::string& server_name) { + int ret; + for (auto type : types::MediaControllerSubscriptionTypeEnum) { + ret = mc_client_unsubscribe(handle_, type.second, server_name.c_str()); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + LoggerE("mc_client_unsubscribe() failed with type %s, error: %d, message: %s", + type.first.c_str(), ret, get_error_message(ret)); + } + } +} + +PlatformResult MediaControllerClient::UnsetAbilityChangeListener() { + ScopeLogger(); + + // we have to unsubscribe all servers before unsetting callback to avoid + // calling callback twice if the callback will be set again + for (auto srv : subscribed_servers) { + UnsubscribeAllTypes(handle_, srv); + } + subscribed_servers.clear(); + + int ret = mc_client_unset_playback_ability_updated_cb(handle_); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + LoggerW("mc_client_unset_playback_ability_updated_cb() failed with error %d, message: %s", ret, + get_error_message(ret)); + } + + ret = mc_client_unset_ability_support_updated_cb(handle_); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + LoggerW("mc_client_unset_ability_support_updated_cb() failed with error %d, message: %s", ret, + get_error_message(ret)); + } + + ability_listener_ = nullptr; + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult MediaControllerClient::SubscribeServer(const std::string& server_name) { + ScopeLogger(); + int ret = 0; + int to_unsubscribe = 0; + if (nullptr == ability_listener_) { + return PlatformResult(ErrorCode::INVALID_STATE_ERR, + "Unable to subscribe server, AbilityChangeListener is not set"); + } + + SCOPE_EXIT { + if (to_unsubscribe > 0) { + for (auto type : types::MediaControllerSubscriptionTypeEnum) { + if (to_unsubscribe > 0) { + ret = mc_client_unsubscribe(handle_, type.second, server_name.c_str()); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + LoggerW("mc_client_unsubscribe() failed on type: %s, with error: %d, message: %s", + type.first.c_str(), ret, get_error_message(ret)); + } + } + --to_unsubscribe; + } + } + }; + + for (auto type : types::MediaControllerSubscriptionTypeEnum) { + ret = mc_client_subscribe(handle_, type.second, server_name.c_str()); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Unable to subscribe server", + ("mc_client_subscribe() error: %d, message: %s", ret, get_error_message(ret))); + } + ++to_unsubscribe; + } + + to_unsubscribe = 0; + subscribed_servers.push_back(server_name); + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult MediaControllerClient::UnsubscribeServer(const std::string& server_name) { + ScopeLogger(); + + UnsubscribeAllTypes(handle_, server_name); + subscribed_servers.remove(server_name); + + /** + * After unsubscribing last media controller server, unsetting and setting callback + * is needed to receive again callbacks from all media controller servers. + */ + if (subscribed_servers.size() <= 0 && nullptr != ability_listener_) { + JsonCallback callback = ability_listener_; + UnsetAbilityChangeListener(); + auto result = SetAbilityChangeListener(callback); + if (!result) { + return result; + } + } + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult MediaControllerClient::FindSubscribedServers(picojson::array* servers) { + ScopeLogger(); + + /** + * If subscription is successful, then servers are subscribed to every subscription type, + * so we can check only the one type of subscription to receive all subscribed servers. + */ + int ret = mc_client_foreach_server_subscribed(handle_, MC_SUBSCRIPTION_TYPE_ABILITY_SUPPORT, + FindServersCallback, servers); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Unable to fetch active servers.", + ("mc_client_foreach_server() error: %d, message: %s", ret, get_error_message(ret))); + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + void MediaControllerClient::OnPlaybackUpdate(const char* server_name, mc_playback_h playback, void* user_data) { ScopeLogger(); @@ -475,6 +697,23 @@ void MediaControllerClient::OnPlaybackUpdate(const char* server_name, mc_playbac client->playback_info_listener_(&data); } +void MediaControllerClient::OnPlaybackAbilityUpdate(const char* server_name, + mc_playback_ability_h ability, + void* user_data) { + ScopeLogger(); + MediaControllerClient* client = static_cast(user_data); + + picojson::value data = picojson::value(picojson::object()); + picojson::object& data_o = data.get(); + + data_o["action"] = picojson::value(std::string("onplaybackabilitychanged")); + data_o["name"] = picojson::value(std::string(server_name)); + // only active servers can trigger listener, thus state is always "ACTIVE" + data_o["state"] = picojson::value(std::string("ACTIVE")); + + client->ability_listener_(&data); +} + void MediaControllerClient::OnShuffleModeUpdate(const char* server_name, mc_shuffle_mode_e mode, void* user_data) { ScopeLogger(); @@ -490,6 +729,40 @@ void MediaControllerClient::OnShuffleModeUpdate(const char* server_name, mc_shuf client->playback_info_listener_(&data); } +void MediaControllerClient::OnSimpleAbilityUpdate(const char* server_name, mc_ability_e type, + mc_ability_support_e ability, void* user_data) { + ScopeLogger(); + MediaControllerClient* client = static_cast(user_data); + + std::string type_str; + PlatformResult result = types::MediaControllerSimpleAbilityEnum.getName(type, &type_str); + if (!result) { + LoggerE("MediaControllerSimpleAbilityEnum.getName() failed, error: %s", + result.message().c_str()); + return; + } + + std::string ability_str; + result = types::MediaControllerAbilitySupportEnum.getName(ability, &ability_str); + if (!result) { + LoggerE("MediaControllerAbilitySupportEnum.getName() failed, error: %s", + result.message().c_str()); + return; + } + + picojson::value data = picojson::value(picojson::object()); + picojson::object& data_o = data.get(); + + data_o["action"] = picojson::value(std::string("onsimpleabilitychanged")); + data_o["name"] = picojson::value(std::string(server_name)); + // only active servers can trigger listener, thus state is always "ACTIVE" + data_o["state"] = picojson::value(std::string("ACTIVE")); + data_o["type"] = picojson::value(type_str); + data_o["ability"] = picojson::value(ability_str); + + client->ability_listener_(&data); +} + void MediaControllerClient::OnRepeatModeUpdate(const char* server_name, mc_repeat_mode_e mode, void* user_data) { ScopeLogger(); diff --git a/src/mediacontroller/mediacontroller_client.h b/src/mediacontroller/mediacontroller_client.h index e073a77..e9dd2b5 100644 --- a/src/mediacontroller/mediacontroller_client.h +++ b/src/mediacontroller/mediacontroller_client.h @@ -18,6 +18,7 @@ #define MEDIACONTROLLER_MEDIACONTROLLER_CLIENT_H_ #include +#include #include #include "common/optional.h" @@ -39,25 +40,20 @@ class MediaControllerClient { common::PlatformResult GetPlaybackInfo(const std::string& server_name, picojson::object* playback_info); common::PlatformResult GetMetadata(const std::string& server_name, picojson::object* metadata); - common::PlatformResult SendPlaybackState(const std::string& server_name, const std::string& state); common::PlatformResult SendPlaybackPosition(const std::string& server_name, double position); common::PlatformResult SendShuffleMode(const std::string& server_name, bool mode); common::PlatformResult SendRepeatMode(const std::string& server_name, bool mode); common::PlatformResult SendRepeatState(const std::string& server_name, const std::string& state); - common::PlatformResult SendSearchRequest(const std::string& server_name, const picojson::value& request, const JsonCallback& callback, char** request_id); - common::PlatformResult SendCommand(const std::string& server_name, const std::string& command, const picojson::value& data, const JsonCallback& reply_cb, char** request_id); - common::PlatformResult SetServerStatusChangeListener(const JsonCallback& callback); common::PlatformResult UnsetServerStatusChangeListener(); - common::PlatformResult SetPlaybackInfoListener(const JsonCallback& callback); common::PlatformResult UnsetPlaybackInfoListener(); common::PlatformResult SendPlaybackItem(const std::string& name, const std::string& playlist_name, @@ -67,6 +63,16 @@ class MediaControllerClient { common::PlatformResult UnsetPlaylistUpdateListener(); common::PlatformResult GetServerIconURI(const std::string& name, common::optional* icon_uri); + common::PlatformResult GetPlaybackAbility(const std::string& server_name, + const std::string& action, picojson::value* abilities); + common::PlatformResult SetAbilityChangeListener(const JsonCallback& callback); + common::PlatformResult UnsetAbilityChangeListener(); + common::PlatformResult GetSimpleAbility(const std::string& server_name, + const std::string& ability_type, + picojson::value* ability_val); + common::PlatformResult SubscribeServer(const std::string& server_name); + common::PlatformResult UnsubscribeServer(const std::string& server_name); + common::PlatformResult FindSubscribedServers(picojson::array* servers); private: mc_client_h handle_; @@ -75,6 +81,9 @@ class MediaControllerClient { JsonCallback server_status_listener_; JsonCallback command_reply_callback_; JsonCallback playlist_update_listener_; + JsonCallback ability_listener_; + + std::list subscribed_servers; static bool FindServersCallback(const char* server_name, void* user_data); @@ -88,6 +97,10 @@ class MediaControllerClient { static void OnMetadataUpdate(const char* server_name, mc_metadata_h metadata_h, void* user_data); static void OnPlaylistUpdate(const char* server_name, mc_playlist_update_mode_e mode, const char* playlist_name, mc_playlist_h playlist, void* user_data); + static void OnPlaybackAbilityUpdate(const char* server_name, mc_playback_ability_h ability, + void* user_data); + static void OnSimpleAbilityUpdate(const char* server_name, mc_ability_e type, + mc_ability_support_e mode, void* user_data); }; } // namespace mediacontroller diff --git a/src/mediacontroller/mediacontroller_instance.cc b/src/mediacontroller/mediacontroller_instance.cc index 41022de..6559f82 100644 --- a/src/mediacontroller/mediacontroller_instance.cc +++ b/src/mediacontroller/mediacontroller_instance.cc @@ -36,6 +36,8 @@ const std::string kPrivilegeMediaControllerClient = const std::string kPrivilegeMediaControllerServer = "http://tizen.org/privilege/mediacontroller.server"; +const char* kAbilityType = "abilityType"; +const char* kServerName = "serverName"; } // namespace using common::ErrorCode; @@ -85,6 +87,9 @@ MediaControllerInstance::MediaControllerInstance() { MediaControllerServerUpdatePlaybackItem); REGISTER_ASYNC("MediaControllerServer_getAllPlaylists", MediaControllerServerGetAllPlaylists); REGISTER_SYNC("MediaControllerServer_updateIconURI", MediaControllerServerUpdateIconURI); + REGISTER_SYNC("MediaControllerServer_savePlaybackAbilities", + MediaControllerServerSavePlaybackAbilities); + REGISTER_SYNC("MediaControllerServer_setSimpleAbility", MediaControllerServerSetSimpleAbility); // client REGISTER_SYNC("MediaControllerManager_getClient", MediaControllerManagerGetClient); @@ -92,6 +97,13 @@ MediaControllerInstance::MediaControllerInstance() { REGISTER_SYNC("MediaControllerClient_getLatestServerInfo", MediaControllerClientGetLatestServerInfo); REGISTER_SYNC("MediaControllerClient_getPlaybackInfo", MediaControllerClientGetPlaybackInfo); + REGISTER_SYNC("MediaControllerClient_getPlaybackAbility", + MediaControllerClientGetPlaybackAbility); + REGISTER_SYNC("MediaControllerClient_getSimpleAbility", MediaControllerClientGetSimpleAbility); + REGISTER_ASYNC("MediaControllerClient_findSubscribedServers", + MediaControllerClientFindSubscribedServers); + + // server_info REGISTER_SYNC("MediaControllerServerInfo_sendPlaybackState", MediaControllerServerInfoSendPlaybackState); REGISTER_ASYNC("MediaControllerServerInfo_sendPlaybackPosition", @@ -122,6 +134,15 @@ MediaControllerInstance::MediaControllerInstance() { REGISTER_ASYNC("MediaControllerServerInfo_getAllPlaylists", MediaControllerServerInfoGetAllPlaylists); REGISTER_SYNC("MediaControllerServerInfo_getIconURI", MediaControllerServerInfoGetIconURI); + REGISTER_SYNC("MediaControllerClient_addAbilityChangeListener", + MediaControllerClientAddAbilityChangeListener); + REGISTER_SYNC("MediaControllerClient_removeAbilityChangeListener", + MediaControllerClientRemoveAbilityChangeListener); + + // abilities_info + REGISTER_SYNC("MediaControllerAbilitiesInfo_subscribe", MediaControllerAbilitiesInfoSubscribe); + REGISTER_SYNC("MediaControllerAbilitiesInfo_unsubscribe", + MediaControllerAbilitiesInfoUnsubscribe); // playlist REGISTER_SYNC("MediaControllerPlaylist_addItem", MediaControllerPlaylistAddItem); @@ -135,18 +156,18 @@ MediaControllerInstance::~MediaControllerInstance() { ScopeLogger(); } -#define CHECK_EXIST(args, name, out) \ - if (!args.contains(name)) { \ - LogAndReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, name " is required argument"), \ - &out); \ - return; \ +#define CHECK_EXIST(args, name, out) \ + if (!args.contains(name)) { \ + std::string msg = std::string(name) + " is required argument"; \ + LogAndReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, msg), &out); \ + return; \ } -#define CHECK_TYPE(args, name, type, out) \ - if (!args.get(name).is()) { \ - LogAndReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, name " is not a " #type), \ - &out); \ - return; \ +#define CHECK_TYPE(args, name, type, out) \ + if (!args.get(name).is()) { \ + std::string msg = std::string(name) + " is not a " + #type; \ + LogAndReportError(PlatformResult(ErrorCode::TYPE_MISMATCH_ERR, msg), &out); \ + return; \ } #define CHECK_ARGS(args, name, type, out) \ @@ -682,6 +703,58 @@ void MediaControllerInstance::MediaControllerServerGetAllPlaylists(const picojso ReportSuccess(out); } +void MediaControllerInstance::MediaControllerServerSavePlaybackAbilities( + const picojson::value& args, picojson::object& out) { + ScopeLogger(); + + if (!server_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: server_")); + return; + } + + CHECK_ARGS(args, "PLAY", std::string, out) + CHECK_ARGS(args, "PAUSE", std::string, out) + CHECK_ARGS(args, "STOP", std::string, out) + CHECK_ARGS(args, "NEXT", std::string, out) + CHECK_ARGS(args, "PREV", std::string, out) + CHECK_ARGS(args, "FORWARD", std::string, out) + CHECK_ARGS(args, "REWIND", std::string, out) + CHECK_ARGS(args, "TOGGLE_PLAY_PAUSE", std::string, out) + + const PlatformResult& result = server_->SavePlaybackAbilities(args); + if (!result) { + LogAndReportError(result, &out, ("Failed server_->SavePlaybackAbilities()")); + return; + } + + ReportSuccess(out); +} + +void MediaControllerInstance::MediaControllerServerSetSimpleAbility(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + if (!server_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: server_")); + return; + } + + CHECK_ARGS(args, kAbilityType, std::string, out) + CHECK_ARGS(args, "support", std::string, out) + + std::string ability_type = args.get(kAbilityType).get(); + std::string support = args.get("support").get(); + const PlatformResult& result = server_->SetSimpleAbility(ability_type, support); + if (!result) { + LogAndReportError(result, &out, ("Failed server_->SetSimpleAbility()")); + return; + } + + ReportSuccess(out); +} + void MediaControllerInstance::MediaControllerManagerGetClient(const picojson::value& args, picojson::object& out) { ScopeLogger(); @@ -778,6 +851,91 @@ void MediaControllerInstance::MediaControllerClientGetPlaybackInfo(const picojso ReportSuccess(playback_info, out); } +void MediaControllerInstance::MediaControllerClientGetPlaybackAbility(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + if (!client_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: client_")); + return; + } + CHECK_ARGS(args, kServerName, std::string, out) + CHECK_ARGS(args, "action", std::string, out) + + std::string server_name = args.get(kServerName).get(); + std::string action = args.get("action").get(); + picojson::value ability = picojson::value(picojson::object()); + PlatformResult result = client_->GetPlaybackAbility(server_name, action, &ability); + if (!result) { + LogAndReportError(result, &out, ("Failed: client_->GetPlaybackAbility")); + return; + } + + ReportSuccess(ability, out); +} + +void MediaControllerInstance::MediaControllerClientGetSimpleAbility(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + if (!client_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: client_")); + return; + } + + CHECK_ARGS(args, kAbilityType, std::string, out) + CHECK_ARGS(args, kServerName, std::string, out) + + picojson::value ability_val = picojson::value(); + std::string ability_type = args.get(kAbilityType).get(); + std::string server_name = args.get(kServerName).get(); + PlatformResult result = client_->GetSimpleAbility(server_name, ability_type, &ability_val); + if (!result) { + LogAndReportError(result, &out, ("Failed: client_->GetSimpleAbility")); + return; + } + + ReportSuccess(ability_val, out); +} + +void MediaControllerInstance::MediaControllerClientFindSubscribedServers( + const picojson::value& args, picojson::object& out) { + ScopeLogger(); + + if (!client_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: client_")); + return; + } + + CHECK_EXIST(args, "callbackId", out) + + picojson::value callback_id = args.get("callbackId"); + + auto search = [this, callback_id]() -> void { + picojson::value response = picojson::value(picojson::object()); + picojson::object& response_obj = response.get(); + + picojson::value servers = picojson::value(picojson::array()); + PlatformResult result = client_->FindSubscribedServers(&servers.get()); + + response_obj["callbackId"] = callback_id; + if (result) { + ReportSuccess(servers, response_obj); + } else { + LogAndReportError(result, &response_obj, ("Failed to find subscribed servers")); + } + + Instance::PostMessage(this, response.serialize().c_str()); + }; + + TaskQueue::GetInstance().Async(search); + + ReportSuccess(out); +} + void MediaControllerInstance::MediaControllerServerInfoSendPlaybackState( const picojson::value& args, picojson::object& out) { ScopeLogger(); @@ -1250,6 +1408,104 @@ void MediaControllerInstance::MediaControllerServerInfoGetIconURI(const picojson ReportSuccess(picojson::value(result), out); } +void MediaControllerInstance::MediaControllerClientAddAbilityChangeListener( + const picojson::value& args, picojson::object& out) { + ScopeLogger(); + + if (!client_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: client_")); + return; + } + + CHECK_EXIST(args, "listenerId", out) + + picojson::value listener_id = args.get("listenerId"); + + JsonCallback callback = [this, listener_id](picojson::value* data) -> void { + if (!data) { + LoggerE("No data passed to json callback"); + return; + } + + picojson::object& request_o = data->get(); + request_o["listenerId"] = listener_id; + + Instance::PostMessage(this, data->serialize().c_str()); + }; + + auto result = client_->SetAbilityChangeListener(callback); + if (!result) { + LogAndReportError(result, &out); + return; + } + + ReportSuccess(out); +} + +void MediaControllerInstance::MediaControllerClientRemoveAbilityChangeListener( + const picojson::value& args, picojson::object& out) { + ScopeLogger(); + + if (!client_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: client_")); + return; + } + + auto result = client_->UnsetAbilityChangeListener(); + if (!result) { + LogAndReportError(result, &out); + return; + } + + ReportSuccess(out); +} + +void MediaControllerInstance::MediaControllerAbilitiesInfoSubscribe(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + if (!client_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: client_")); + return; + } + + CHECK_ARGS(args, kServerName, std::string, out) + + const std::string& server_name = args.get(kServerName).get(); + auto result = client_->SubscribeServer(server_name); + if (!result) { + LogAndReportError(result, &out); + return; + } + + ReportSuccess(out); +} + +void MediaControllerInstance::MediaControllerAbilitiesInfoUnsubscribe(const picojson::value& args, + picojson::object& out) { + ScopeLogger(); + + if (!client_) { + LogAndReportError(PlatformResult(ErrorCode::UNKNOWN_ERR, "Unknown error occured."), &out, + ("Failed: client_")); + return; + } + + CHECK_ARGS(args, kServerName, std::string, out) + + const std::string& server_name = args.get(kServerName).get(); + auto result = client_->UnsubscribeServer(server_name); + if (!result) { + LogAndReportError(result, &out); + return; + } + + ReportSuccess(out); +} + void MediaControllerInstance::MediaControllerPlaylistAddItem(const picojson::value& args, picojson::object& out) { ScopeLogger(); diff --git a/src/mediacontroller/mediacontroller_instance.h b/src/mediacontroller/mediacontroller_instance.h index 5757c94..66b081f 100644 --- a/src/mediacontroller/mediacontroller_instance.h +++ b/src/mediacontroller/mediacontroller_instance.h @@ -65,12 +65,25 @@ class MediaControllerInstance : public common::ParsedInstance { void MediaControllerServerUpdatePlaybackItem(const picojson::value& args, picojson::object& out); void MediaControllerServerGetAllPlaylists(const picojson::value& args, picojson::object& out); void MediaControllerServerUpdateIconURI(const picojson::value& args, picojson::object& out); + void MediaControllerServerSavePlaybackAbilities(const picojson::value& args, + picojson::object& out); + void MediaControllerServerSetSimpleAbility(const picojson::value& args, picojson::object& out); // client void MediaControllerManagerGetClient(const picojson::value& args, picojson::object& out); void MediaControllerClientFindServers(const picojson::value& args, picojson::object& out); void MediaControllerClientGetLatestServerInfo(const picojson::value& args, picojson::object& out); void MediaControllerClientGetPlaybackInfo(const picojson::value& args, picojson::object& out); + void MediaControllerClientGetPlaybackAbility(const picojson::value& args, picojson::object& out); + void MediaControllerClientGetSimpleAbility(const picojson::value& args, picojson::object& out); + void MediaControllerClientAddAbilityChangeListener(const picojson::value& args, + picojson::object& out); + void MediaControllerClientRemoveAbilityChangeListener(const picojson::value& args, + picojson::object& out); + void MediaControllerClientFindSubscribedServers(const picojson::value& args, + picojson::object& out); + + // serverInfo void MediaControllerServerInfoSendPlaybackState(const picojson::value& args, picojson::object& out); void MediaControllerServerInfoSendPlaybackPosition(const picojson::value& args, @@ -98,6 +111,10 @@ class MediaControllerInstance : public common::ParsedInstance { void MediaControllerServerInfoGetAllPlaylists(const picojson::value& args, picojson::object& out); void MediaControllerServerInfoGetIconURI(const picojson::value& args, picojson::object& out); + // abilities_info + void MediaControllerAbilitiesInfoSubscribe(const picojson::value& args, picojson::object& out); + void MediaControllerAbilitiesInfoUnsubscribe(const picojson::value& args, picojson::object& out); + // playlist void MediaControllerPlaylistAddItem(const picojson::value& args, picojson::object& out); void MediaControllerPlaylistGetItems(const picojson::value& args, picojson::object& out); diff --git a/src/mediacontroller/mediacontroller_server.cc b/src/mediacontroller/mediacontroller_server.cc index 37e6212..fb913d5 100644 --- a/src/mediacontroller/mediacontroller_server.cc +++ b/src/mediacontroller/mediacontroller_server.cc @@ -40,7 +40,14 @@ MediaControllerServer::MediaControllerServer() shuffle_mode_(MC_SHUFFLE_MODE_OFF), repeat_mode_(MC_REPEAT_MODE_OFF), is_shuffle_mode_set_(false), - is_repeat_mode_set_(false) { + is_repeat_mode_set_(false), + playback_abilities_{MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED, + MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED, + MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED, + MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED}, + simple_abilities_{MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED, + MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED, + MC_ABILITY_SUPPORTED_UNDECIDED, MC_ABILITY_SUPPORTED_UNDECIDED} { ScopeLogger(); } @@ -659,6 +666,117 @@ PlatformResult MediaControllerServer::MediaControllerPlaylistGetItems(const std: return PlatformResult(ErrorCode::NO_ERROR); } +PlatformResult MediaControllerServer::SetPlaybackAbility(const std::string& action, + const std::string& support_str) { + ScopeLogger(); + PlatformResult result = PlatformResult(ErrorCode::NO_ERROR); + + mc_playback_action_e action_e; + // Currently MediaControllerPlaybackActionEnum does not have member "TOGGLE_PLAY_PAUSE". + // It should be fixed in the future. + if ("TOGGLE_PLAY_PAUSE" == action) { + action_e = MC_PLAYBACK_ACTION_TOGGLE_PLAY_PAUSE; + } else { + result = types::MediaControllerPlaybackActionEnum.getValue(action, &action_e); + if (!result) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of playback action", + ("MediaControllerPlaybackActionEnum.getValue() failed, error: %s", + result.message().c_str())); + } + } + + mc_ability_support_e support; + result = types::MediaControllerAbilitySupportEnum.getValue(support_str, &support); + if (!result) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of ability", + ("MediaControllerAbilitySupportEnum.getValue() failed, error: %s", + result.message().c_str())); + } + + if (support == playback_abilities_.get(action)) { + LoggerD("No change in playback ability support, skipping"); + return PlatformResult(ErrorCode::NO_ERROR); + } + + int ret = mc_server_set_playback_ability(handle_, action_e, support); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error setting playback ability", + ("mc_server_set_playback_ability() error: %d, message: %s", ret, get_error_message(ret))); + } + + playback_abilities_.set(action, support); + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult MediaControllerServer::SavePlaybackAbilities(const picojson::value& abilities) { + ScopeLogger(); + + // Currently MediaControllerPlaybackActionEnum does not have member "TOGGLE_PLAY_PAUSE". + // It should be fixed in the future. + PlatformResult result = SetPlaybackAbility("TOGGLE_PLAY_PAUSE", + abilities.get("TOGGLE_PLAY_PAUSE").get()); + if (!result) { + return result; + } + + for (auto action : types::MediaControllerPlaybackActionEnum) { + const std::string& action_str = action.first.c_str(); + const std::string& support_str = abilities.get(action_str).get(); + result = SetPlaybackAbility(action_str, support_str); + if (!result) { + return result; + } + } + + int ret = mc_server_update_playback_ability(handle_); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error updating playback ability", + ("mc_server_update_playback_ability() error: %d, message: %s", ret, + get_error_message(ret))); + } + return PlatformResult(ErrorCode::NO_ERROR); +} + +PlatformResult MediaControllerServer::SetSimpleAbility(const std::string& ability_type, + const std::string& support_str) { + ScopeLogger(); + + mc_ability_support_e support_e; + PlatformResult result = + types::MediaControllerAbilitySupportEnum.getValue(support_str, &support_e); + if (!result) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of ability", + ("MediaControllerAbilitySupportEnum.getValue() failed, error: %s", + result.message().c_str())); + } + + mc_ability_support_e current_ability = simple_abilities_.get(ability_type); + + if (support_e == current_ability) { + LoggerD("No change in ability support, skipping"); + return PlatformResult(ErrorCode::NO_ERROR); + } + + mc_ability_e ability_e; + result = types::MediaControllerSimpleAbilityEnum.getValue(ability_type, &ability_e); + if (!result) { + return LogAndCreateResult(ErrorCode::UNKNOWN_ERR, "Error getting value of ability", + ("MediaControllerSimpleAbilityEnum.getValue() failed, error: %s", + result.message().c_str())); + } + + int ret = mc_server_set_ability_support(handle_, ability_e, support_e); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error setting ability", + ("mc_server_set_ability_support() error: %d, message: %s", ret, get_error_message(ret))); + } + + simple_abilities_.set(ability_type, support_e); + return PlatformResult(ErrorCode::NO_ERROR); +} + PlatformResult MediaControllerServer::SetChangeRequestPlaybackInfoListener( const JsonCallback& callback) { ScopeLogger(); diff --git a/src/mediacontroller/mediacontroller_server.h b/src/mediacontroller/mediacontroller_server.h index fc5a3bb..2de7696 100644 --- a/src/mediacontroller/mediacontroller_server.h +++ b/src/mediacontroller/mediacontroller_server.h @@ -26,6 +26,143 @@ namespace extension { namespace mediacontroller { +namespace { +const char* kPlay = "PLAY"; +const char* kPause = "PAUSE"; +const char* kStop = "STOP"; +const char* kNext = "NEXT"; +const char* kPrev = "PREV"; +const char* kForward = "FORWARD"; +const char* kRewind = "REWIND"; +const char* kTogglePlayPause = "TOGGLE_PLAY_PAUSE"; + +const char* kPlaybackPosition = "PLAYBACK_POSITION"; +const char* kShuffle = "SHUFFLE"; +const char* kRepeat = "REPEAT"; +const char* kPlaylist = "PLAYLIST"; +const char* kClientCustom = "CLIENT_CUSTOM"; +const char* kSearch = "SEARCH"; + +} // namespace + +struct playback_abilities { + mc_ability_support_e play_; + mc_ability_support_e pause_; + mc_ability_support_e stop_; + mc_ability_support_e next_; + mc_ability_support_e prev_; + mc_ability_support_e forward_; + mc_ability_support_e rewind_; + mc_ability_support_e toggle_play_pause_; + + mc_ability_support_e get(const std::string& ability) { + if (kPlay == ability) { + return play_; + } + if (kPause == ability) { + return pause_; + } + if (kStop == ability) { + return stop_; + } + if (kNext == ability) { + return next_; + } + if (kPrev == ability) { + return prev_; + } + if (kForward == ability) { + return forward_; + } + if (kRewind == ability) { + return rewind_; + } + if (kTogglePlayPause == ability) { + return toggle_play_pause_; + } + return MC_ABILITY_SUPPORTED_UNDECIDED; + } + + void set(const std::string& action, mc_ability_support_e val) { + if (kPlay == action) { + play_ = val; + } + if (kPause == action) { + pause_ = val; + } + if (kStop == action) { + stop_ = val; + } + if (kNext == action) { + next_ = val; + } + if (kPrev == action) { + prev_ = val; + } + if (kForward == action) { + forward_ = val; + } + if (kRewind == action) { + rewind_ = val; + } + if (kTogglePlayPause == action) { + toggle_play_pause_ = val; + } + } +}; + +struct simple_abilities { + mc_ability_support_e playback_position_; + mc_ability_support_e shuffle_; + mc_ability_support_e repeat_; + mc_ability_support_e playlist_; + mc_ability_support_e custom_command_; + mc_ability_support_e search_; + + mc_ability_support_e get(const std::string& ability) { + if (kPlaybackPosition == ability) { + return playback_position_; + } + if (kShuffle == ability) { + return shuffle_; + } + if (kRepeat == ability) { + return repeat_; + } + if (kPlaylist == ability) { + return playlist_; + } + if (kClientCustom == ability) { + return custom_command_; + } + if (kSearch == ability) { + return search_; + } + return MC_ABILITY_SUPPORTED_UNDECIDED; + } + + void set(const std::string& ability, mc_ability_support_e val) { + if (kPlaybackPosition == ability) { + playback_position_ = val; + } + if (kShuffle == ability) { + shuffle_ = val; + } + if (kRepeat == ability) { + repeat_ = val; + } + if (kPlaylist == ability) { + playlist_ = val; + } + if (kClientCustom == ability) { + custom_command_ = val; + } + if (kSearch == ability) { + search_ = val; + } + } +}; + class MediaControllerServer { public: MediaControllerServer(); @@ -64,6 +201,11 @@ class MediaControllerServer { common::PlatformResult MediaControllerPlaylistGetItems(const std::string& name, picojson::array* items); common::PlatformResult UpdateIconURI(const char* icon_uri); + common::PlatformResult SetPlaybackAbility(const std::string& action, + const std::string& support_str); + common::PlatformResult SavePlaybackAbilities(const picojson::value& abilities); + common::PlatformResult SetSimpleAbility(const std::string& ability_type, + const std::string& support_str); private: mc_server_h handle_; @@ -77,6 +219,8 @@ class MediaControllerServer { mc_repeat_mode_e repeat_mode_; bool is_shuffle_mode_set_; bool is_repeat_mode_set_; + playback_abilities playback_abilities_; + simple_abilities simple_abilities_; JsonCallback command_listener_; std::map playlist_handle_map_; diff --git a/src/mediacontroller/mediacontroller_utils.cc b/src/mediacontroller/mediacontroller_utils.cc index 7df8487..8bf61a7 100644 --- a/src/mediacontroller/mediacontroller_utils.cc +++ b/src/mediacontroller/mediacontroller_utils.cc @@ -95,6 +95,23 @@ const common::PlatformEnum MediaControllerSearchCategoryEn {"GENRE", MC_SEARCH_GENRE}, {"TPO", MC_SEARCH_TPO}}; +const common::PlatformEnum MediaControllerAbilitySupportEnum{ + {"YES", MC_ABILITY_SUPPORTED_YES}, + {"NO", MC_ABILITY_SUPPORTED_NO}, + {"UNDECIDED", MC_ABILITY_SUPPORTED_UNDECIDED}}; + +const common::PlatformEnum MediaControllerSubscriptionTypeEnum{ + {"PLAYBACK_ABILITY", MC_SUBSCRIPTION_TYPE_PLAYBACK_ABILITY}, + {"SIMPLE_ABILITIES", MC_SUBSCRIPTION_TYPE_ABILITY_SUPPORT}}; + +const common::PlatformEnum MediaControllerSimpleAbilityEnum{ + {"PLAYBACK_POSITION", MC_ABILITY_PLAYBACK_POSITION}, + {"SHUFFLE", MC_ABILITY_SHUFFLE}, + {"REPEAT", MC_ABILITY_REPEAT}, + {"PLAYLIST", MC_ABILITY_PLAYLIST}, + {"CLIENT_CUSTOM", MC_ABILITY_CLIENT_CUSTOM}, + {"SEARCH", MC_ABILITY_SEARCH}}; + PlatformResult ConvertPlaybackState(mc_playback_h playback_h, std::string* state) { ScopeLogger(); @@ -200,6 +217,41 @@ PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metad return PlatformResult(ErrorCode::NO_ERROR); } +PlatformResult ConvertPlaybackAbility(mc_playback_ability_h ability_h, const std::string& action, + std::string* ability_str) { + ScopeLogger(); + + mc_ability_support_e ability_e; + mc_playback_action_e action_e; + PlatformResult result = PlatformResult(ErrorCode::NO_ERROR); + + if ("TOGGLE_PLAY_PAUSE" == action) { + action_e = MC_PLAYBACK_ACTION_TOGGLE_PLAY_PAUSE; + } else { + result = MediaControllerPlaybackActionEnum.getValue(action, &action_e); + if (!result) { + LoggerE("MediaControllerPlaybackActionEnum.getValue() failed, error: %s", + result.message().c_str()); + return result; + } + } + + int ret = mc_playback_action_is_supported(ability_h, action_e, &ability_e); + if (MEDIA_CONTROLLER_ERROR_NONE != ret) { + return LogAndCreateResult( + ErrorCode::UNKNOWN_ERR, "Error checking playback action ability support", + ("mc_playback_action_is_supported() error: %d, message: %s", ret, get_error_message(ret))); + } + result = MediaControllerAbilitySupportEnum.getName(ability_e, ability_str); + if (!result) { + LoggerE("MediaControllerAbilitySupportEnum.getName() failed, error: %s", + result.message().c_str()); + return result; + } + + return PlatformResult(ErrorCode::NO_ERROR); +} + } // types PlatformResult utils::GetAllPlaylists(const std::string& app_id, picojson::array* playlists) { diff --git a/src/mediacontroller/mediacontroller_utils.h b/src/mediacontroller/mediacontroller_utils.h index 18fe21b..6d98e88 100644 --- a/src/mediacontroller/mediacontroller_utils.h +++ b/src/mediacontroller/mediacontroller_utils.h @@ -37,6 +37,8 @@ common::PlatformResult ConvertContentAgeRating(mc_playback_h playback_h, std::st common::PlatformResult ConvertPlaybackPosition(mc_playback_h playback_h, double* position); common::PlatformResult ConvertMetadata(mc_metadata_h metadata_h, picojson::object* metadata); common::PlatformResult ConvertContentType(mc_playback_h playback_h, std::string* contentType); +common::PlatformResult ConvertPlaybackAbility(mc_playback_ability_h ability_h, + const std::string& action, std::string* ability_str); extern const common::PlatformEnum MediaControllerServerStateEnum; extern const common::PlatformEnum MediaControllerPlaybackStateEnum; @@ -46,6 +48,9 @@ extern const common::PlatformEnum MediaControllerRepeatModeEnu extern const common::PlatformEnum MediaControllerContentAgeRatingEnum; extern const common::PlatformEnum MediaControllerContentTypeEnum; extern const common::PlatformEnum MediaControllerSearchCategoryEnum; +extern const common::PlatformEnum MediaControllerAbilitySupportEnum; +extern const common::PlatformEnum MediaControllerSubscriptionTypeEnum; +extern const common::PlatformEnum MediaControllerSimpleAbilityEnum; } // namespace types