From 673d01982f5dd4724828d50d8885b970158f22f2 Mon Sep 17 00:00:00 2001 From: Lazar Kovacic Date: Wed, 15 Dec 2021 03:16:04 +0100 Subject: [PATCH] Added delegate methods for attributes, updated linux and android tv app --- .../linux/include/tv-callbacks.cpp | 13 + .../android/java/ContentLauncherManager.cpp | 329 +++++++----------- .../android/java/ContentLauncherManager.h | 27 +- .../tv-app/linux/include/cluster-init.cpp | 42 --- .../ContentLauncherManager.cpp | 78 ++--- .../content-launcher/ContentLauncherManager.h | 17 +- examples/tv-app/linux/main.cpp | 15 +- examples/tv-casting-app/linux/main.cpp | 3 + .../content-launch-delegate.h | 63 ++++ .../content-launch-server.cpp | 107 +++++- .../content-launch-server.h | 18 +- 11 files changed, 361 insertions(+), 351 deletions(-) create mode 100644 src/app/clusters/content-launch-server/content-launch-delegate.h diff --git a/examples/all-clusters-app/linux/include/tv-callbacks.cpp b/examples/all-clusters-app/linux/include/tv-callbacks.cpp index 79a6ee8413daac..47108ec588eff1 100644 --- a/examples/all-clusters-app/linux/include/tv-callbacks.cpp +++ b/examples/all-clusters-app/linux/include/tv-callbacks.cpp @@ -22,7 +22,20 @@ ******************************************************************************* ******************************************************************************/ +#include +#include + bool lowPowerClusterSleep() { return true; } + +uint32_t contentLauncherClusterGetSupportedStreamingProtocols() +{ + return 0; +} + +std::list contentLauncherClusterGetAcceptsHeaderList() +{ + return { "example", "example" }; +} diff --git a/examples/tv-app/android/java/ContentLauncherManager.cpp b/examples/tv-app/android/java/ContentLauncherManager.cpp index 9f7233f50c961b..8946c8856097aa 100644 --- a/examples/tv-app/android/java/ContentLauncherManager.cpp +++ b/examples/tv-app/android/java/ContentLauncherManager.cpp @@ -45,206 +45,9 @@ using namespace chip; ContentLauncherManager ContentLauncherManager::sInstance; -namespace { - -class ContentLauncherAttrAccess : public app::AttributeAccessInterface -{ -public: - ContentLauncherAttrAccess() : app::AttributeAccessInterface(Optional::Missing(), app::Clusters::ContentLauncher::Id) - {} - - CHIP_ERROR Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) override - { - if (aPath.mAttributeId == app::Clusters::ContentLauncher::Attributes::AcceptsHeaderList::Id) - { - return ContentLauncherMgr().GetAcceptsHeader(aEncoder); - } - else if (aPath.mAttributeId == app::Clusters::ContentLauncher::Attributes::SupportedStreamingProtocols::Id) - { - return ContentLauncherMgr().GetSupportedStreamingTypes(aEncoder); - } - - return CHIP_NO_ERROR; - } -}; - -ContentLauncherAttrAccess gContentLauncherAttrAccess; - -} // anonymous namespace - -/** @brief Content Launch Cluster Init - * - * This function is called when a specific cluster is initialized. It gives the - * application an opportunity to take care of cluster initialization procedures. - * It is called exactly once for each endpoint where cluster is present. - * - * @param endpoint Ver.: always - * - */ -void emberAfContentLauncherClusterInitCallback(EndpointId endpoint) -{ - static bool attrAccessRegistered = false; - if (!attrAccessRegistered) - { - registerAttributeAccessOverride(&gContentLauncherAttrAccess); - attrAccessRegistered = true; - } -} - -ContentLaunchResponse contentLauncherClusterLaunchContent(chip::EndpointId endpointId, - std::list parameterList, bool autoplay, - const chip::CharSpan & data) -{ - return ContentLauncherMgr().LaunchContent(endpointId, parameterList, autoplay, data); -} - -ContentLaunchResponse contentLauncherClusterLaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, - ContentLaunchBrandingInformation & brandingInformation) -{ - return ContentLauncherMgr().LaunchUrl(contentUrl, displayString, brandingInformation); -} - -void ContentLauncherManager::InitializeWithObjects(jobject managerObject) -{ - JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for ContentLauncherManager")); - - mContentLauncherManagerObject = env->NewGlobalRef(managerObject); - VerifyOrReturn(mContentLauncherManagerObject != nullptr, ChipLogError(Zcl, "Failed to NewGlobalRef ContentLauncherManager")); - - jclass ContentLauncherClass = env->GetObjectClass(managerObject); - VerifyOrReturn(ContentLauncherClass != nullptr, ChipLogError(Zcl, "Failed to get ContentLauncherManager Java class")); - - mGetAcceptsHeaderMethod = env->GetMethodID(ContentLauncherClass, "getAcceptsHeader", "()[Ljava/lang/String;"); - if (mGetAcceptsHeaderMethod == nullptr) - { - ChipLogError(Zcl, "Failed to access MediaInputManager 'getInputList' method"); - env->ExceptionClear(); - } - - mGetSupportedStreamingTypesMethod = env->GetMethodID(ContentLauncherClass, "getSupportedStreamingTypes", "()[I"); - if (mGetSupportedStreamingTypesMethod == nullptr) - { - ChipLogError(Zcl, "Failed to access MediaInputManager 'getSupportedStreamingTypes' method"); - env->ExceptionClear(); - } - - mLaunchContentMethod = env->GetMethodID( - ContentLauncherClass, "launchContent", - "([Lcom/tcl/chip/tvapp/ContentLaunchSearchParameter;ZLjava/lang/String;)Lcom/tcl/chip/tvapp/ContentLaunchResponse;"); - if (mLaunchContentMethod == nullptr) - { - ChipLogError(Zcl, "Failed to access MediaInputManager 'launchContent' method"); - env->ExceptionClear(); - } - - mLaunchUrlMethod = env->GetMethodID(ContentLauncherClass, "launchUrl", - "(Ljava/lang/String;Ljava/lang/String;Lcom/tcl/chip/tvapp/" - "ContentLaunchBrandingInformation;)Lcom/tcl/chip/tvapp/ContentLaunchResponse;"); - if (mLaunchUrlMethod == nullptr) - { - ChipLogError(AppServer, "Failed to access 'launchUrl' method"); - env->ExceptionClear(); - } -} - -CHIP_ERROR ContentLauncherManager::GetAcceptsHeader(chip::app::AttributeValueEncoder & aEncoder) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - - ChipLogProgress(Zcl, "Received ContentLauncherManager::GetAcceptsHeader"); - VerifyOrExit(mContentLauncherManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - VerifyOrExit(mGetAcceptsHeaderMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); - - return aEncoder.EncodeList([this, env](const auto & encoder) -> CHIP_ERROR { - jobjectArray headersArray = (jobjectArray) env->CallObjectMethod(mContentLauncherManagerObject, mGetAcceptsHeaderMethod); - if (env->ExceptionCheck()) - { - ChipLogError(Zcl, "Java exception in ContentLauncherManager::GetAcceptsHeader"); - env->ExceptionDescribe(); - env->ExceptionClear(); - return CHIP_ERROR_INCORRECT_STATE; - } - - jint size = env->GetArrayLength(headersArray); - for (int i = 0; i < size; i++) - { - jstring acceptsheader = (jstring) env->GetObjectArrayElement(headersArray, i); - if (acceptsheader != nullptr) - { - JniUtfString header(env, acceptsheader); - - chip::ByteSpan bHeader((const uint8_t *) (header.c_str()), (size_t)(header.size())); - ReturnErrorOnFailure(encoder.Encode(bHeader)); - - // Todo: should be chanSpan? - // ReturnErrorOnFailure(encoder.Encode(header.charSpan())); - } - } - - return CHIP_NO_ERROR; - }); - -exit: - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "ContentLauncherManager::GetAcceptsHeader status error: %s", err.AsString()); - } - - return err; -} - -CHIP_ERROR ContentLauncherManager::GetSupportedStreamingTypes(chip::app::AttributeValueEncoder & aEncoder) -{ - CHIP_ERROR err = CHIP_NO_ERROR; - JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); - - ChipLogProgress(Zcl, "Received ContentLauncherManager::GetSupportedStreamingTypes"); - VerifyOrExit(mContentLauncherManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - VerifyOrExit(mGetSupportedStreamingTypesMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); - VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); - - return aEncoder.EncodeList([this, env](const auto & encoder) -> CHIP_ERROR { - jintArray typesArray = (jintArray) env->CallObjectMethod(mContentLauncherManagerObject, mGetSupportedStreamingTypesMethod); - if (env->ExceptionCheck()) - { - ChipLogError(Zcl, "Java exception in ContentLauncherManager::GetSupportedStreamingTypes"); - env->ExceptionDescribe(); - env->ExceptionClear(); - return CHIP_ERROR_INCORRECT_STATE; - } - - jboolean isCopy = JNI_FALSE; - jint * ptypes = env->GetIntArrayElements(typesArray, &isCopy); - jint size = env->GetArrayLength(typesArray); - - CHIP_ERROR err = CHIP_NO_ERROR; - for (int i = 0; i < size; i++) - { - err = encoder.Encode(static_cast(ptypes[i])); - if (err != CHIP_NO_ERROR) - { - break; - } - } - env->ReleaseIntArrayElements(typesArray, ptypes, 0); - return err; - }); - -exit: - if (err != CHIP_NO_ERROR) - { - ChipLogError(Zcl, "ContentLauncherManager::GetAcceptsHeader status error: %s", err.AsString()); - } - - return err; -} - -ContentLaunchResponse ContentLauncherManager::LaunchContent(chip::EndpointId endpointId, - std::list parameterList, bool autoplay, - const chip::CharSpan & data) +ContentLaunchResponse ContentLauncherManager::HandleLaunchContent(chip::EndpointId endpointId, + std::list parameterList, bool autoplay, + const chip::CharSpan & data) { ContentLaunchResponse response; CHIP_ERROR err = CHIP_NO_ERROR; @@ -297,8 +100,9 @@ ContentLaunchResponse ContentLauncherManager::LaunchContent(chip::EndpointId end return response; } -ContentLaunchResponse ContentLauncherManager::LaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, - ContentLaunchBrandingInformation & brandingInformation) +ContentLaunchResponse ContentLauncherManager::HandleLaunchUrl(const chip::CharSpan & contentUrl, + const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation) { ContentLaunchResponse response; CHIP_ERROR err = CHIP_NO_ERROR; @@ -351,3 +155,124 @@ ContentLaunchResponse ContentLauncherManager::LaunchUrl(const chip::CharSpan & c return response; } + +std::list ContentLauncherManager::HandleGetAcceptsHeaderList() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + std::list acceptedHeadersList; + + ChipLogProgress(Zcl, "Received ContentLauncherManager::GetAcceptsHeader"); + VerifyOrExit(mContentLauncherManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mGetAcceptsHeaderMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + { + jobjectArray acceptedHeadersArray = + (jobjectArray) env->CallObjectMethod(mContentLauncherManagerObject, mGetAcceptsHeaderMethod); + if (env->ExceptionCheck()) + { + ChipLogError(Zcl, "Java exception in ContentLauncherManager::GetAcceptsHeader"); + env->ExceptionDescribe(); + env->ExceptionClear(); + err = CHIP_ERROR_INCORRECT_STATE; + goto exit; + } + + jint size = env->GetArrayLength(acceptedHeadersArray); + for (int i = 0; i < size; i++) + { + + jstring jAcceptedHeader = (jstring) env->GetObjectArrayElement(acceptedHeadersArray, i); + const char * convertedValue = (env)->GetStringUTFChars(jAcceptedHeader, JNI_FALSE); + std::string acceptedHeader = std::string(convertedValue, strlen(convertedValue)); + acceptedHeadersList.push_front(acceptedHeader); + } + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "ContentLauncherManager::GetAcceptsHeader status error: %s", err.AsString()); + } + + return acceptedHeadersList; +} + +uint32_t ContentLauncherManager::HandleGetSupportedStreamingProtocols() +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + uint32_t supportedStreamingProtocols = 0; + + ChipLogProgress(Zcl, "Received ContentLauncherManager::GetSupportedStreamingProtocols"); + VerifyOrExit(mContentLauncherManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mGetSupportedStreamingProtocolsMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + { + jint jSupportedStreamingProtocols = + env->CallIntMethod(mContentLauncherManagerObject, mGetSupportedStreamingProtocolsMethod); + supportedStreamingProtocols = (uint32_t) jSupportedStreamingProtocols; + if (env->ExceptionCheck()) + { + ChipLogError(Zcl, "Java exception in ContentLauncherManager::GetAcceptsHeader"); + env->ExceptionDescribe(); + env->ExceptionClear(); + err = CHIP_ERROR_INCORRECT_STATE; + goto exit; + } + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "ContentLauncherManager::GetSupportedStreamingProtocols status error: %s", err.AsString()); + } + + return supportedStreamingProtocols; +} + +void ContentLauncherManager::InitializeWithObjects(jobject managerObject) +{ + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for ContentLauncherManager")); + + mContentLauncherManagerObject = env->NewGlobalRef(managerObject); + VerifyOrReturn(mContentLauncherManagerObject != nullptr, ChipLogError(Zcl, "Failed to NewGlobalRef ContentLauncherManager")); + + jclass ContentLauncherClass = env->GetObjectClass(managerObject); + VerifyOrReturn(ContentLauncherClass != nullptr, ChipLogError(Zcl, "Failed to get ContentLauncherManager Java class")); + + mGetAcceptsHeaderMethod = env->GetMethodID(ContentLauncherClass, "getAcceptsHeader", "()[Ljava/lang/String;"); + if (mGetAcceptsHeaderMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access MediaInputManager 'getInputList' method"); + env->ExceptionClear(); + } + + mGetSupportedStreamingProtocolsMethod = env->GetMethodID(ContentLauncherClass, "getSupportedStreamingProtocols", "()[I"); + if (mGetSupportedStreamingProtocolsMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access MediaInputManager 'getSupportedStreamingProtocols' method"); + env->ExceptionClear(); + } + + mLaunchContentMethod = env->GetMethodID( + ContentLauncherClass, "launchContent", + "([Lcom/tcl/chip/tvapp/ContentLaunchSearchParameter;ZLjava/lang/String;)Lcom/tcl/chip/tvapp/ContentLaunchResponse;"); + if (mLaunchContentMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access MediaInputManager 'launchContent' method"); + env->ExceptionClear(); + } + + mLaunchUrlMethod = env->GetMethodID(ContentLauncherClass, "launchUrl", + "(Ljava/lang/String;Ljava/lang/String;Lcom/tcl/chip/tvapp/" + "ContentLaunchBrandingInformation;)Lcom/tcl/chip/tvapp/ContentLaunchResponse;"); + if (mLaunchUrlMethod == nullptr) + { + ChipLogError(AppServer, "Failed to access 'launchUrl' method"); + env->ExceptionClear(); + } +} diff --git a/examples/tv-app/android/java/ContentLauncherManager.h b/examples/tv-app/android/java/ContentLauncherManager.h index 01b843530a2e77..5ca9a268949bf0 100644 --- a/examples/tv-app/android/java/ContentLauncherManager.h +++ b/examples/tv-app/android/java/ContentLauncherManager.h @@ -21,31 +21,32 @@ #include #include -#include +#include #include #include #include -class ContentLauncherManager +class ContentLauncherManager : public chip::app::Clusters::ContentLauncher::Delegate { public: void InitializeWithObjects(jobject managerObject); - CHIP_ERROR GetAcceptsHeader(chip::app::AttributeValueEncoder & aEncoder); - CHIP_ERROR GetSupportedStreamingTypes(chip::app::AttributeValueEncoder & aEncoder); - ContentLaunchResponse LaunchContent(chip::EndpointId endpointId, std::list parameterList, bool autoplay, - const chip::CharSpan & data); - ContentLaunchResponse LaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, - ContentLaunchBrandingInformation & brandingInformation); + + ContentLaunchResponse HandleLaunchContent(chip::EndpointId endpointId, std::list parameterList, + bool autoplay, const chip::CharSpan & data) override; + ContentLaunchResponse HandleLaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation) override; + std::list HandleGetAcceptsHeaderList() override; + uint32_t HandleGetSupportedStreamingProtocols() override; private: friend ContentLauncherManager & ContentLauncherMgr(); static ContentLauncherManager sInstance; - jobject mContentLauncherManagerObject = nullptr; - jmethodID mGetAcceptsHeaderMethod = nullptr; - jmethodID mGetSupportedStreamingTypesMethod = nullptr; - jmethodID mLaunchContentMethod = nullptr; - jmethodID mLaunchUrlMethod = nullptr; + jobject mContentLauncherManagerObject = nullptr; + jmethodID mGetAcceptsHeaderMethod = nullptr; + jmethodID mGetSupportedStreamingProtocolsMethod = nullptr; + jmethodID mLaunchContentMethod = nullptr; + jmethodID mLaunchUrlMethod = nullptr; }; inline ContentLauncherManager & ContentLauncherMgr() diff --git a/examples/tv-app/linux/include/cluster-init.cpp b/examples/tv-app/linux/include/cluster-init.cpp index 8ec354664ee23c..54d6b1dab412a0 100644 --- a/examples/tv-app/linux/include/cluster-init.cpp +++ b/examples/tv-app/linux/include/cluster-init.cpp @@ -189,48 +189,6 @@ void emberAfAudioOutputClusterInitCallback(EndpointId endpoint) namespace { -class ContentLauncherAttrAccess : public app::AttributeAccessInterface -{ -public: - ContentLauncherAttrAccess() : app::AttributeAccessInterface(Optional::Missing(), app::Clusters::ContentLauncher::Id) - {} - - CHIP_ERROR Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) override - { - if (aPath.mAttributeId == app::Clusters::ContentLauncher::Attributes::AcceptsHeaderList::Id) - { - return ContentLauncherManager().proxyGetAcceptsHeader(aEncoder); - } - - return CHIP_NO_ERROR; - } -}; - -ContentLauncherAttrAccess gContentLauncherAttrAccess; - -} // anonymous namespace - -/** @brief Content Launch Cluster Init - * - * This function is called when a specific cluster is initialized. It gives the - * application an opportunity to take care of cluster initialization procedures. - * It is called exactly once for each endpoint where cluster is present. - * - * @param endpoint Ver.: always - * - */ -void emberAfContentLauncherClusterInitCallback(EndpointId endpoint) -{ - static bool attrAccessRegistered = false; - if (!attrAccessRegistered) - { - registerAttributeAccessOverride(&gContentLauncherAttrAccess); - attrAccessRegistered = true; - } -} - -namespace { - TvAttrAccess gMediaInputAttrAccess; diff --git a/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.cpp b/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.cpp index 5168261cf5e4d6..c95dc3e6e71d83 100644 --- a/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.cpp +++ b/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.cpp @@ -38,56 +38,18 @@ using namespace std; using namespace chip::AppPlatform; -CHIP_ERROR ContentLauncherManager::Init() +ContentLaunchResponse ContentLauncherManager::HandleLaunchContent(chip::EndpointId endpointId, + std::list parameterList, bool autoplay, + const chip::CharSpan & data) { - CHIP_ERROR err = CHIP_NO_ERROR; - - // TODO: Store feature map once it is supported - map featureMap; - featureMap["CS"] = true; - featureMap["UP"] = true; - featureMap["WA"] = true; - - SuccessOrExit(err); -exit: - return err; -} - -CHIP_ERROR ContentLauncherManager::proxyGetAcceptsHeader(chip::app::AttributeValueEncoder & aEncoder) -{ - ChipLogProgress(Zcl, "ContentLauncherManager::proxyGetAcceptsHeader "); - return aEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { - std::list headerExample = { "image/*", "video/*" }; - - for (string entry : headerExample) - { - ReturnErrorOnFailure(encoder.Encode(chip::CharSpan(entry.c_str(), entry.length()))); - } - return CHIP_NO_ERROR; - }); -} - -CHIP_ERROR ContentLauncherManager::proxyGetSupportedStreamingTypes(chip::app::AttributeValueEncoder & aEncoder) -{ - ChipLogProgress(Zcl, "ContentLauncherManager::proxyGetSupportedStreamingTypes "); - return aEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { - // ReturnErrorOnFailure(encoder.Encode(EMBER_ZCL_CONTENT_LAUNCH_STREAMING_TYPE_DASH)); - // ReturnErrorOnFailure(encoder.Encode(EMBER_ZCL_CONTENT_LAUNCH_STREAMING_TYPE_HLS)); - return CHIP_NO_ERROR; - }); -} - -ContentLaunchResponse ContentLauncherManager::proxyLaunchContentRequest(chip::EndpointId endpointId, - list parameterList, bool autoplay, - string data) -{ - ChipLogProgress(Zcl, "ContentLauncherManager::proxyLaunchContentRequest endpoint=%d", endpointId); + ChipLogProgress(Zcl, "ContentLauncherManager::HandleLaunchContent "); + string dataString(data.data(), data.size()); #if CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED ContentApp * app = chip::AppPlatform::AppPlatform::GetInstance().GetContentAppByEndpointId(endpointId); if (app != NULL) { - return app->GetContentLauncher()->LaunchContent(parameterList, autoplay, data); + return app->GetContentLauncher()->LaunchContent(parameterList, autoplay, dataString); } #endif // CHIP_DEVICE_CONFIG_APP_PLATFORM_ENABLED @@ -98,10 +60,15 @@ ContentLaunchResponse ContentLauncherManager::proxyLaunchContentRequest(chip::En response.status = EMBER_ZCL_CONTENT_LAUNCH_STATUS_SUCCESS; return response; } -ContentLaunchResponse ContentLauncherManager::proxyLaunchUrlRequest(string contentUrl, string displayString, - ContentLaunchBrandingInformation brandingInformation) + +ContentLaunchResponse ContentLauncherManager::HandleLaunchUrl(const chip::CharSpan & contentUrl, + const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation) { - ChipLogProgress(Zcl, "ContentLauncherManager::proxyLaunchUrlRequest contentUrl=%s ", contentUrl.c_str()); + ChipLogProgress(Zcl, "ContentLauncherManager::HandleLaunchUrl"); + + string contentUrlString(contentUrl.data(), contentUrl.size()); + string displayStringString(displayString.data(), displayString.size()); // TODO: Insert code here ContentLaunchResponse response; @@ -111,18 +78,15 @@ ContentLaunchResponse ContentLauncherManager::proxyLaunchUrlRequest(string conte return response; } -ContentLaunchResponse contentLauncherClusterLaunchContent(chip::EndpointId endpointId, - std::list parameterList, bool autoplay, - const chip::CharSpan & data) +std::list ContentLauncherManager::HandleGetAcceptsHeaderList() { - string dataString(data.data(), data.size()); - return ContentLauncherManager().proxyLaunchContentRequest(endpointId, parameterList, autoplay, dataString); + ChipLogProgress(Zcl, "ContentLauncherManager::HandleGetAcceptsHeaderList"); + return { "example", "example" }; } -ContentLaunchResponse contentLauncherClusterLaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, - ContentLaunchBrandingInformation & brandingInformation) +uint32_t ContentLauncherManager::HandleGetSupportedStreamingProtocols() { - string contentUrlString(contentUrl.data(), contentUrl.size()); - string displayStringString(displayString.data(), displayString.size()); - return ContentLauncherManager().proxyLaunchUrlRequest(contentUrlString, displayStringString, brandingInformation); + ChipLogProgress(Zcl, "ContentLauncherManager::HandleGetSupportedStreamingProtocols"); + uint32_t streamingProtocols = 0; + return streamingProtocols; } diff --git a/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.h b/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.h index aedeb2cc134b98..f0a3e7996b9266 100644 --- a/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.h +++ b/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.h @@ -20,21 +20,20 @@ #include #include -#include +#include #include #include #include #include -class ContentLauncherManager +class ContentLauncherManager : public chip::app::Clusters::ContentLauncher::Delegate { public: - CHIP_ERROR Init(); - CHIP_ERROR proxyGetAcceptsHeader(chip::app::AttributeValueEncoder & aEncoder); - CHIP_ERROR proxyGetSupportedStreamingTypes(chip::app::AttributeValueEncoder & aEncoder); - ContentLaunchResponse proxyLaunchContentRequest(chip::EndpointId endpointId, std::list parameterList, - bool autoplay, std::string data); - ContentLaunchResponse proxyLaunchUrlRequest(std::string contentUrl, std::string displayString, - ContentLaunchBrandingInformation brandingInformation); + ContentLaunchResponse HandleLaunchContent(chip::EndpointId endpointId, std::list parameterList, + bool autoplay, const chip::CharSpan & data) override; + ContentLaunchResponse HandleLaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation) override; + std::list HandleGetAcceptsHeaderList() override; + uint32_t HandleGetSupportedStreamingProtocols() override; }; diff --git a/examples/tv-app/linux/main.cpp b/examples/tv-app/linux/main.cpp index 35ca1a8d5fabf2..1aa352d280a00a 100644 --- a/examples/tv-app/linux/main.cpp +++ b/examples/tv-app/linux/main.cpp @@ -52,6 +52,11 @@ bool emberAfBasicClusterMfgSpecificPingCallback(chip::app::CommandHandler * comm return true; } +namespace { +static ContentLauncherManager contentLauncherManager; +constexpr chip::EndpointId kContentLauncherEndpoint = 1; +} // namespace + int main(int argc, char * argv[]) { CHIP_ERROR err = CHIP_NO_ERROR; @@ -72,10 +77,6 @@ int main(int argc, char * argv[]) err = AudioOutputManager().Init(); SuccessOrExit(err); - // Init Content Launcher Manager - err = ContentLauncherManager().Init(); - SuccessOrExit(err); - // Init Media Input Manager err = MediaInputManager().Init(); SuccessOrExit(err); @@ -115,3 +116,9 @@ int main(int argc, char * argv[]) } return 0; } + +void emberAfContentLauncherClusterInitCallback(EndpointId endpoint) +{ + ChipLogProgress(Zcl, "TV Linux App: ContentLauncherManager::SetDelegate"); + chip::app::Clusters::ContentLauncher::SetDelegate(kContentLauncherEndpoint, &contentLauncherManager); +} diff --git a/examples/tv-casting-app/linux/main.cpp b/examples/tv-casting-app/linux/main.cpp index 7cd5934347e967..2aee5c5b3422ba 100644 --- a/examples/tv-casting-app/linux/main.cpp +++ b/examples/tv-casting-app/linux/main.cpp @@ -30,6 +30,9 @@ #include #include +#include +#include + using namespace chip; using namespace chip::Controller; using namespace chip::Credentials; diff --git a/src/app/clusters/content-launch-server/content-launch-delegate.h b/src/app/clusters/content-launch-server/content-launch-delegate.h new file mode 100644 index 00000000000000..0d668bacbf72aa --- /dev/null +++ b/src/app/clusters/content-launch-server/content-launch-delegate.h @@ -0,0 +1,63 @@ +/* + * + * Copyright (c) 2021 Project CHIP Authors + * All rights reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#pragma once + +#include +#include +#include + +#include +#include +#include + +struct ContentLaunchResponse +{ + CHIP_ERROR err; + chip::CharSpan data; + chip::app::Clusters::ContentLauncher::ContentLaunchStatus status; +}; + +namespace chip { +namespace app { +namespace Clusters { +namespace ContentLauncher { + +/** @brief + * Defines methods for implementing application-specific logic for the Content Launcher Cluster. + */ +class Delegate +{ +public: + virtual ContentLaunchResponse HandleLaunchContent(chip::EndpointId endpointId, std::list parameterList, + bool autoplay, const chip::CharSpan & data) = 0; + + virtual ContentLaunchResponse HandleLaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation) = 0; + + virtual std::list HandleGetAcceptsHeaderList() = 0; + + virtual uint32_t HandleGetSupportedStreamingProtocols() = 0; + + virtual ~Delegate() = default; +}; + +} // namespace ContentLauncher +} // namespace Clusters +} // namespace app +} // namespace chip diff --git a/src/app/clusters/content-launch-server/content-launch-server.cpp b/src/app/clusters/content-launch-server/content-launch-server.cpp index e507771575567b..7bdd2b0f2b80fb 100644 --- a/src/app/clusters/content-launch-server/content-launch-server.cpp +++ b/src/app/clusters/content-launch-server/content-launch-server.cpp @@ -55,39 +55,92 @@ using namespace chip; using namespace chip::app; -ContentLaunchResponse contentLauncherClusterLaunchContent(chip::EndpointId endpointId, - std::list parameterList, bool autoplay, - const chip::CharSpan & data); +// ----------------------------------------------------------------------------- +// Delegate Implementation -ContentLaunchResponse contentLauncherClusterLaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, - ContentLaunchBrandingInformation & brandingInformation); +using chip::app::Clusters::ContentLauncher::Delegate; + +namespace { + +Delegate * gDelegateTable[EMBER_AF_CONTENT_LAUNCH_CLUSTER_SERVER_ENDPOINT_COUNT] = { nullptr }; + +Delegate * GetDelegate(EndpointId endpoint) +{ + uint16_t ep = emberAfFindClusterServerEndpointIndex(endpoint, chip::app::Clusters::ContentLauncher::Id); + return (ep == 0xFFFF ? NULL : gDelegateTable[ep]); +} + +bool SendStatusIfDelegateNull(EndpointId endpoint) +{ + if (GetDelegate(endpoint) == nullptr) + { + ChipLogError(Zcl, "No ContentLauncher Delegate set for ep:%" PRIu16, endpoint); + emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_UNSUP_COMMAND); + return true; + } + return false; +} +} // namespace + +namespace chip { +namespace app { +namespace Clusters { +namespace ContentLauncher { + +void SetDelegate(EndpointId endpoint, Delegate * delegate) +{ + uint16_t ep = emberAfFindClusterServerEndpointIndex(endpoint, chip::app::Clusters::ContentLauncher::Id); + if (ep != 0xFFFF) + { + gDelegateTable[ep] = delegate; + } + else + { + } +} + +} // namespace ContentLauncher +} // namespace Clusters +} // namespace app +} // namespace chip + +// ----------------------------------------------------------------------------- +// Attribute Accessor Implementation namespace { class ContentLauncherAttrAccess : public app::AttributeAccessInterface { public: - ContentLauncherAttrAccess() : app::AttributeAccessInterface(Optional::Missing(), app::Clusters::ContentLauncher::Id) + ContentLauncherAttrAccess() : + app::AttributeAccessInterface(Optional::Missing(), chip::app::Clusters::ContentLauncher::Id) {} CHIP_ERROR Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) override; private: - CHIP_ERROR ReadAcceptsHeaderAttribute(app::AttributeValueEncoder & aEncoder); - CHIP_ERROR ReadSupportedStreamingProtocols(app::AttributeValueEncoder & aEncoder); + CHIP_ERROR ReadAcceptsHeaderAttribute(app::AttributeValueEncoder & aEncoder, Delegate * delegate); + CHIP_ERROR ReadSupportedStreamingProtocols(app::AttributeValueEncoder & aEncoder, Delegate * delegate); }; ContentLauncherAttrAccess gContentLauncherAttrAccess; CHIP_ERROR ContentLauncherAttrAccess::Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) { + EndpointId endpoint = aPath.mEndpointId; + Delegate * delegate = GetDelegate(endpoint); + if (SendStatusIfDelegateNull(endpoint)) + { + return CHIP_NO_ERROR; + } + switch (aPath.mAttributeId) { case app::Clusters::ContentLauncher::Attributes::AcceptsHeaderList::Id: { - return ReadAcceptsHeaderAttribute(aEncoder); + return ReadAcceptsHeaderAttribute(aEncoder, delegate); } case app::Clusters::ContentLauncher::Attributes::SupportedStreamingProtocols::Id: { - return ReadSupportedStreamingProtocols(aEncoder); + return ReadSupportedStreamingProtocols(aEncoder, delegate); } default: { break; @@ -96,38 +149,49 @@ CHIP_ERROR ContentLauncherAttrAccess::Read(const app::ConcreteReadAttributePath return CHIP_NO_ERROR; } -CHIP_ERROR ContentLauncherAttrAccess::ReadAcceptsHeaderAttribute(app::AttributeValueEncoder & aEncoder) +CHIP_ERROR ContentLauncherAttrAccess::ReadAcceptsHeaderAttribute(app::AttributeValueEncoder & aEncoder, Delegate * delegate) { - return aEncoder.EncodeList([](const auto & encoder) -> CHIP_ERROR { - for (uint8_t index = 0; index < 2; index++) + std::list acceptsHeaderList = delegate->HandleGetAcceptsHeaderList(); + return aEncoder.EncodeList([acceptsHeaderList](const auto & encoder) -> CHIP_ERROR { + for (std::string acceptedHeader : acceptsHeaderList) { - CharSpan span("example", strlen("example")); + CharSpan span(acceptedHeader.c_str(), strlen(acceptedHeader.c_str())); ReturnErrorOnFailure(encoder.Encode(span)); } return CHIP_NO_ERROR; }); } -CHIP_ERROR ContentLauncherAttrAccess::ReadSupportedStreamingProtocols(app::AttributeValueEncoder & aEncoder) +CHIP_ERROR ContentLauncherAttrAccess::ReadSupportedStreamingProtocols(app::AttributeValueEncoder & aEncoder, Delegate * delegate) { - uint8_t streamingProtocols = 0; + uint32_t streamingProtocols = delegate->HandleGetSupportedStreamingProtocols(); return aEncoder.Encode(streamingProtocols); } } // anonymous namespace +// ----------------------------------------------------------------------------- +// Matter Framework Callbacks Implementation + bool emberAfContentLauncherClusterLaunchContentCallback( chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, const chip::app::Clusters::ContentLauncher::Commands::LaunchContent::DecodableType & commandData) { CHIP_ERROR err = CHIP_NO_ERROR; chip::app::Clusters::ContentLauncher::Commands::LaunchContentResponse::Type response; + EndpointId endpoint = commandPath.mEndpointId; auto & autoplay = commandData.autoPlay; auto & data = commandData.data; std::list parameterList; - ContentLaunchResponse resp = contentLauncherClusterLaunchContent(emberAfCurrentEndpoint(), parameterList, autoplay, data); + Delegate * delegate = GetDelegate(endpoint); + if (SendStatusIfDelegateNull(endpoint)) + { + return true; + } + + ContentLaunchResponse resp = delegate->HandleLaunchContent(emberAfCurrentEndpoint(), parameterList, autoplay, data); VerifyOrExit(resp.err == CHIP_NO_ERROR, err = resp.err); response.contentLaunchStatus = resp.status; @@ -153,12 +217,19 @@ bool emberAfContentLauncherClusterLaunchURLCallback( { CHIP_ERROR err = CHIP_NO_ERROR; chip::app::Clusters::ContentLauncher::Commands::LaunchURLResponse::Type response; + EndpointId endpoint = commandPath.mEndpointId; auto & contentUrl = commandData.contentURL; auto & displayString = commandData.displayString; ContentLaunchBrandingInformation brandingInformation; - ContentLaunchResponse resp = contentLauncherClusterLaunchUrl(contentUrl, displayString, brandingInformation); + Delegate * delegate = GetDelegate(endpoint); + if (SendStatusIfDelegateNull(endpoint)) + { + return true; + } + + ContentLaunchResponse resp = delegate->HandleLaunchUrl(contentUrl, displayString, brandingInformation); VerifyOrExit(resp.err == CHIP_NO_ERROR, err = resp.err); response.contentLaunchStatus = resp.status; diff --git a/src/app/clusters/content-launch-server/content-launch-server.h b/src/app/clusters/content-launch-server/content-launch-server.h index 2d4934e5fc0eb9..7a0911078ffad5 100644 --- a/src/app/clusters/content-launch-server/content-launch-server.h +++ b/src/app/clusters/content-launch-server/content-launch-server.h @@ -17,12 +17,18 @@ #pragma once +#include "content-launch-delegate.h" #include #include -struct ContentLaunchResponse -{ - CHIP_ERROR err; - chip::CharSpan data; - chip::app::Clusters::ContentLauncher::ContentLaunchStatus status; -}; +namespace chip { +namespace app { +namespace Clusters { +namespace ContentLauncher { + +void SetDelegate(EndpointId endpoint, Delegate * delegate); + +} // namespace ContentLauncher +} // namespace Clusters +} // namespace app +} // namespace chip