From 1378455c931184e65c38612b1a1a92fb34af629f Mon Sep 17 00:00:00 2001 From: "Hui.Li-TCL" Date: Tue, 30 Nov 2021 11:26:02 +0800 Subject: [PATCH] Added Content launcher low power media playback and tvchannel cluster to java (#12250) * added ContentLauncherManager cluster * added LowPowerManager cluster * added MediaPlaybackManager cluster * added TvChannelManager cluster * Restyled by whitespace * Restyled by google-java-format * Restyled by clang-format * Restyled by gn * fix content launcher response error * Restyled by clang-format * move most response code to content-launch-server as other clusters * Restyled by clang-format * fix linux build error Co-authored-by: Restyled.io --- .../tcl/chip/chiptvserver/MainActivity.java | 8 + examples/tv-app/android/BUILD.gn | 29 +- .../tv-app/android/include/cluster-init.cpp | 76 --- .../ContentLauncherManager.cpp | 138 ------ .../content-launcher/ContentLauncherManager.h | 44 -- .../include/low-power/LowPowerManager.cpp | 25 - .../media-playback/MediaPlaybackManager.cpp | 79 ---- .../include/tv-channel/TvChannelManager.cpp | 84 ---- .../android/java/ContentLauncherManager.cpp | 352 ++++++++++++++ .../android/java/ContentLauncherManager.h | 54 +++ .../tv-app/android/java/LowPowerManager.cpp | 76 +++ .../LowPowerManager.h} | 26 +- .../android/java/MediaPlaybackManager.cpp | 217 +++++++++ .../MediaPlaybackManager.h | 22 +- examples/tv-app/android/java/TVApp-JNI.cpp | 24 + .../tv-app/android/java/TvChannelManager.cpp | 444 ++++++++++++++++++ .../tv-app/android/java/TvChannelManager.h | 57 +++ .../ContentLaunchBrandingInformation.java | 62 +++ .../tcl/chip/tvapp/ContentLaunchManager.java | 59 +++ .../chip/tvapp/ContentLaunchManagerStub.java | 42 ++ .../tcl/chip/tvapp/ContentLaunchResponse.java | 19 + .../tvapp/ContentLaunchSearchParameter.java | 71 +++ .../com/tcl/chip/tvapp/LowPowerManager.java | 22 + .../tcl/chip/tvapp/LowPowerManagerStub.java | 14 + .../tcl/chip/tvapp/MediaPlaybackManager.java | 125 +++++ .../chip/tvapp/MediaPlaybackManagerStub.java | 118 +++++ .../java/src/com/tcl/chip/tvapp/TvApp.java | 8 + .../src/com/tcl/chip/tvapp/TvChannelInfo.java | 56 +++ .../tcl/chip/tvapp/TvChannelLineupInfo.java | 56 +++ .../com/tcl/chip/tvapp/TvChannelManager.java | 33 ++ .../tcl/chip/tvapp/TvChannelManagerStub.java | 67 +++ .../ContentLauncherManager.cpp | 41 +- .../content-launcher/ContentLauncherManager.h | 6 +- .../content-launch-server.cpp | 68 ++- .../content-launch-server.h | 12 +- 35 files changed, 2117 insertions(+), 517 deletions(-) delete mode 100644 examples/tv-app/android/include/content-launcher/ContentLauncherManager.cpp delete mode 100644 examples/tv-app/android/include/content-launcher/ContentLauncherManager.h delete mode 100644 examples/tv-app/android/include/low-power/LowPowerManager.cpp delete mode 100644 examples/tv-app/android/include/media-playback/MediaPlaybackManager.cpp delete mode 100644 examples/tv-app/android/include/tv-channel/TvChannelManager.cpp create mode 100644 examples/tv-app/android/java/ContentLauncherManager.cpp create mode 100644 examples/tv-app/android/java/ContentLauncherManager.h create mode 100644 examples/tv-app/android/java/LowPowerManager.cpp rename examples/tv-app/android/{include/tv-channel/TvChannelManager.h => java/LowPowerManager.h} (63%) create mode 100644 examples/tv-app/android/java/MediaPlaybackManager.cpp rename examples/tv-app/android/{include/media-playback => java}/MediaPlaybackManager.h (57%) create mode 100644 examples/tv-app/android/java/TvChannelManager.cpp create mode 100644 examples/tv-app/android/java/TvChannelManager.h create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchBrandingInformation.java create mode 100755 examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchManager.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchManagerStub.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchResponse.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchSearchParameter.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/LowPowerManager.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/LowPowerManagerStub.java create mode 100755 examples/tv-app/android/java/src/com/tcl/chip/tvapp/MediaPlaybackManager.java create mode 100755 examples/tv-app/android/java/src/com/tcl/chip/tvapp/MediaPlaybackManagerStub.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelInfo.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelLineupInfo.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelManager.java create mode 100644 examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelManagerStub.java rename examples/tv-app/android/include/low-power/LowPowerManager.h => src/app/clusters/content-launch-server/content-launch-server.h (73%) diff --git a/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/MainActivity.java b/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/MainActivity.java index 9d51e2ff71492e..41014fe7f100ea 100644 --- a/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/MainActivity.java +++ b/examples/tv-app/android/App/app/src/main/java/com/tcl/chip/chiptvserver/MainActivity.java @@ -15,9 +15,13 @@ import chip.setuppayload.DiscoveryCapability; import chip.setuppayload.SetupPayload; import chip.setuppayload.SetupPayloadParser; +import com.tcl.chip.tvapp.ContentLaunchManagerStub; import com.tcl.chip.tvapp.KeypadInputManagerStub; +import com.tcl.chip.tvapp.LowPowerManagerStub; import com.tcl.chip.tvapp.MediaInputManagerStub; +import com.tcl.chip.tvapp.MediaPlaybackManagerStub; import com.tcl.chip.tvapp.TvApp; +import com.tcl.chip.tvapp.TvChannelManagerStub; import com.tcl.chip.tvapp.WakeOnLanManagerStub; import java.util.HashSet; @@ -38,6 +42,10 @@ protected void onCreate(Bundle savedInstanceState) { tvApp.setKeypadInputManager(new KeypadInputManagerStub()); tvApp.setWakeOnLanManager(new WakeOnLanManagerStub()); tvApp.setMediaInputManager(new MediaInputManagerStub()); + tvApp.setContentLaunchManager(new ContentLaunchManagerStub()); + tvApp.setLowPowerManager(new LowPowerManagerStub()); + tvApp.setMediaPlaybackManager(new MediaPlaybackManagerStub()); + tvApp.setTvChannelManager(new TvChannelManagerStub()); AndroidChipPlatform chipPlatform = new AndroidChipPlatform( diff --git a/examples/tv-app/android/BUILD.gn b/examples/tv-app/android/BUILD.gn index c7da6f1190a258..5be448221c55dc 100644 --- a/examples/tv-app/android/BUILD.gn +++ b/examples/tv-app/android/BUILD.gn @@ -34,23 +34,23 @@ shared_library("jni") { "include/audio-output/AudioOutputManager.h", "include/cluster-change-attribute.cpp", "include/cluster-init.cpp", - "include/content-launcher/ContentLauncherManager.cpp", - "include/content-launcher/ContentLauncherManager.h", "include/endpoint-configuration/EndpointConfigurationStorage.cpp", "include/endpoint-configuration/EndpointConfigurationStorage.h", - "include/low-power/LowPowerManager.cpp", - "include/low-power/LowPowerManager.h", - "include/media-playback/MediaPlaybackManager.cpp", - "include/media-playback/MediaPlaybackManager.h", "include/target-navigator/TargetNavigatorManager.cpp", "include/target-navigator/TargetNavigatorManager.h", - "include/tv-channel/TvChannelManager.cpp", - "include/tv-channel/TvChannelManager.h", + "java/ContentLauncherManager.cpp", + "java/ContentLauncherManager.h", "java/KeypadInputManager.cpp", "java/KeypadInputManager.h", + "java/LowPowerManager.cpp", + "java/LowPowerManager.h", "java/MediaInputManager.cpp", "java/MediaInputManager.h", + "java/MediaPlaybackManager.cpp", + "java/MediaPlaybackManager.h", "java/TVApp-JNI.cpp", + "java/TvChannelManager.cpp", + "java/TvChannelManager.h", "java/WakeOnLanManager.cpp", "java/WakeOnLanManager.h", ] @@ -83,12 +83,25 @@ android_library("java") { ] sources = [ + "java/src/com/tcl/chip/tvapp/ContentLaunchBrandingInformation.java", + "java/src/com/tcl/chip/tvapp/ContentLaunchManager.java", + "java/src/com/tcl/chip/tvapp/ContentLaunchManagerStub.java", + "java/src/com/tcl/chip/tvapp/ContentLaunchResponse.java", + "java/src/com/tcl/chip/tvapp/ContentLaunchSearchParameter.java", "java/src/com/tcl/chip/tvapp/KeypadInputManager.java", "java/src/com/tcl/chip/tvapp/KeypadInputManagerStub.java", + "java/src/com/tcl/chip/tvapp/LowPowerManager.java", + "java/src/com/tcl/chip/tvapp/LowPowerManagerStub.java", "java/src/com/tcl/chip/tvapp/MediaInputInfo.java", "java/src/com/tcl/chip/tvapp/MediaInputManager.java", "java/src/com/tcl/chip/tvapp/MediaInputManagerStub.java", + "java/src/com/tcl/chip/tvapp/MediaPlaybackManager.java", + "java/src/com/tcl/chip/tvapp/MediaPlaybackManagerStub.java", "java/src/com/tcl/chip/tvapp/TvApp.java", + "java/src/com/tcl/chip/tvapp/TvChannelInfo.java", + "java/src/com/tcl/chip/tvapp/TvChannelLineupInfo.java", + "java/src/com/tcl/chip/tvapp/TvChannelManager.java", + "java/src/com/tcl/chip/tvapp/TvChannelManagerStub.java", "java/src/com/tcl/chip/tvapp/WakeOnLanManager.java", "java/src/com/tcl/chip/tvapp/WakeOnLanManagerStub.java", ] diff --git a/examples/tv-app/android/include/cluster-init.cpp b/examples/tv-app/android/include/cluster-init.cpp index 2ef9a280110e15..287c7c23cb020c 100644 --- a/examples/tv-app/android/include/cluster-init.cpp +++ b/examples/tv-app/android/include/cluster-init.cpp @@ -19,9 +19,7 @@ #include "application-basic/ApplicationBasicManager.h" #include "application-launcher/ApplicationLauncherManager.h" #include "audio-output/AudioOutputManager.h" -#include "content-launcher/ContentLauncherManager.h" #include "target-navigator/TargetNavigatorManager.h" -#include "tv-channel/TvChannelManager.h" #include #include @@ -80,33 +78,6 @@ void emberAfApplicationBasicClusterInitCallback(chip::EndpointId endpoint) namespace { -TvAttrAccess - gTvChannelAttrAccess; - -} // anonymous namespace - -/** @brief Tv Channel 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 emberAfTvChannelClusterInitCallback(EndpointId endpoint) -{ - static bool attrAccessRegistered = false; - if (!attrAccessRegistered) - { - registerAttributeAccessOverride(&gTvChannelAttrAccess); - attrAccessRegistered = true; - } -} - -namespace { - TvAttrAccess gApplicationLauncherAttrAccess; @@ -161,53 +132,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); - } - - if (aPath.mAttributeId == app::Clusters::ContentLauncher::Attributes::SupportedStreamingTypes::Id) - { - return ContentLauncherManager().proxyGetSupportedStreamingTypes(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 gTargetNavigatorAttrAccess; diff --git a/examples/tv-app/android/include/content-launcher/ContentLauncherManager.cpp b/examples/tv-app/android/include/content-launcher/ContentLauncherManager.cpp deleted file mode 100644 index 001fe840623d32..00000000000000 --- a/examples/tv-app/android/include/content-launcher/ContentLauncherManager.cpp +++ /dev/null @@ -1,138 +0,0 @@ -/* - * - * 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. - */ - -#include "ContentLauncherManager.h" - -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include - -using namespace std; - -CHIP_ERROR ContentLauncherManager::Init() -{ - 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) -{ - return aEncoder.EncodeList([](const chip::app::TagBoundEncoder & encoder) -> CHIP_ERROR { - // TODO: Insert code here - char headerExample[] = "exampleHeader"; - int maximumVectorSize = 1; - - for (uint16_t i = 0; i < maximumVectorSize; ++i) - { - ReturnErrorOnFailure(encoder.Encode(chip::ByteSpan(chip::Uint8::from_char(headerExample), sizeof(headerExample) - 1))); - } - return CHIP_NO_ERROR; - }); -} - -CHIP_ERROR ContentLauncherManager::proxyGetSupportedStreamingTypes(chip::app::AttributeValueEncoder & aEncoder) -{ - return aEncoder.EncodeList([](const chip::app::TagBoundEncoder & encoder) -> CHIP_ERROR { - // TODO: Insert code here - 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(list parameterList, bool autoplay, - string data) -{ - // TODO: Insert code here - ContentLaunchResponse response; - response.data = "Example data"; - response.status = EMBER_ZCL_CONTENT_LAUNCH_STATUS_SUCCESS; - return response; -} -ContentLaunchResponse ContentLauncherManager::proxyLaunchUrlRequest(string contentUrl, string displayString, - ContentLaunchBrandingInformation brandingInformation) -{ - // TODO: Insert code here - ContentLaunchResponse response; - response.data = "Example data"; - response.status = EMBER_ZCL_CONTENT_LAUNCH_STATUS_SUCCESS; - return response; -} - -static void sendResponse(const char * responseName, ContentLaunchResponse launchResponse, chip::CommandId commandId) -{ - emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT), ZCL_CONTENT_LAUNCH_CLUSTER_ID, - commandId, "us", launchResponse.status, &launchResponse.data); - - EmberStatus status = emberAfSendResponse(); - if (status != EMBER_SUCCESS) - { - ChipLogError(Zcl, "Failed to send %s. Error:%d", responseName, static_cast(status)); - } -} - -bool emberAfContentLauncherClusterLaunchContentCallback( - chip::app::CommandHandler * command, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ContentLauncher::Commands::LaunchContent::DecodableType & commandData) -{ - auto & autoplay = commandData.autoPlay; - auto & data = commandData.data; - - string dataString(data.data(), data.size()); - list parameterList; - ContentLaunchResponse response = ContentLauncherManager().proxyLaunchContentRequest(parameterList, autoplay, dataString); - sendResponse("LaunchContent", response, ZCL_LAUNCH_CONTENT_RESPONSE_COMMAND_ID); - return true; -} - -bool emberAfContentLauncherClusterLaunchURLCallback( - chip::app::CommandHandler * command, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::DecodableType & commandData) -{ - auto & contentUrl = commandData.contentURL; - auto & displayString = commandData.displayString; - - string contentUrlString(contentUrl.data(), contentUrl.size()); - string displayStringString(displayString.data(), displayString.size()); - ContentLaunchBrandingInformation brandingInformation; - ContentLaunchResponse response = - ContentLauncherManager().proxyLaunchUrlRequest(contentUrlString, displayStringString, brandingInformation); - sendResponse("LaunchURL", response, ZCL_LAUNCH_URL_RESPONSE_COMMAND_ID); - return true; -} diff --git a/examples/tv-app/android/include/content-launcher/ContentLauncherManager.h b/examples/tv-app/android/include/content-launcher/ContentLauncherManager.h deleted file mode 100644 index 285e521e889934..00000000000000 --- a/examples/tv-app/android/include/content-launcher/ContentLauncherManager.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * - * 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 -{ - EmberAfContentLaunchStatus status; - std::string data; -}; - -class ContentLauncherManager -{ -public: - CHIP_ERROR Init(); - CHIP_ERROR proxyGetAcceptsHeader(chip::app::AttributeValueEncoder & aEncoder); - CHIP_ERROR proxyGetSupportedStreamingTypes(chip::app::AttributeValueEncoder & aEncoder); - ContentLaunchResponse proxyLaunchContentRequest(std::list parameterList, bool autoplay, - std::string data); - ContentLaunchResponse proxyLaunchUrlRequest(std::string contentUrl, std::string displayString, - ContentLaunchBrandingInformation brandingInformation); -}; diff --git a/examples/tv-app/android/include/low-power/LowPowerManager.cpp b/examples/tv-app/android/include/low-power/LowPowerManager.cpp deleted file mode 100644 index 97016d4b01a74f..00000000000000 --- a/examples/tv-app/android/include/low-power/LowPowerManager.cpp +++ /dev/null @@ -1,25 +0,0 @@ -/* - * - * 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. - */ - -#include "LowPowerManager.h" - -bool lowPowerClusterSleep() -{ - // TODO: Insert code here - return true; -} diff --git a/examples/tv-app/android/include/media-playback/MediaPlaybackManager.cpp b/examples/tv-app/android/include/media-playback/MediaPlaybackManager.cpp deleted file mode 100644 index 3146f0c3d79036..00000000000000 --- a/examples/tv-app/android/include/media-playback/MediaPlaybackManager.cpp +++ /dev/null @@ -1,79 +0,0 @@ -/** - * - * Copyright (c) 2021 Project CHIP Authors - * - * 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. - */ - -#include "MediaPlaybackManager.h" -#include -#include -#include - -#include -#include - -using namespace std; - -CHIP_ERROR MediaPlaybackManager::Init() -{ - CHIP_ERROR err = CHIP_NO_ERROR; - - // TODO: Store feature map once it is supported - map featureMap; - featureMap["AS"] = true; - - SuccessOrExit(err); -exit: - return err; -} - -EmberAfMediaPlaybackStatus MediaPlaybackManager::proxyMediaPlaybackRequest(MediaPlaybackRequest mediaPlaybackRequest, - uint64_t deltaPositionMilliseconds) -{ - switch (mediaPlaybackRequest) - { - case MEDIA_PLAYBACK_REQUEST_PLAY: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_PAUSE: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_STOP: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_START_OVER: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_PREVIOUS: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_NEXT: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_REWIND: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_FAST_FORWARD: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_SKIP_FORWARD: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_SKIP_BACKWARD: - // TODO: Insert code here - case MEDIA_PLAYBACK_REQUEST_SEEK: - return EMBER_ZCL_MEDIA_PLAYBACK_STATUS_SUCCESS; - break; - default: { - return EMBER_ZCL_MEDIA_PLAYBACK_STATUS_SUCCESS; - } - } -} - -EmberAfMediaPlaybackStatus mediaPlaybackClusterSendMediaPlaybackRequest(MediaPlaybackRequest mediaPlaybackRequest, - uint64_t deltaPositionMilliseconds) -{ - return MediaPlaybackManager().proxyMediaPlaybackRequest(mediaPlaybackRequest, deltaPositionMilliseconds); -} diff --git a/examples/tv-app/android/include/tv-channel/TvChannelManager.cpp b/examples/tv-app/android/include/tv-channel/TvChannelManager.cpp deleted file mode 100644 index 5d5a1ead456e6d..00000000000000 --- a/examples/tv-app/android/include/tv-channel/TvChannelManager.cpp +++ /dev/null @@ -1,84 +0,0 @@ -/** - * - * Copyright (c) 2021 Project CHIP Authors - * - * 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. - */ - -#include "TvChannelManager.h" -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -using namespace chip; - -CHIP_ERROR TvChannelManager::Init() -{ - CHIP_ERROR err = CHIP_NO_ERROR; - // TODO: Store feature map once it is supported - std::map featureMap; - featureMap["CL"] = true; - featureMap["LI"] = true; - - SuccessOrExit(err); -exit: - return err; -} - -CHIP_ERROR TvChannelManager::proxyGetTvChannelList(chip::app::AttributeValueEncoder & aEncoder) -{ - return aEncoder.EncodeList([](const chip::app::TagBoundEncoder & encoder) -> CHIP_ERROR { - // TODO: Insert code here - int maximumVectorSize = 2; - char affiliateCallSign[] = "exampleASign"; - char callSign[] = "exampleCSign"; - char name[] = "exampleName"; - - for (int i = 0; i < maximumVectorSize; ++i) - { - chip::app::Clusters::TvChannel::Structs::TvChannelInfo::Type channelInfo; - channelInfo.affiliateCallSign = CharSpan(affiliateCallSign, sizeof(affiliateCallSign) - 1); - channelInfo.callSign = CharSpan(callSign, sizeof(callSign) - 1); - channelInfo.name = CharSpan(name, sizeof(name) - 1); - channelInfo.majorNumber = static_cast(1 + i); - channelInfo.minorNumber = static_cast(2 + i); - ReturnErrorOnFailure(encoder.Encode(channelInfo)); - } - return CHIP_NO_ERROR; - }); -} - -TvChannelInfo tvChannelClusterChangeChannel(std::string match) -{ - // TODO: Insert code here - TvChannelInfo channel = {}; - return channel; -} -bool tvChannelClusterChangeChannelByNumber(uint16_t majorNumber, uint16_t minorNumber) -{ - // TODO: Insert code here - return true; -} -bool tvChannelClusterSkipChannel(uint16_t count) -{ - // TODO: Insert code here - return true; -} diff --git a/examples/tv-app/android/java/ContentLauncherManager.cpp b/examples/tv-app/android/java/ContentLauncherManager.cpp new file mode 100644 index 00000000000000..5adbddebc03679 --- /dev/null +++ b/examples/tv-app/android/java/ContentLauncherManager.cpp @@ -0,0 +1,352 @@ +/* + * + * 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. + */ + +#include "ContentLauncherManager.h" + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace std; +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::SupportedStreamingTypes::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(std::list parameterList, bool autoplay, + const chip::CharSpan & data) +{ + return ContentLauncherMgr().LaunchContent(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 chip::app::TagBoundEncoder & 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 chip::app::TagBoundEncoder & 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(std::list parameterList, bool autoplay, + const chip::CharSpan & data) +{ + ContentLaunchResponse response; + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "Received ContentLauncherManager::LaunchContent"); + VerifyOrExit(mContentLauncherManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mLaunchContentMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + { + UtfString jData(env, data); + + // Todo: make parameterList java + jobjectArray parameterArray = nullptr; + + jobject resp = + env->CallObjectMethod(mContentLauncherManagerObject, mLaunchContentMethod, parameterArray, autoplay, jData.jniValue()); + if (env->ExceptionCheck()) + { + ChipLogError(Zcl, "Java exception in ContentLauncherManager::LaunchContent"); + env->ExceptionDescribe(); + env->ExceptionClear(); + err = CHIP_ERROR_INCORRECT_STATE; + goto exit; + } + + VerifyOrExit(resp != nullptr, err = CHIP_JNI_ERROR_NULL_OBJECT); + jclass respCls = env->GetObjectClass(resp); + jfieldID statusFid = env->GetFieldID(respCls, "status", "I"); + VerifyOrExit(statusFid != nullptr, err = CHIP_JNI_ERROR_FIELD_NOT_FOUND); + jint status = env->GetIntField(resp, statusFid); + + jfieldID dataFid = env->GetFieldID(respCls, "data", "Ljava/lang/String;"); + VerifyOrExit(dataFid != nullptr, err = CHIP_JNI_ERROR_FIELD_NOT_FOUND); + jstring jdataStr = (jstring) env->GetObjectField(resp, dataFid); + JniUtfString dataStr(env, jdataStr); + + response.status = static_cast(status); + response.data = dataStr.charSpan(); + } + +exit: + response.err = err; + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "ContentLauncherManager::LaunchContent status error: %s", err.AsString()); + } + + return response; +} + +ContentLaunchResponse ContentLauncherManager::LaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation) +{ + ContentLaunchResponse response; + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "Received ContentLauncherManager::LaunchContent"); + VerifyOrExit(mContentLauncherManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mLaunchUrlMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + { + UtfString jContentUrl(env, contentUrl); + UtfString jDisplayString(env, displayString); + + // Todo: make brandingInformation java + jobjectArray branding = nullptr; + + jobject resp = env->CallObjectMethod(mContentLauncherManagerObject, mLaunchUrlMethod, jContentUrl.jniValue(), + jDisplayString.jniValue(), branding); + if (env->ExceptionCheck()) + { + ChipLogError(Zcl, "Java exception in ContentLauncherManager::LaunchUrl"); + env->ExceptionDescribe(); + env->ExceptionClear(); + err = CHIP_ERROR_INCORRECT_STATE; + goto exit; + } + + VerifyOrExit(resp != nullptr, err = CHIP_JNI_ERROR_NULL_OBJECT); + jclass respCls = env->GetObjectClass(resp); + jfieldID statusFid = env->GetFieldID(respCls, "status", "I"); + VerifyOrExit(statusFid != nullptr, err = CHIP_JNI_ERROR_FIELD_NOT_FOUND); + jint status = env->GetIntField(resp, statusFid); + + jfieldID dataFid = env->GetFieldID(respCls, "data", "Ljava/lang/String;"); + VerifyOrExit(dataFid != nullptr, err = CHIP_JNI_ERROR_FIELD_NOT_FOUND); + jstring jdataStr = (jstring) env->GetObjectField(resp, dataFid); + JniUtfString dataStr(env, jdataStr); + + response.status = static_cast(status); + response.data = dataStr.charSpan(); + } + +exit: + response.err = err; + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "ContentLauncherManager::LaunchUrl status error: %s", err.AsString()); + } + + return response; +} diff --git a/examples/tv-app/android/java/ContentLauncherManager.h b/examples/tv-app/android/java/ContentLauncherManager.h new file mode 100644 index 00000000000000..22378e41955df7 --- /dev/null +++ b/examples/tv-app/android/java/ContentLauncherManager.h @@ -0,0 +1,54 @@ +/* + * + * 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 + +class ContentLauncherManager +{ +public: + void InitializeWithObjects(jobject managerObject); + CHIP_ERROR GetAcceptsHeader(chip::app::AttributeValueEncoder & aEncoder); + CHIP_ERROR GetSupportedStreamingTypes(chip::app::AttributeValueEncoder & aEncoder); + ContentLaunchResponse LaunchContent(std::list parameterList, bool autoplay, + const chip::CharSpan & data); + ContentLaunchResponse LaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation); + +private: + friend ContentLauncherManager & ContentLauncherMgr(); + + static ContentLauncherManager sInstance; + jobject mContentLauncherManagerObject = nullptr; + jmethodID mGetAcceptsHeaderMethod = nullptr; + jmethodID mGetSupportedStreamingTypesMethod = nullptr; + jmethodID mLaunchContentMethod = nullptr; + jmethodID mLaunchUrlMethod = nullptr; +}; + +inline ContentLauncherManager & ContentLauncherMgr() +{ + return ContentLauncherManager::sInstance; +} diff --git a/examples/tv-app/android/java/LowPowerManager.cpp b/examples/tv-app/android/java/LowPowerManager.cpp new file mode 100644 index 00000000000000..52dd0b7586d5a8 --- /dev/null +++ b/examples/tv-app/android/java/LowPowerManager.cpp @@ -0,0 +1,76 @@ +/* + * + * 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. + */ + +#include "LowPowerManager.h" +#include +#include +#include +#include +#include + +using namespace chip; + +LowPowerManager LowPowerManager::sInstance; + +bool lowPowerClusterSleep() +{ + return LowPowerMgr().Sleep(); +} + +void LowPowerManager::InitializeWithObjects(jobject managerObject) +{ + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for LowPowerManager")); + + mLowPowerManagerObject = env->NewGlobalRef(managerObject); + VerifyOrReturn(mLowPowerManagerObject != nullptr, ChipLogError(Zcl, "Failed to NewGlobalRef LowPowerManager")); + + jclass LowPowerManagerClass = env->GetObjectClass(managerObject); + VerifyOrReturn(LowPowerManagerClass != nullptr, ChipLogError(Zcl, "Failed to get LowPowerManager Java class")); + + mSleepMethod = env->GetMethodID(LowPowerManagerClass, "sleep", "()Z"); + if (mSleepMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access LowPowerManager 'sleep' method"); + env->ExceptionClear(); + } +} + +bool LowPowerManager::Sleep() +{ + jboolean ret = JNI_FALSE; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "Received LowPowerManager::Sleep"); + VerifyOrExit(mLowPowerManagerObject != nullptr, ChipLogError(Zcl, "mLowPowerManagerObject null")); + VerifyOrExit(mSleepMethod != nullptr, ChipLogError(Zcl, "mSleepMethod null")); + VerifyOrExit(env != NULL, ChipLogError(Zcl, "env null")); + + env->ExceptionClear(); + ret = env->CallBooleanMethod(mLowPowerManagerObject, mSleepMethod); + if (env->ExceptionCheck()) + { + ChipLogError(DeviceLayer, "Java exception in LowPowerManager::Sleep"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + +exit: + return static_cast(ret); +} diff --git a/examples/tv-app/android/include/tv-channel/TvChannelManager.h b/examples/tv-app/android/java/LowPowerManager.h similarity index 63% rename from examples/tv-app/android/include/tv-channel/TvChannelManager.h rename to examples/tv-app/android/java/LowPowerManager.h index aaf78b8deec744..6602136b914428 100644 --- a/examples/tv-app/android/include/tv-channel/TvChannelManager.h +++ b/examples/tv-app/android/java/LowPowerManager.h @@ -1,6 +1,7 @@ -/** +/* * * 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. @@ -17,15 +18,24 @@ #pragma once -#include - +#include #include -#include -#include -class TvChannelManager +class LowPowerManager { public: - CHIP_ERROR Init(); - CHIP_ERROR proxyGetTvChannelList(chip::app::AttributeValueEncoder & aEncoder); + void InitializeWithObjects(jobject managerObject); + bool Sleep(); + +private: + friend LowPowerManager & LowPowerMgr(); + + static LowPowerManager sInstance; + jobject mLowPowerManagerObject = nullptr; + jmethodID mSleepMethod = nullptr; }; + +inline LowPowerManager & LowPowerMgr() +{ + return LowPowerManager::sInstance; +} diff --git a/examples/tv-app/android/java/MediaPlaybackManager.cpp b/examples/tv-app/android/java/MediaPlaybackManager.cpp new file mode 100644 index 00000000000000..0b760157f68167 --- /dev/null +++ b/examples/tv-app/android/java/MediaPlaybackManager.cpp @@ -0,0 +1,217 @@ +/** + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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. + */ + +#include "MediaPlaybackManager.h" +#include +#include +#include +#include +#include +#include + +using namespace chip; + +MediaPlaybackManager MediaPlaybackManager::sInstance; + +class MediaPlayBackAttrAccess : public app::AttributeAccessInterface +{ +public: + MediaPlayBackAttrAccess() : app::AttributeAccessInterface(Optional::Missing(), app::Clusters::MediaPlayback::Id) {} + + CHIP_ERROR Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) override + { + int attrId = -1; + + switch (aPath.mAttributeId) + { + case app::Clusters::MediaPlayback::Attributes::PlaybackState::Id: { + attrId = ZCL_MEDIA_PLAYBACK_STATE_ATTRIBUTE_ID; + break; + } + case app::Clusters::MediaPlayback::Attributes::StartTime::Id: { + attrId = ZCL_MEDIA_PLAYBACK_START_TIME_ATTRIBUTE_ID; + break; + } + case app::Clusters::MediaPlayback::Attributes::Duration::Id: { + attrId = ZCL_MEDIA_PLAYBACK_DURATION_ATTRIBUTE_ID; + break; + } + case app::Clusters::MediaPlayback::Attributes::PositionUpdatedAt::Id: { + attrId = ZCL_MEDIA_PLAYBACK_PLAYBACK_POSITION_UPDATED_AT_ATTRIBUTE_ID; + break; + } + case app::Clusters::MediaPlayback::Attributes::Position::Id: { + attrId = ZCL_MEDIA_PLAYBACK_PLAYBACK_POSITION_ATTRIBUTE_ID; + break; + } + case app::Clusters::MediaPlayback::Attributes::PlaybackSpeed::Id: { + attrId = ZCL_MEDIA_PLAYBACK_PLAYBACK_SPEED_ATTRIBUTE_ID; + break; + } + case app::Clusters::MediaPlayback::Attributes::SeekRangeEnd::Id: { + attrId = ZCL_MEDIA_PLAYBACK_PLAYBACK_SEEK_RANGE_END_ATTRIBUTE_ID; + break; + } + case app::Clusters::MediaPlayback::Attributes::SeekRangeStart::Id: { + attrId = ZCL_MEDIA_PLAYBACK_PLAYBACK_SEEK_RANGE_START_ATTRIBUTE_ID; + break; + } + } + + if (attrId >= 0) + { + return MediaPlaybackMgr().GetAttribute(aEncoder, attrId); + } + + return CHIP_NO_ERROR; + } +}; + +MediaPlayBackAttrAccess gMediaPlayBackAttrAccess; + +/** @brief Media PlayBack 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 emberAfMediaPlaybackClusterInitCallback(EndpointId endpoint) +{ + static bool attrAccessRegistered = false; + if (!attrAccessRegistered) + { + registerAttributeAccessOverride(&gMediaPlayBackAttrAccess); + attrAccessRegistered = true; + } +} + +EmberAfMediaPlaybackStatus mediaPlaybackClusterSendMediaPlaybackRequest(MediaPlaybackRequest mediaPlaybackRequest, + uint64_t deltaPositionMilliseconds) +{ + return MediaPlaybackMgr().Request(mediaPlaybackRequest, deltaPositionMilliseconds); +} + +void MediaPlaybackManager::InitializeWithObjects(jobject managerObject) +{ + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for MediaPlaybackManager")); + + mMediaPlaybackManagerObject = env->NewGlobalRef(managerObject); + VerifyOrReturn(mMediaPlaybackManagerObject != nullptr, ChipLogError(Zcl, "Failed to NewGlobalRef MediaPlaybackManager")); + + jclass mMediaPlaybackManagerClass = env->GetObjectClass(managerObject); + VerifyOrReturn(mMediaPlaybackManagerClass != nullptr, ChipLogError(Zcl, "Failed to get MediaPlaybackManager Java class")); + + mGetAttributeMethod = env->GetMethodID(mMediaPlaybackManagerClass, "getAttributes", "(I)J"); + if (mGetAttributeMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'getMediaPlaybackAttribute' method"); + env->ExceptionClear(); + } + + mRequestMethod = env->GetMethodID(mMediaPlaybackManagerClass, "request", "(IJ)I"); + if (mRequestMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access MediaPlaybackManager 'proxyMediaPlaybackRequest' method"); + env->ExceptionClear(); + } +} + +CHIP_ERROR MediaPlaybackManager::GetAttribute(chip::app::AttributeValueEncoder & aEncoder, int attributeId) +{ + jlong jAttributeValue = -1; + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "Received MediaPlaybackManager::GetAttribute:%d", attributeId); + VerifyOrExit(mMediaPlaybackManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mGetAttributeMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + jAttributeValue = env->CallLongMethod(mMediaPlaybackManagerObject, mGetAttributeMethod, static_cast(attributeId)); + if (env->ExceptionCheck()) + { + ChipLogError(AppServer, "Java exception in MediaPlaybackManager::GetAttribute"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return CHIP_ERROR_INCORRECT_STATE; + } + + if (jAttributeValue >= 0) + { + switch (attributeId) + { + case ZCL_MEDIA_PLAYBACK_PLAYBACK_SPEED_ATTRIBUTE_ID: { + // TODO: Convert to single once it is supported + // float speed = static_cast(jAttributeValue) / 10000.0f; + err = aEncoder.Encode(static_cast(jAttributeValue)); + break; + } + + default: { + err = aEncoder.Encode(static_cast(jAttributeValue)); + } + } + } + else + { + err = CHIP_ERROR_INCORRECT_STATE; + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "MediaPlaybackManager::GetAttribute status error: %s", err.AsString()); + } + + return err; +} + +EmberAfMediaPlaybackStatus MediaPlaybackManager::Request(MediaPlaybackRequest mediaPlaybackRequest, + uint64_t deltaPositionMilliseconds) +{ + jint ret = -1; + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "MediaPlaybackManager::Request %d-%ld", mediaPlaybackRequest, deltaPositionMilliseconds); + VerifyOrExit(mMediaPlaybackManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mRequestMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + env->ExceptionClear(); + ret = env->CallIntMethod(mMediaPlaybackManagerObject, mRequestMethod, static_cast(mediaPlaybackRequest), + static_cast(deltaPositionMilliseconds)); + if (env->ExceptionCheck()) + { + ChipLogError(AppServer, "Java exception in MediaPlaybackManager::GetAttribute"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return EMBER_ZCL_MEDIA_PLAYBACK_STATUS_INVALID_STATE_FOR_COMMAND; + } + +exit: + if (err != CHIP_NO_ERROR) + { + return EMBER_ZCL_MEDIA_PLAYBACK_STATUS_INVALID_STATE_FOR_COMMAND; + } + + return static_cast(ret); +} diff --git a/examples/tv-app/android/include/media-playback/MediaPlaybackManager.h b/examples/tv-app/android/java/MediaPlaybackManager.h similarity index 57% rename from examples/tv-app/android/include/media-playback/MediaPlaybackManager.h rename to examples/tv-app/android/java/MediaPlaybackManager.h index e90601e6d6a99c..018c9cba8c8041 100644 --- a/examples/tv-app/android/include/media-playback/MediaPlaybackManager.h +++ b/examples/tv-app/android/java/MediaPlaybackManager.h @@ -21,17 +21,27 @@ #include #include #include - +#include +#include #include class MediaPlaybackManager { public: - CHIP_ERROR Init(); - void storeNewPlaybackState(chip::EndpointId endpoint, uint8_t newPlaybackState); - EmberAfMediaPlaybackStatus proxyMediaPlaybackRequest(MediaPlaybackRequest mediaPlaybackRequest, - uint64_t deltaPositionMilliseconds); + void InitializeWithObjects(jobject managerObject); + CHIP_ERROR GetAttribute(chip::app::AttributeValueEncoder & aEncoder, int attributeId); + EmberAfMediaPlaybackStatus Request(MediaPlaybackRequest mediaPlaybackRequest, uint64_t deltaPositionMilliseconds); private: - uint8_t oldPlaybackState; + friend MediaPlaybackManager & MediaPlaybackMgr(); + + static MediaPlaybackManager sInstance; + jobject mMediaPlaybackManagerObject = nullptr; + jmethodID mRequestMethod = nullptr; + jmethodID mGetAttributeMethod = nullptr; }; + +inline MediaPlaybackManager & MediaPlaybackMgr() +{ + return MediaPlaybackManager::sInstance; +} diff --git a/examples/tv-app/android/java/TVApp-JNI.cpp b/examples/tv-app/android/java/TVApp-JNI.cpp index 6b7b5310645d3d..accd4a0ccc5cb0 100644 --- a/examples/tv-app/android/java/TVApp-JNI.cpp +++ b/examples/tv-app/android/java/TVApp-JNI.cpp @@ -16,8 +16,12 @@ * */ +#include "ContentLauncherManager.h" #include "KeypadInputManager.h" +#include "LowPowerManager.h" #include "MediaInputManager.h" +#include "MediaPlaybackManager.h" +#include "TvChannelManager.h" #include "WakeOnLanManager.h" #include #include @@ -50,3 +54,23 @@ JNI_METHOD(void, setMediaInputManager)(JNIEnv *, jobject, jobject manager) { MediaInputMgr().InitializeWithObjects(manager); } + +JNI_METHOD(void, setContentLaunchManager)(JNIEnv *, jobject, jobject manager) +{ + ContentLauncherMgr().InitializeWithObjects(manager); +} + +JNI_METHOD(void, setLowPowerManager)(JNIEnv *, jobject, jobject manager) +{ + LowPowerMgr().InitializeWithObjects(manager); +} + +JNI_METHOD(void, setMediaPlaybackManager)(JNIEnv *, jobject, jobject manager) +{ + MediaPlaybackMgr().InitializeWithObjects(manager); +} + +JNI_METHOD(void, setTvChannelManager)(JNIEnv *, jobject, jobject manager) +{ + TvChannelMgr().InitializeWithObjects(manager); +} diff --git a/examples/tv-app/android/java/TvChannelManager.cpp b/examples/tv-app/android/java/TvChannelManager.cpp new file mode 100644 index 00000000000000..ead4b7e7ae673e --- /dev/null +++ b/examples/tv-app/android/java/TvChannelManager.cpp @@ -0,0 +1,444 @@ +/** + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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. + */ + +#include "TvChannelManager.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +using namespace chip; + +TvChannelManager TvChannelManager::sInstance; + +class ChannelInfoAttrAccess : public app::AttributeAccessInterface +{ +public: + ChannelInfoAttrAccess() : app::AttributeAccessInterface(Optional::Missing(), app::Clusters::TvChannel::Id) {} + CHIP_ERROR Read(const app::ConcreteReadAttributePath & aPath, app::AttributeValueEncoder & aEncoder) override + { + if (aPath.mAttributeId == app::Clusters::TvChannel::Attributes::TvChannelList::Id) + { + return TvChannelMgr().getTvChannelList(aEncoder); + } + else if (aPath.mAttributeId == app::Clusters::TvChannel::Attributes::TvChannelLineup::Id) + { + return TvChannelMgr().getTvChannelLineup(aEncoder); + } + else if (aPath.mAttributeId == app::Clusters::TvChannel::Attributes::CurrentTvChannel::Id) + { + return TvChannelMgr().getCurrentTvChannel(aEncoder); + } + + return CHIP_NO_ERROR; + } +}; + +ChannelInfoAttrAccess gTvChannelAttrAccess; + +/** @brief Tv Channel 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 emberAfTvChannelClusterInitCallback(EndpointId endpoint) +{ + static bool attrAccessRegistered = false; + if (!attrAccessRegistered) + { + registerAttributeAccessOverride(&gTvChannelAttrAccess); + attrAccessRegistered = true; + } +} + +TvChannelInfo tvChannelClusterChangeChannel(std::string match) +{ + return TvChannelMgr().ChangeChannelByMatch(match); +} + +bool tvChannelClusterChangeChannelByNumber(uint16_t majorNumber, uint16_t minorNumber) +{ + return TvChannelMgr().changeChannelByNumber(majorNumber, minorNumber); +} + +bool tvChannelClusterSkipChannel(uint16_t count) +{ + return TvChannelMgr().skipChannnel(count); +} + +void TvChannelManager::InitializeWithObjects(jobject managerObject) +{ + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + VerifyOrReturn(env != nullptr, ChipLogError(Zcl, "Failed to GetEnvForCurrentThread for TvChannelManager")); + + mTvChannelManagerObject = env->NewGlobalRef(managerObject); + VerifyOrReturn(mTvChannelManagerObject != nullptr, ChipLogError(Zcl, "Failed to NewGlobalRef TvChannelManager")); + + jclass managerClass = env->GetObjectClass(mTvChannelManagerObject); + VerifyOrReturn(managerClass != nullptr, ChipLogError(Zcl, "Failed to get TvChannelManager Java class")); + + mGetChannelListMethod = env->GetMethodID(managerClass, "getChannelList", "()[Lcom/tcl/chip/tvapp/TvChannelInfo;"); + if (mGetChannelListMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access TvChannelManager 'getChannelList' method"); + env->ExceptionClear(); + } + + mGetLineupMethod = env->GetMethodID(managerClass, "getLineup", "()Lcom/tcl/chip/tvapp/TvChannelLineupInfo;"); + if (mGetLineupMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access TvChannelManager 'getLineup' method"); + env->ExceptionClear(); + } + + mGetCurrentChannelMethod = env->GetMethodID(managerClass, "getCurrentChannel", "()Lcom/tcl/chip/tvapp/TvChannelInfo;"); + if (mGetCurrentChannelMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access TvChannelManager 'getCurrentChannel' method"); + env->ExceptionClear(); + } + + mChangeChannelMethod = + env->GetMethodID(managerClass, "changeChannel", "(Ljava/lang/String;)Lcom/tcl/chip/tvapp/TvChannelInfo;"); + if (mChangeChannelMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access TvChannelManager 'changeChannel' method"); + env->ExceptionClear(); + } + + mchangeChannelByNumberMethod = env->GetMethodID(managerClass, "changeChannelByNumber", "(II)Z"); + if (mchangeChannelByNumberMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access TvChannelManager 'changeChannelByNumber' method"); + env->ExceptionClear(); + } + + mskipChannelMethod = env->GetMethodID(managerClass, "skipChannel", "(I)Z"); + if (mskipChannelMethod == nullptr) + { + ChipLogError(Zcl, "Failed to access TvChannelManager 'skipChannel' method"); + env->ExceptionClear(); + } +} + +CHIP_ERROR TvChannelManager::getTvChannelList(chip::app::AttributeValueEncoder & aEncoder) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "Received TvChannelManager::getTvChannelList"); + VerifyOrExit(mTvChannelManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mGetChannelListMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + return aEncoder.EncodeList([env, this](const chip::app::TagBoundEncoder & encoder) -> CHIP_ERROR { + jobjectArray channelInfoList = (jobjectArray) env->CallObjectMethod(mTvChannelManagerObject, mGetChannelListMethod); + jint length = env->GetArrayLength(channelInfoList); + for (jint i = 0; i < length; i++) + { + chip::app::Clusters::TvChannel::Structs::TvChannelInfo::Type channelInfo; + jobject channelObject = env->GetObjectArrayElement(channelInfoList, i); + jclass channelClass = env->GetObjectClass(channelObject); + + jfieldID getCallSignField = env->GetFieldID(channelClass, "callSign", "Ljava/lang/String;"); + jstring jcallSign = static_cast(env->GetObjectField(channelObject, getCallSignField)); + if (jcallSign != NULL) + { + JniUtfString callsign(env, jcallSign); + channelInfo.callSign = callsign.charSpan(); + } + + jfieldID getNameField = env->GetFieldID(channelClass, "name", "Ljava/lang/String;"); + jstring jname = static_cast(env->GetObjectField(channelObject, getNameField)); + if (jname != NULL) + { + JniUtfString name(env, jname); + channelInfo.callSign = name.charSpan(); + } + + jfieldID getJaffiliateCallSignField = env->GetFieldID(channelClass, "affiliateCallSign", "Ljava/lang/String;"); + jstring jaffiliateCallSign = static_cast(env->GetObjectField(channelObject, getJaffiliateCallSignField)); + if (jaffiliateCallSign != NULL) + { + JniUtfString affiliateCallSign(env, jaffiliateCallSign); + channelInfo.callSign = affiliateCallSign.charSpan(); + } + + jfieldID majorNumField = env->GetFieldID(channelClass, "majorNumber", "I"); + jint jmajorNum = env->GetIntField(channelObject, majorNumField); + channelInfo.majorNumber = static_cast(jmajorNum); + + jfieldID minorNumField = env->GetFieldID(channelClass, "minorNumber", "I"); + jint jminorNum = env->GetIntField(channelObject, minorNumField); + channelInfo.majorNumber = static_cast(jminorNum); + ReturnErrorOnFailure(encoder.Encode(channelInfo)); + } + return CHIP_NO_ERROR; + }); +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "TvChannelManager::getTVChannelList status error: %s", err.AsString()); + } + + return err; +} + +CHIP_ERROR TvChannelManager::getTvChannelLineup(chip::app::AttributeValueEncoder & aEncoder) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "Received TvChannelManager::getTvChannelLineup"); + VerifyOrExit(mTvChannelManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mGetLineupMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + { + chip::app::Clusters::TvChannel::Structs::TvChannelLineupInfo::Type channelLineupInfo; + + jobject channelLineupObject = env->CallObjectMethod(mTvChannelManagerObject, mGetLineupMethod); + jclass channelLineupClazz = env->GetObjectClass(channelLineupObject); + + jfieldID operatorNameFild = env->GetFieldID(channelLineupClazz, "operatorName", "Ljava/lang/String;"); + jstring joperatorName = static_cast(env->GetObjectField(channelLineupObject, operatorNameFild)); + if (joperatorName != NULL) + { + JniUtfString operatorName(env, joperatorName); + channelLineupInfo.operatorName = operatorName.charSpan(); + } + + jfieldID lineupNameFild = env->GetFieldID(channelLineupClazz, "lineupName", "Ljava/lang/String;"); + jstring jlineupName = static_cast(env->GetObjectField(channelLineupObject, lineupNameFild)); + if (jlineupName != NULL) + { + JniUtfString lineupName(env, jlineupName); + channelLineupInfo.lineupName = lineupName.charSpan(); + } + + jfieldID postalCodeFild = env->GetFieldID(channelLineupClazz, "postalCode", "Ljava/lang/String;"); + jstring jpostalCode = static_cast(env->GetObjectField(channelLineupObject, postalCodeFild)); + if (jpostalCode != NULL) + { + JniUtfString postalCode(env, jpostalCode); + channelLineupInfo.postalCode = postalCode.charSpan(); + } + + jfieldID lineupInfoTypeFild = env->GetFieldID(channelLineupClazz, "lineupInfoType", "I"); + jint jlineupInfoType = (env->GetIntField(channelLineupObject, lineupInfoTypeFild)); + channelLineupInfo.lineupInfoType = static_cast(jlineupInfoType); + + ReturnErrorOnFailure(aEncoder.Encode(channelLineupInfo)); + + return CHIP_NO_ERROR; + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "TvChannelManager::getTvChannelLineup status error: %s", err.AsString()); + } + + return err; +} + +CHIP_ERROR TvChannelManager::getCurrentTvChannel(chip::app::AttributeValueEncoder & aEncoder) +{ + CHIP_ERROR err = CHIP_NO_ERROR; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + ChipLogProgress(Zcl, "Received TvChannelManager::getCurrentTvChannel"); + VerifyOrExit(mTvChannelManagerObject != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(mGetCurrentChannelMethod != nullptr, err = CHIP_ERROR_INCORRECT_STATE); + VerifyOrExit(env != NULL, err = CHIP_JNI_ERROR_NO_ENV); + + { + chip::app::Clusters::TvChannel::Structs::TvChannelInfo::Type channelInfo; + + jobject channelInfoObject = env->CallObjectMethod(mTvChannelManagerObject, mGetCurrentChannelMethod); + jclass channelClass = env->GetObjectClass(channelInfoObject); + + jfieldID getCallSignField = env->GetFieldID(channelClass, "callSign", "Ljava/lang/String;"); + jstring jcallSign = static_cast(env->GetObjectField(channelInfoObject, getCallSignField)); + if (jcallSign != NULL) + { + JniUtfString callsign(env, jcallSign); + channelInfo.callSign = callsign.charSpan(); + } + + jfieldID getNameField = env->GetFieldID(channelClass, "name", "Ljava/lang/String;"); + jstring jname = static_cast(env->GetObjectField(channelInfoObject, getNameField)); + if (jname != NULL) + { + JniUtfString name(env, jname); + channelInfo.callSign = name.charSpan(); + } + + jfieldID getJaffiliateCallSignField = env->GetFieldID(channelClass, "affiliateCallSign", "Ljava/lang/String;"); + jstring jaffiliateCallSign = static_cast(env->GetObjectField(channelInfoObject, getJaffiliateCallSignField)); + if (jaffiliateCallSign != NULL) + { + JniUtfString affiliateCallSign(env, jaffiliateCallSign); + channelInfo.callSign = affiliateCallSign.charSpan(); + } + + jfieldID majorNumField = env->GetFieldID(channelClass, "majorNumber", "I"); + jint jmajorNum = env->GetIntField(channelInfoObject, majorNumField); + channelInfo.majorNumber = static_cast(jmajorNum); + + jfieldID minorNumField = env->GetFieldID(channelClass, "minorNumber", "I"); + jint jminorNum = env->GetIntField(channelInfoObject, minorNumField); + channelInfo.majorNumber = static_cast(jminorNum); + + ReturnErrorOnFailure(aEncoder.Encode(channelInfo)); + return CHIP_NO_ERROR; + } + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "TvChannelManager::getTVChannel status error: %s", err.AsString()); + } + + return err; +} + +TvChannelInfo TvChannelManager::ChangeChannelByMatch(std::string name) +{ + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + TvChannelInfo channelInfo{ 0, 0 }; + + ChipLogProgress(Zcl, "Received TvChannelManager::ChangeChannelByMatch name %s", name.c_str()); + VerifyOrExit(mTvChannelManagerObject != nullptr, ChipLogError(Zcl, "mTvChannelManagerObject null")); + VerifyOrExit(mChangeChannelMethod != nullptr, ChipLogError(Zcl, "mChangeChannelMethod null")); + VerifyOrExit(env != NULL, ChipLogError(Zcl, "env null")); + + { + UtfString jniname(env, name.c_str()); + env->ExceptionClear(); + jobject channelObject = env->CallObjectMethod(mTvChannelManagerObject, mChangeChannelMethod, jniname.jniValue()); + if (env->ExceptionCheck()) + { + ChipLogError(DeviceLayer, "Java exception in TvChannelManager::ChangeChannelByMatch"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return channelInfo; + } + + jclass channelClass = env->GetObjectClass(channelObject); + + jfieldID getCallSignField = env->GetFieldID(channelClass, "callSign", "Ljava/lang/String;"); + jstring jcallSign = static_cast(env->GetObjectField(channelObject, getCallSignField)); + if (jcallSign != NULL) + { + JniUtfString callsign(env, jcallSign); + channelInfo.callSign = callsign.charSpan(); + } + + jfieldID getNameField = env->GetFieldID(channelClass, "name", "Ljava/lang/String;"); + jstring jname = static_cast(env->GetObjectField(channelObject, getNameField)); + if (jname != NULL) + { + JniUtfString junitname(env, jname); + channelInfo.callSign = junitname.charSpan(); + } + jfieldID getJaffiliateCallSignField = env->GetFieldID(channelClass, "affiliateCallSign", "Ljava/lang/String;"); + jstring jaffiliateCallSign = static_cast(env->GetObjectField(channelObject, getJaffiliateCallSignField)); + if (jaffiliateCallSign != NULL) + { + JniUtfString affiliateCallSign(env, jaffiliateCallSign); + channelInfo.callSign = affiliateCallSign.charSpan(); + } + + jfieldID majorNumField = env->GetFieldID(channelClass, "majorNumber", "I"); + jint jmajorNum = env->GetIntField(channelObject, majorNumField); + channelInfo.majorNumber = static_cast(jmajorNum); + + jfieldID minorNumField = env->GetFieldID(channelClass, "minorNumber", "I"); + jint jminorNum = env->GetIntField(channelObject, minorNumField); + channelInfo.majorNumber = static_cast(jminorNum); + } + +exit: + return channelInfo; +} + +bool TvChannelManager::changeChannelByNumber(uint16_t majorNumber, uint16_t minorNumber) +{ + jboolean ret = JNI_FALSE; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "Received TvChannelManager::tvChannelClusterChangeChannelByNumber majorNumber %d, minorNumber %d", + majorNumber, minorNumber); + VerifyOrExit(mTvChannelManagerObject != nullptr, ChipLogError(Zcl, "mTvChannelManagerObject null")); + VerifyOrExit(mchangeChannelByNumberMethod != nullptr, ChipLogError(Zcl, "mchangeChannelByNumberMethod null")); + VerifyOrExit(env != NULL, ChipLogError(Zcl, "env null")); + + env->ExceptionClear(); + + ret = env->CallBooleanMethod(mTvChannelManagerObject, mchangeChannelByNumberMethod, static_cast(majorNumber), + static_cast(minorNumber)); + if (env->ExceptionCheck()) + { + ChipLogError(DeviceLayer, "Java exception in TvChannelManager::changeChannelByNumber"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + +exit: + return static_cast(ret); +} + +bool TvChannelManager::skipChannnel(uint16_t count) +{ + jboolean ret = JNI_FALSE; + JNIEnv * env = JniReferences::GetInstance().GetEnvForCurrentThread(); + + ChipLogProgress(Zcl, "Received TvChannelManager::skipChannnel count %d", count); + VerifyOrExit(mTvChannelManagerObject != nullptr, ChipLogError(Zcl, "mTvChannelManagerObject null")); + VerifyOrExit(mskipChannelMethod != nullptr, ChipLogError(Zcl, "mskipChannelMethod null")); + VerifyOrExit(env != NULL, ChipLogError(Zcl, "env null")); + + env->ExceptionClear(); + + ret = env->CallBooleanMethod(mTvChannelManagerObject, mskipChannelMethod, static_cast(count)); + if (env->ExceptionCheck()) + { + ChipLogError(DeviceLayer, "Java exception in TvChannelManager::SkipChannel"); + env->ExceptionDescribe(); + env->ExceptionClear(); + return false; + } + +exit: + return static_cast(ret); +} diff --git a/examples/tv-app/android/java/TvChannelManager.h b/examples/tv-app/android/java/TvChannelManager.h new file mode 100644 index 00000000000000..7d9ad650066626 --- /dev/null +++ b/examples/tv-app/android/java/TvChannelManager.h @@ -0,0 +1,57 @@ +/** + * + * Copyright (c) 2021 Project CHIP Authors + * + * 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 + +class TvChannelManager +{ +public: + void InitializeWithObjects(jobject managerObject); + CHIP_ERROR getTvChannelList(chip::app::AttributeValueEncoder & aEncoder); + CHIP_ERROR getTvChannelLineup(chip::app::AttributeValueEncoder & aEncoder); + CHIP_ERROR getCurrentTvChannel(chip::app::AttributeValueEncoder & aEncoder); + + TvChannelInfo ChangeChannelByMatch(std::string name); + bool changeChannelByNumber(uint16_t majorNumber, uint16_t minorNumber); + bool skipChannnel(uint16_t count); + +private: + friend TvChannelManager & TvChannelMgr(); + + static TvChannelManager sInstance; + jobject mTvChannelManagerObject = nullptr; + jmethodID mGetChannelListMethod = nullptr; + jmethodID mGetLineupMethod = nullptr; + jmethodID mGetCurrentChannelMethod = nullptr; + + jmethodID mChangeChannelMethod = nullptr; + jmethodID mchangeChannelByNumberMethod = nullptr; + jmethodID mskipChannelMethod = nullptr; +}; + +inline class TvChannelManager & TvChannelMgr() +{ + return TvChannelManager::sInstance; +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchBrandingInformation.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchBrandingInformation.java new file mode 100644 index 00000000000000..57bf9478984f76 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchBrandingInformation.java @@ -0,0 +1,62 @@ +package com.tcl.chip.tvapp; + +public class ContentLaunchBrandingInformation { + + public class StyleInformation { + /** Used for dimensions defined in a number of Pixels. */ + public static final int METRIC_PIXELS = 0; + + /** + * Used for dimensions defined as a percentage of the overall display dimensions. For example, + * if using a Percentage Metric type for a Width measurement of 50.0, against a display width of + * 1920 pixels, then the resulting value used would be 960 pixels (50.0% of 1920) for that + * dimension. Whenever a measurement uses this Metric type, the resulting values SHALL be + * rounded ("floored") towards 0 if the measurement requires an integer final value. + */ + public static final int METRIC_PERCENTAGE = 1; + + /** + * The URL of image used for Styling different Video Player sections like Logo, Watermark etc. + */ + public String imageUrl; + + /** + * The color, in RGB or RGBA, used for styling different Video Player sections like Logo, + * Watermark, etc. The value SHALL conform to the 6-digit or 8-digit format defined for CSS sRGB + * hexadecimal color notation. Examples: + * + *

#76DE19 for R=0x76, G=0xDE, B=0x19, A absent #76DE1980 for R=0x76, G=0xDE, B=0x19, A=0x80 + */ + public String color; + + /** The width using the metric defined in Metric */ + public double width; + + /** The height using the metric defined in Metric */ + public double height; + + /** Tmetric used for defining Height/Width in METRIC_XXX */ + public int metric; + } + + /** The name of of the provider for the given content. */ + public String providerName; + + /** + * The background of the Video Player while content launch request is being processed by it. This + * background information MAY also be used by the Video Player when it is in idle state. + */ + public StyleInformation background; + + /** The style of progress bar for media playback. */ + public StyleInformation progressBar; + + /** + * The screen shown when the Video Player is in an idle state. If this property is not populated, + * the Video Player SHALL default to logo or the provider name. + */ + public StyleInformation splash; + + /** The watermark shown when the media is playing. */ + public StyleInformation waterMark; +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchManager.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchManager.java new file mode 100755 index 00000000000000..ba8b209ad1e709 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchManager.java @@ -0,0 +1,59 @@ +/* + * 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. + * + */ + +package com.tcl.chip.tvapp; + +public interface ContentLaunchManager { + + int STREAMING_TYPE_DASH = 0; + int STREAMING_TYPE_HLS = 1; + + /** + * @return The list of content types supported by the Video Player or Content App in the form of + * entries in the HTTP "Accept" request header. + */ + String[] getAcceptsHeader(); + + /** @return The list information about supported streaming protocols in STREAMING_TYPE_XXX. */ + int[] getSupportedStreamingTypes(); + + /** + * Launch the specified content with optional search criteria. + * + * @param search search list for content for display or playback. + * @param autoplay whether to automatically start playing content. + * @param data Optional app-specific data. + */ + ContentLaunchResponse launchContent( + ContentLaunchSearchParameter[] search, boolean autoplay, String data); + + /** + * Launch content from the specified URL. + * + *

The content types supported include those identified in the AcceptHeader and + * SupportedStreamingProtocols attributes. + * + * @param url The URL of content to launch. + * @param display If present, SHALL provide a string that MAY be used to describe the content + * being accessed at the given URL. + * @param branding If present, SHALL indicate the BrandingInformation that MAY be displayed when + * playing back the given content. + */ + ContentLaunchResponse launchUrl( + String url, String display, ContentLaunchBrandingInformation branding); +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchManagerStub.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchManagerStub.java new file mode 100644 index 00000000000000..cf95fa807b31a3 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchManagerStub.java @@ -0,0 +1,42 @@ +package com.tcl.chip.tvapp; + +import android.util.Log; + +public class ContentLaunchManagerStub implements ContentLaunchManager { + + private final String TAG = ContentLaunchManagerStub.class.getSimpleName(); + + @Override + public String[] getAcceptsHeader() { + String[] headers = + new String[] { + "application/dash+xml", "application/vnd.apple.mpegurl", "text/html", + }; + + return headers; + } + + @Override + public int[] getSupportedStreamingTypes() { + int[] types = new int[] {STREAMING_TYPE_DASH, STREAMING_TYPE_HLS}; + return types; + } + + @Override + public ContentLaunchResponse launchContent( + ContentLaunchSearchParameter[] search, boolean autoplay, String data) { + Log.d(TAG, "launchContent:" + data + " autoplay=" + autoplay); + ContentLaunchResponse resp = + new ContentLaunchResponse(ContentLaunchResponse.STATUS_SUCCESS, "Example data in Java"); + return resp; + } + + @Override + public ContentLaunchResponse launchUrl( + String url, String display, ContentLaunchBrandingInformation branding) { + Log.d(TAG, "launchUrl:" + url + " display=" + display); + ContentLaunchResponse resp = + new ContentLaunchResponse(ContentLaunchResponse.STATUS_SUCCESS, "Example data in Java"); + return resp; + } +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchResponse.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchResponse.java new file mode 100644 index 00000000000000..96da369e2f4c6b --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchResponse.java @@ -0,0 +1,19 @@ +package com.tcl.chip.tvapp; + +public class ContentLaunchResponse { + + public static final int STATUS_SUCCESS = 0; + public static final int STATUS_URL_NOT_AVAILABLE = 1; + public static final int STATUS_AUTH_FAILED = 2; + + public ContentLaunchResponse(int status, String data) { + this.status = status; + this.data = data; + } + + /** The status in STATUS_XXX */ + public int status; + + /** Optional app-specific data. */ + public String data; +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchSearchParameter.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchSearchParameter.java new file mode 100644 index 00000000000000..6a9050b0baf82e --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/ContentLaunchSearchParameter.java @@ -0,0 +1,71 @@ +package com.tcl.chip.tvapp; + +import java.util.Map; + +public class ContentLaunchSearchParameter { + + /** Actor represents an actor credited in video media content; for example, "Gaby sHoffman" */ + public static final int TYPE_ACTOR = 0; + + /** Channel represents the identifying data for a television channel; for example, "PBS" */ + public static final int TYPE_CHANNEL = 1; + + /** A character represented in video media content; for example, "Snow White" */ + public static final int TYPE_CHARACTER = 2; + + /** A director of the video media content; for example, "Spike Lee" */ + public static final int TYPE_DIRECTOR = 3; + + /** + * An event is a reference to a type of event; examples would include sports, music, or other + * types of events. For example, searching for "Football games" would search for a 'game' event + * entity and a 'football' sport entity. + */ + public static final int TYPE_EVENT = 4; + + /** + * A franchise is a video entity which can represent a number of video entities, like movies or TV + * shows. For example, take the fictional franchise "Intergalactic Wars" which represents a + * collection of movie trilogies, as well as animated and live action TV shows. This entity type + * was introduced to account for requests by customers such as "Find Intergalactic Wars movies", + * which would search for all 'Intergalactic Wars' programs of the MOVIE MediaType, rather than + * attempting to match to a single title. + */ + public static final int TYPE_FRANCHISE = 5; + + /** Genre represents the genre of video media content such as action, drama or comedy. */ + public static final int TYPE_GENRE = 6; + + /** League represents the categorical information for a sporting league; for example, "NCAA" */ + public static final int TYPE_LEAGUE = 7; + + /** Popularity indicates whether the user asks for popular content. */ + public static final int TYPE_POPULARITY = 8; + + /** The provider (MSP) the user wants this media to be played on; for example, "Netflix". */ + public static final int TYPE_PROVIDER = 9; + + /** Sport represents the categorical information of a sport; for example, football */ + public static final int TYPE_SPORT = 10; + + /** + * SportsTeam represents the categorical information of a professional sports team; for example, + * "University of Washington Huskies" + */ + public static final int TYPE_SPORTS_TEAM = 11; + + /** + * The type of content requested. Supported types are "Movie", "MovieSeries", "TVSeries", + * "TVSeason", "TVEpisode", "SportsEvent", and "Video" + */ + public static final int TYPE_TYPE = 12; + + /** content data type in TYPE_XXX */ + public int type; + + /** The entity value, which is a search string, ex. "Manchester by the Sea". */ + public String data; + + /** This object defines additional name=value pairs that can be used for identifying content. */ + public Map.Entry[] externalIDList; +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/LowPowerManager.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/LowPowerManager.java new file mode 100644 index 00000000000000..acee84a0dbe0c7 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/LowPowerManager.java @@ -0,0 +1,22 @@ +/* + * 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. + * + */ +package com.tcl.chip.tvapp; + +public interface LowPowerManager { + boolean sleep(); +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/LowPowerManagerStub.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/LowPowerManagerStub.java new file mode 100644 index 00000000000000..8dd9e2f8caab87 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/LowPowerManagerStub.java @@ -0,0 +1,14 @@ +package com.tcl.chip.tvapp; + +import android.util.Log; + +public class LowPowerManagerStub implements LowPowerManager { + + private final String TAG = LowPowerManagerStub.class.getSimpleName(); + + @Override + public boolean sleep() { + Log.d(TAG, "sleep"); + return true; + } +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/MediaPlaybackManager.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/MediaPlaybackManager.java new file mode 100755 index 00000000000000..abfb8032708781 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/MediaPlaybackManager.java @@ -0,0 +1,125 @@ +/* + * 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. + * + */ + +package com.tcl.chip.tvapp; + +public interface MediaPlaybackManager { + + /** The current playback state of media in PLAYBACK_STATE_XXXX */ + int ATTRIBUTE_PLAYBACK_STATE = 0x0000; + + /** The start time of the media in milliseconds (UTC time) */ + int ATTRIBUTE_PLAYBACK_START_TIME = 0x0001; + + /** The duration, in milliseconds or 0 if the duration is not applicable */ + int ATTRIBUTE_PLAYBACK_DURATION = 0x0002; + + /** The position of playback (Position field) at the time (UpdateAt field) in milliseconds. */ + int ATTRIBUTE_PLAYBACK_POSITION_UPDATED_AT = 0x0003; + + /** The position of playback (Position field) at the time (UpdateAt field) in milliseconds. */ + int ATTRIBUTE_PLAYBACK_POSITION = 0x0004; + + /** + * The speed at which the current media is being played. 10000 means 1X, 5000 means 0.5X and 20000 + * means 2X + */ + int ATTRIBUTE_PLAYBACK_SPEED = 0x0005; + + /** The earliest valid position to which a client MAY seek back, in milliseconds */ + int ATTRIBUTE_PLAYBACK_SEEK_RANGE_END = 0x0006; + + /** The furthest forward valid position to which a client MAY seek forward, in milliseconds */ + int ATTRIBUTE_PLAYBACK_SEEK_RANGE_START = 0x0007; + + int PLAYBACK_STATE_PLAYING = 0; + int PLAYBACK_STATE_PAUSED = 1; + int PLAYBACK_STATE_NOT_PLAYING = 2; + int PLAYBACK_STATE_BUFFERING = 3; + + /** + * Play media. If content is currently in a FastForward or Rewind state. Play SHALL return media + * to normal playback speed. + */ + int REQUEST_PLAY = 0; + + /** Pause playback of the media. */ + int REQUEST_PAUSE = 1; + + /** Stop playback of the media */ + int REQUEST_STOP = 2; + + /** Start Over with the current media playback item. */ + int REQUEST_START_OVER = 3; + + /** Go back to the previous media playback item. */ + int REQUEST_PREVIOUS = 4; + + /** Go forward to the next media playback item. */ + int REQUEST_NEXT = 5; + + /** + * Start playback of the media backward in case the media is currently playing in the forward + * direction or is not playing. + */ + int REQUEST_REWIND = 6; + + /** + * Start playback of the media in the forward direction in case the media is currently playing in + * the backward direction or is not playing. If the playback is already happening in the forward + * direction receipt of this command SHALL increase the speed of the media playback. Different + * "fast-forward" speeds MAY be be reflected on the media playback device based upon the number of + * sequential calls to this function and the capability of the device. + */ + int REQUEST_FAST_FORWARD = 7; + + /** Skip forward in the media by the given number of milliseconds */ + int REQUEST_SKIP_FORWARD = 8; + + /** Skip backward in the media by the given number of milliseconds */ + int REQUEST_SKIP_BACKWARD = 9; + + /** + * Change the playback position in the media to the given position by the given number of + * milliseconds + */ + int REQUEST_SEEK = 10; + + int RESPONSE_STATUS_SUCCESS = 0; + int RESPONSE_STATUS_INVALID_STATE_FOR_COMMAND = 1; + int RESPONSE_STATUS_NOT_ALLOWED = 2; + int RESPONSE_STATUS_NOT_ACTIVE = 3; + int RESPONSE_STATUS_SPEED_OUT_OF_RANGE = 4; + int RESPONSE_STATUS_SEEK_OUT_OF_RANGE = 5; + + /** + * Get the attribute by id in ATTRIBUTE_PLAYBACK_XXX + * + * @return The value of return defines in ATTRIBUTE_PLAYBACK_XXX, or -1 on error + */ + long getAttributes(int attributesId); + + /** + * Request for cmd in REQUEST_XXX, the paramters for request is defined in REQUEST_XXX + * + * @param parameter The means of parameter is defined in RESPONSE_STATUS_XXX or meaningless if not + * defined. + * @return the response status defined in RESPONSE_STATUS_XXX + */ + int request(int cmd, long parameter); +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/MediaPlaybackManagerStub.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/MediaPlaybackManagerStub.java new file mode 100755 index 00000000000000..d4fb7552935e10 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/MediaPlaybackManagerStub.java @@ -0,0 +1,118 @@ +/* + * 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. + * + */ + +package com.tcl.chip.tvapp; + +import android.util.Log; +import java.util.Date; + +/** Stub implement of MediaPlaybackManager, TV manufacture should have there own implements */ +public class MediaPlaybackManagerStub implements MediaPlaybackManager { + + private final String TAG = MediaPlaybackManagerStub.class.getSimpleName(); + + @Override + public long getAttributes(int attributesId) { + switch (attributesId) { + case ATTRIBUTE_PLAYBACK_STATE: + Log.d(TAG, "getAttributes CurrentState"); + return PLAYBACK_STATE_PLAYING; + + case ATTRIBUTE_PLAYBACK_START_TIME: + Log.d(TAG, "getAttributes StartTime"); + return 100; + + case ATTRIBUTE_PLAYBACK_DURATION: + Log.d(TAG, "getAttributes Duration"); + return 5 * 60 * 1000; + + case ATTRIBUTE_PLAYBACK_POSITION_UPDATED_AT: + Log.d(TAG, "getAttributes SampledPosition UpdatedAt"); + return new Date().getTime() * 1000; + + case ATTRIBUTE_PLAYBACK_POSITION: + Log.d(TAG, "getAttributes SampledPosition Position"); + return 3 * 60 * 1000; + + case ATTRIBUTE_PLAYBACK_SPEED: + Log.d(TAG, "getAttributes SampledPosition PlaybackSpeed"); + return 10000; + + case ATTRIBUTE_PLAYBACK_SEEK_RANGE_END: + Log.d(TAG, "getAttributes SampledPosition SeekRangeEnd"); + return 5 * 60 * 1000; + + case ATTRIBUTE_PLAYBACK_SEEK_RANGE_START: + Log.d(TAG, "getAttributes SampledPosition SeekRangeStart"); + return 200; + } + + return -1; + } + + @Override + public int request(int cmd, long parameter) { + switch (cmd) { + case REQUEST_PLAY: + Log.d(TAG, "request Play"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_PAUSE: + Log.d(TAG, "request pause"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_STOP: + Log.d(TAG, "request stop"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_START_OVER: + Log.d(TAG, "request start over"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_PREVIOUS: + Log.d(TAG, "request previous"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_NEXT: + Log.d(TAG, "request next"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_REWIND: + Log.d(TAG, "request rewind"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_FAST_FORWARD: + Log.d(TAG, "request fast forward"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_SKIP_FORWARD: + Log.d(TAG, "request skip forward " + parameter + " milliseconds"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_SKIP_BACKWARD: + Log.d(TAG, "request skip backward " + parameter + " milliseconds"); + return RESPONSE_STATUS_SUCCESS; + + case REQUEST_SEEK: + Log.d(TAG, "request seek to " + parameter + " milliseconds"); + return RESPONSE_STATUS_SUCCESS; + } + + return RESPONSE_STATUS_NOT_ALLOWED; + } +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java index 9bb52cc1305014..f41b974a82d090 100644 --- a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvApp.java @@ -26,6 +26,14 @@ public TvApp() {} public native void setMediaInputManager(MediaInputManager manager); + public native void setContentLaunchManager(ContentLaunchManager manager); + + public native void setLowPowerManager(LowPowerManager manager); + + public native void setMediaPlaybackManager(MediaPlaybackManager manager); + + public native void setTvChannelManager(TvChannelManager manager); + static { System.loadLibrary("TvApp"); } diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelInfo.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelInfo.java new file mode 100644 index 00000000000000..da087c9fa87ead --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelInfo.java @@ -0,0 +1,56 @@ +/* + * 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. + * + */ +package com.tcl.chip.tvapp; + +public class TvChannelInfo { + public int majorNumber; + public int minorNumber; + public String name; + public String callSign; + public String affiliateCallSign; + + public TvChannelInfo() {} + + public TvChannelInfo( + int majorNumber, int minorNumber, String name, String callSign, String affiliateCallSign) { + this.majorNumber = majorNumber; + this.minorNumber = minorNumber; + this.name = name; + this.callSign = callSign; + this.affiliateCallSign = affiliateCallSign; + } + + @Override + public String toString() { + return "TvChannelInfo{" + + "majorNumber=" + + majorNumber + + ", minorNumber=" + + minorNumber + + ", name='" + + name + + '\'' + + ", callSign='" + + callSign + + '\'' + + ", affiliateCallSign='" + + affiliateCallSign + + '\'' + + '}'; + } +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelLineupInfo.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelLineupInfo.java new file mode 100644 index 00000000000000..0a8dffe9876036 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelLineupInfo.java @@ -0,0 +1,56 @@ +/* + * 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. + * + */ +package com.tcl.chip.tvapp; + +public class TvChannelLineupInfo { + public static final int LINEUP_INFO_TYPE_MSO = 0x00; + + public String operatorName; + public String lineupName; + public String postalCode; + public int lineupInfoType; + + public TvChannelLineupInfo(String operatorName, String lineupName, String postalCode) { + this(operatorName, lineupName, postalCode, LINEUP_INFO_TYPE_MSO); + } + + public TvChannelLineupInfo( + String operatorName, String lineupName, String postalCode, int lineupInfoType) { + this.operatorName = operatorName; + this.lineupName = lineupName; + this.postalCode = postalCode; + this.lineupInfoType = lineupInfoType; + } + + @Override + public String toString() { + return "TvChannelLineupInfo{" + + "operatorName='" + + operatorName + + '\'' + + ", lineupName='" + + lineupName + + '\'' + + ", postalCode='" + + postalCode + + '\'' + + ", lineupInfoType=" + + lineupInfoType + + '}'; + } +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelManager.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelManager.java new file mode 100644 index 00000000000000..3eedb1afbd6b1b --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelManager.java @@ -0,0 +1,33 @@ +/* + * 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. + * + */ +package com.tcl.chip.tvapp; + +public interface TvChannelManager { + + TvChannelInfo[] getChannelList(); + + TvChannelLineupInfo getLineup(); + + TvChannelInfo getCurrentChannel(); + + TvChannelInfo changeChannel(String match); + + boolean changeChannelByNumber(int majorNumber, int minorNumber); + + boolean skipChannel(int count); +} diff --git a/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelManagerStub.java b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelManagerStub.java new file mode 100644 index 00000000000000..c72ab4fe8605d2 --- /dev/null +++ b/examples/tv-app/android/java/src/com/tcl/chip/tvapp/TvChannelManagerStub.java @@ -0,0 +1,67 @@ +/* + * 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. + * + */ +package com.tcl.chip.tvapp; + +import android.util.Log; + +public class TvChannelManagerStub implements TvChannelManager { + private static final String TAG = "ChannelManagerStub"; + + @Override + public TvChannelInfo[] getChannelList() { + TvChannelInfo tvChannelInfo1 = + new TvChannelInfo(1, 1, "HDMI1", "callSign1", "affiliateCallSign1"); + TvChannelInfo tvChannelInfo2 = + new TvChannelInfo(2, 2, "HDMI2", "callSign2", "affiliateCallSign2"); + Log.d(TAG, "getTVChannelList"); + return new TvChannelInfo[] {tvChannelInfo1, tvChannelInfo2}; + } + + @Override + public TvChannelLineupInfo getLineup() { + TvChannelLineupInfo lineupInfo = new TvChannelLineupInfo("operator", "lineup", "postalCode"); + Log.d(TAG, "getTVChannelLineup: " + lineupInfo); + return lineupInfo; + } + + @Override + public TvChannelInfo getCurrentChannel() { + Log.d(TAG, "getCurrentTVChannel: "); + return new TvChannelInfo(1, 1, "HDMI", "callSign", "affiliateCallSign"); + } + + @Override + public TvChannelInfo changeChannel(String match) { + Log.d(TAG, "changeChannel: " + match); + return new TvChannelInfo(1, 1, "HDMI", "callSign", "affiliateCallSign"); + } + + @Override + public boolean changeChannelByNumber(int majorNumber, int minorNumber) { + Log.d( + TAG, + "changeChannelByNumber: majorNumber = " + majorNumber + " minorNumber = " + minorNumber); + return true; + } + + @Override + public boolean skipChannel(int count) { + Log.d(TAG, "skipChannel: count = " + count); + return true; + } +} diff --git a/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.cpp b/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.cpp index 001fe840623d32..0b2c74f196da3d 100644 --- a/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.cpp +++ b/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.cpp @@ -81,6 +81,7 @@ ContentLaunchResponse ContentLauncherManager::proxyLaunchContentRequest(list parameterList, bool autoplay, + const chip::CharSpan & data) { - emberAfFillExternalBuffer((ZCL_CLUSTER_SPECIFIC_COMMAND | ZCL_FRAME_CONTROL_SERVER_TO_CLIENT), ZCL_CONTENT_LAUNCH_CLUSTER_ID, - commandId, "us", launchResponse.status, &launchResponse.data); - - EmberStatus status = emberAfSendResponse(); - if (status != EMBER_SUCCESS) - { - ChipLogError(Zcl, "Failed to send %s. Error:%d", responseName, static_cast(status)); - } -} - -bool emberAfContentLauncherClusterLaunchContentCallback( - chip::app::CommandHandler * command, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ContentLauncher::Commands::LaunchContent::DecodableType & commandData) -{ - auto & autoplay = commandData.autoPlay; - auto & data = commandData.data; - string dataString(data.data(), data.size()); - list parameterList; - ContentLaunchResponse response = ContentLauncherManager().proxyLaunchContentRequest(parameterList, autoplay, dataString); - sendResponse("LaunchContent", response, ZCL_LAUNCH_CONTENT_RESPONSE_COMMAND_ID); - return true; + return ContentLauncherManager().proxyLaunchContentRequest(parameterList, autoplay, dataString); } -bool emberAfContentLauncherClusterLaunchURLCallback( - chip::app::CommandHandler * command, const chip::app::ConcreteCommandPath & commandPath, - const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::DecodableType & commandData) +ContentLaunchResponse contentLauncherClusterLaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation) { - auto & contentUrl = commandData.contentURL; - auto & displayString = commandData.displayString; - string contentUrlString(contentUrl.data(), contentUrl.size()); string displayStringString(displayString.data(), displayString.size()); - ContentLaunchBrandingInformation brandingInformation; - ContentLaunchResponse response = - ContentLauncherManager().proxyLaunchUrlRequest(contentUrlString, displayStringString, brandingInformation); - sendResponse("LaunchURL", response, ZCL_LAUNCH_URL_RESPONSE_COMMAND_ID); - return true; + return ContentLauncherManager().proxyLaunchUrlRequest(contentUrlString, displayStringString, brandingInformation); } diff --git a/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.h b/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.h index 285e521e889934..174204c47028fb 100644 --- a/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.h +++ b/examples/tv-app/linux/include/content-launcher/ContentLauncherManager.h @@ -20,16 +20,12 @@ #include #include +#include #include #include #include #include -struct ContentLaunchResponse -{ - EmberAfContentLaunchStatus status; - std::string data; -}; class ContentLauncherManager { 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 5568f0795011bb..2c9c327dfa6474 100644 --- a/src/app/clusters/content-launch-server/content-launch-server.cpp +++ b/src/app/clusters/content-launch-server/content-launch-server.cpp @@ -38,22 +38,78 @@ ******************************************************************************* ******************************************************************************/ +#include "content-launch-server.h" +#include #include #include +#include using namespace chip; -bool emberAfContentLauncherClusterLaunchContentCallback(app::CommandHandler * commandObj) +ContentLaunchResponse contentLauncherClusterLaunchContent(std::list parameterList, bool autoplay, + const chip::CharSpan & data); +ContentLaunchResponse contentLauncherClusterLaunchUrl(const chip::CharSpan & contentUrl, const chip::CharSpan & displayString, + ContentLaunchBrandingInformation & brandingInformation); + +bool emberAfContentLauncherClusterLaunchContentCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::ContentLauncher::Commands::LaunchContent::DecodableType & commandData) { - EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; - emberAfSendImmediateDefaultResponse(status); + CHIP_ERROR err = CHIP_NO_ERROR; + chip::app::Clusters::ContentLauncher::Commands::LaunchContentResponse::Type response; + + auto & autoplay = commandData.autoPlay; + auto & data = commandData.data; + std::list parameterList; + + ContentLaunchResponse resp = contentLauncherClusterLaunchContent(parameterList, autoplay, data); + VerifyOrExit(resp.err == CHIP_NO_ERROR, err = resp.err); + + response.contentLaunchStatus = resp.status; + response.data = resp.data; + + err = commandObj->AddResponseData(commandPath, response); + SuccessOrExit(err); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "emberAfContentLauncherClusterLaunchContentCallback error: %s", err.AsString()); + + emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_FAILURE); + } + return true; } -bool emberAfContentLauncherClusterLaunchURLCallback(app::CommandHandler * commandObj) +bool emberAfContentLauncherClusterLaunchURLCallback( + chip::app::CommandHandler * commandObj, const chip::app::ConcreteCommandPath & commandPath, + const chip::app::Clusters::ContentLauncher::Commands::LaunchURL::DecodableType & commandData) { - EmberAfStatus status = EMBER_ZCL_STATUS_SUCCESS; - emberAfSendImmediateDefaultResponse(status); + CHIP_ERROR err = CHIP_NO_ERROR; + chip::app::Clusters::ContentLauncher::Commands::LaunchURLResponse::Type response; + + auto & contentUrl = commandData.contentURL; + auto & displayString = commandData.displayString; + ContentLaunchBrandingInformation brandingInformation; + + ContentLaunchResponse resp = contentLauncherClusterLaunchUrl(contentUrl, displayString, brandingInformation); + VerifyOrExit(resp.err == CHIP_NO_ERROR, err = resp.err); + + response.contentLaunchStatus = resp.status; + response.data = resp.data; + + err = commandObj->AddResponseData(commandPath, response); + SuccessOrExit(err); + +exit: + if (err != CHIP_NO_ERROR) + { + ChipLogError(Zcl, "emberAfContentLauncherClusterLaunchURLCallback error: %s", err.AsString()); + + emberAfSendImmediateDefaultResponse(EMBER_ZCL_STATUS_FAILURE); + } + return true; } diff --git a/examples/tv-app/android/include/low-power/LowPowerManager.h b/src/app/clusters/content-launch-server/content-launch-server.h similarity index 73% rename from examples/tv-app/android/include/low-power/LowPowerManager.h rename to src/app/clusters/content-launch-server/content-launch-server.h index 75d500ca12de74..2d4934e5fc0eb9 100644 --- a/examples/tv-app/android/include/low-power/LowPowerManager.h +++ b/src/app/clusters/content-launch-server/content-launch-server.h @@ -1,7 +1,6 @@ -/* +/** * * 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. @@ -18,9 +17,12 @@ #pragma once -#include +#include +#include -class LowPowerManager +struct ContentLaunchResponse { -public: + CHIP_ERROR err; + chip::CharSpan data; + chip::app::Clusters::ContentLauncher::ContentLaunchStatus status; };