diff --git a/examples/tv-casting-app/darwin/TvCasting/MatterBridge/CastingServerBridge.h b/examples/tv-casting-app/darwin/TvCasting/MatterBridge/CastingServerBridge.h index 5e2587416e8eeb..d66909a60ca7f7 100644 --- a/examples/tv-casting-app/darwin/TvCasting/MatterBridge/CastingServerBridge.h +++ b/examples/tv-casting-app/darwin/TvCasting/MatterBridge/CastingServerBridge.h @@ -23,21 +23,86 @@ @interface CastingServerBridge : NSObject +@property void (^_Nullable commissioningCompleteCallback)(bool); + +@property void (^_Nullable launchUrlResponseCallback)(bool); + + (CastingServerBridge * _Nullable)getSharedInstance; +/*! + @brief Browse for on-network commissioner TVs + + @param clientQueue Queue to dispatch the call to the discoveryRequestSentHandler on + + @param discoveryRequestSentHandler Handler to call after the Commissioner discovery request has been sent + */ - (void)discoverCommissioners:(dispatch_queue_t _Nonnull)clientQueue discoveryRequestSentHandler:(nullable void (^)(bool))discoveryRequestSentHandler; +/*! + @brief Retrieve a discovered commissioner TV + + @param index Index in the list of discovered commissioners + + @param clientQueue Queue to dispatch the call to the discoveredCommissionerHandler on + + @param discoveredCommissionerHandler Handler to call after a discovered commissioner has been retrieved + */ - (void)getDiscoveredCommissioner:(int)index clientQueue:(dispatch_queue_t _Nonnull)clientQueue discoveredCommissionerHandler:(nullable void (^)(DiscoveredNodeData * _Nullable))discoveredCommissionerHandler; +/*! + @brief Send a User Directed Commissioning request to a commissioner TV + + @param commissionerIpAddress IP address of the commissioner + + @param commissionerPort Port number at which the commissioner is listening for User Directed Commissioning requests + + @param platformInterface Platform representation of the commissioner's IP address's interface + + @param clientQueue Queue to dispatch the call to the udcRequestSentHandler on + + @param udcRequestSentHandler Handler to call on sending the User Directed Commissioning request + */ - (void)sendUserDirectedCommissioningRequest:(NSString * _Nonnull)commissionerIpAddress commissionerPort:(uint16_t)commissionerPort platformInterface:(unsigned int)platformInterface clientQueue:(dispatch_queue_t _Nonnull)clientQueue udcRequestSentHandler:(nullable void (^)(bool))udcRequestSentHandler; +/*! + @brief Request opening of a basic commissioning window + + @param commissioningCompleteCallback Callback for when commissioning of this app has been completed via a call to the general + commissioning cluster (by usually an on-network TV/Media device acting as a Matter commissioner) + + @param clientQueue Queue to dispatch the call to the commissioningWindowRequestedHandler on + + @param commissioningWindowRequestedHandler Handler to call on requesting the opening of a commissioning window + */ +- (void)openBasicCommissioningWindow:(nullable void (^)(bool))commissioningCompleteCallback + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + commissioningWindowRequestedHandler:(nullable void (^)(bool))commissioningWindowRequestedHandler; + +/*! + @brief Send a Content Launcher:LaunchURL request to a TV + + @param contentUrl URL of the content to launch on the TV + + @param contentDisplayStr Display string value corresponding to the content + + @param launchUrlResponseCallback Callback for when the Launch URL response has been received + + @param clientQueue Queue to dispatch the call to the launchUrlRequestSentHandler on + + @param launchUrlRequestSentHandler Handler to call on sending the Launch URL request + */ +- (void)contentLauncherLaunchUrl:(NSString * _Nonnull)contentUrl + contentDisplayStr:(NSString * _Nonnull)contentDisplayStr + launchUrlResponseCallback:(nullable void (^)(bool))launchUrlResponseCallback + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + launchUrlRequestSentHandler:(nullable void (^)(bool))launchUrlRequestSentHandler; @end #endif /* CastingServerBridge_h */ diff --git a/examples/tv-casting-app/darwin/TvCasting/MatterBridge/CastingServerBridge.mm b/examples/tv-casting-app/darwin/TvCasting/MatterBridge/CastingServerBridge.mm index 33678347bd3ed6..3c870da50313c9 100644 --- a/examples/tv-casting-app/darwin/TvCasting/MatterBridge/CastingServerBridge.mm +++ b/examples/tv-casting-app/darwin/TvCasting/MatterBridge/CastingServerBridge.mm @@ -123,10 +123,11 @@ - (void)sendUserDirectedCommissioningRequest:(NSString * _Nonnull)commissionerIp clientQueue:(dispatch_queue_t _Nonnull)clientQueue udcRequestSentHandler:(nullable void (^)(bool))udcRequestSentHandler { - ChipLogProgress( - AppServer, "CastingServerBridge().sendUserDirectedCommissioningRequest() called with port %d", commissionerPort); + ChipLogProgress(AppServer, + "CastingServerBridge().sendUserDirectedCommissioningRequest() called with IP %s port %d platformInterface %d", + [commissionerIpAddress UTF8String], commissionerPort, platformInterface); - dispatch_async(chip::DeviceLayer::PlatformMgrImpl().GetWorkQueue(), ^{ + dispatch_async(_chipWorkQueue, ^{ bool udcRequestStatus; chip::Inet::IPAddress commissionerAddrInet; if (chip::Inet::IPAddress::FromString([commissionerIpAddress UTF8String], commissionerAddrInet) == false) { @@ -153,4 +154,39 @@ - (void)sendUserDirectedCommissioningRequest:(NSString * _Nonnull)commissionerIp }); }); } + +- (void)openBasicCommissioningWindow:(nullable void (^)(bool))commissioningCompleteCallback + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + commissioningWindowRequestedHandler:(nullable void (^)(bool))commissioningWindowRequestedHandler +{ + ChipLogProgress(AppServer, "CastingServerBridge().openBasicCommissioningWindow() called"); + + dispatch_async(_chipWorkQueue, ^{ + CHIP_ERROR err = CastingServer::GetInstance()->OpenBasicCommissioningWindow( + [&commissioningCompleteCallback](CHIP_ERROR err) { commissioningCompleteCallback(CHIP_NO_ERROR == err); }); + + dispatch_async(clientQueue, ^{ + commissioningWindowRequestedHandler(CHIP_NO_ERROR == err); + }); + }); +} + +- (void)contentLauncherLaunchUrl:(NSString * _Nonnull)contentUrl + contentDisplayStr:(NSString * _Nonnull)contentDisplayStr + launchUrlResponseCallback:(nullable void (^)(bool))launchUrlResponseCallback + clientQueue:(dispatch_queue_t _Nonnull)clientQueue + launchUrlRequestSentHandler:(nullable void (^)(bool))launchUrlRequestSentHandler +{ + ChipLogProgress(AppServer, "CastingServerBridge().contentLauncherLaunchUrl() called"); + + dispatch_async(_chipWorkQueue, ^{ + CHIP_ERROR err + = CastingServer::GetInstance()->ContentLauncherLaunchURL([contentUrl UTF8String], [contentDisplayStr UTF8String], + [&launchUrlResponseCallback](CHIP_ERROR err) { launchUrlResponseCallback(CHIP_NO_ERROR == err); }); + dispatch_async(clientQueue, ^{ + launchUrlRequestSentHandler(CHIP_NO_ERROR == err); + }); + }); +} + @end diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/project.pbxproj b/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/project.pbxproj index 179a71f9ac2275..b03976f499ccd2 100644 --- a/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/project.pbxproj +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting.xcodeproj/project.pbxproj @@ -15,6 +15,8 @@ 3C7507B92853EFF000D7DB3A /* CommissioningViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C7507B82853EFF000D7DB3A /* CommissioningViewModel.swift */; }; 3C7507BC2857A6EE00D7DB3A /* DiscoveredNodeDataConverter.mm in Sources */ = {isa = PBXBuildFile; fileRef = 3C7507BB2857A6EE00D7DB3A /* DiscoveredNodeDataConverter.mm */; }; 3C9ACC05284ABF4000718B2D /* libTvCastingCommon.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 3C9ACC04284ABF2F00718B2D /* libTvCastingCommon.a */; }; + 3CA19435285BA780004768D5 /* ContentLauncherView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CA19434285BA780004768D5 /* ContentLauncherView.swift */; }; + 3CA19437285BA877004768D5 /* ContentLauncherViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CA19436285BA877004768D5 /* ContentLauncherViewModel.swift */; }; 3CC0E8FA2841DD3400EC6A18 /* TvCastingApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CC0E8F92841DD3400EC6A18 /* TvCastingApp.swift */; }; 3CC0E8FC2841DD3400EC6A18 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CC0E8FB2841DD3400EC6A18 /* ContentView.swift */; }; 3CC0E8FE2841DD3500EC6A18 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3CC0E8FD2841DD3500EC6A18 /* Assets.xcassets */; }; @@ -34,6 +36,8 @@ 3C7507BB2857A6EE00D7DB3A /* DiscoveredNodeDataConverter.mm */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.objcpp; path = DiscoveredNodeDataConverter.mm; sourceTree = ""; }; 3C7507BD2857A72A00D7DB3A /* DiscoveredNodeDataConverter.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = DiscoveredNodeDataConverter.hpp; sourceTree = ""; }; 3C9ACC04284ABF2F00718B2D /* libTvCastingCommon.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; name = libTvCastingCommon.a; path = lib/libTvCastingCommon.a; sourceTree = BUILT_PRODUCTS_DIR; }; + 3CA19434285BA780004768D5 /* ContentLauncherView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentLauncherView.swift; sourceTree = ""; }; + 3CA19436285BA877004768D5 /* ContentLauncherViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentLauncherViewModel.swift; sourceTree = ""; }; 3CC0E8F62841DD3400EC6A18 /* TvCasting.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TvCasting.app; sourceTree = BUILT_PRODUCTS_DIR; }; 3CC0E8F92841DD3400EC6A18 /* TvCastingApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TvCastingApp.swift; sourceTree = ""; }; 3CC0E8FB2841DD3400EC6A18 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; @@ -98,6 +102,8 @@ 3C7507B62853A3AD00D7DB3A /* CommissionerDiscoveryViewModel.swift */, 3C7507AE28529A5F00D7DB3A /* CommissioningView.swift */, 3C7507B82853EFF000D7DB3A /* CommissioningViewModel.swift */, + 3CA19434285BA780004768D5 /* ContentLauncherView.swift */, + 3CA19436285BA877004768D5 /* ContentLauncherViewModel.swift */, ); path = TvCasting; sourceTree = ""; @@ -237,6 +243,8 @@ 3C7507B92853EFF000D7DB3A /* CommissioningViewModel.swift in Sources */, 3C7507BC2857A6EE00D7DB3A /* DiscoveredNodeDataConverter.mm in Sources */, 3C7507A72851188500D7DB3A /* DiscoveredNodeData.mm in Sources */, + 3CA19437285BA877004768D5 /* ContentLauncherViewModel.swift in Sources */, + 3CA19435285BA780004768D5 /* ContentLauncherView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -280,6 +288,7 @@ DEBUG_INFORMATION_FORMAT = dwarf; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; + "EXCLUDED_ARCHS[sdk=iphonesimulator*]" = arm64; GCC_C_LANGUAGE_STANDARD = gnu11; GCC_DYNAMIC_NO_PIC = NO; GCC_NO_COMMON_BLOCKS = YES; @@ -296,7 +305,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.2; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; MTL_FAST_MATH = YES; ONLY_ACTIVE_ARCH = YES; @@ -352,7 +361,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 15.2; + IPHONEOS_DEPLOYMENT_TARGET = 15.0; MTL_ENABLE_DEBUG_INFO = NO; MTL_FAST_MATH = YES; SDKROOT = iphoneos; diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissioningView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissioningView.swift index fee82a9c9cfacd..1d202f61332812 100644 --- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissioningView.swift +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissioningView.swift @@ -59,6 +59,20 @@ struct CommissioningView: View { Text("Failed to open Commissioning window!") .foregroundColor(Color.red) } + + if(viewModel.commisisoningComplete == true) + { + NavigationLink( + destination: ContentLauncherView(), + label: { + Text("Next") + .frame(width: 75, height: 30, alignment: .center) + .border(Color.black, width: 1) + } + ).background(Color.blue) + .foregroundColor(Color.white) + .padding() + } } .navigationTitle("Commissioning...") .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top) diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissioningViewModel.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissioningViewModel.swift index 6e8a030a065c1d..34edf1f1af9e81 100644 --- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissioningViewModel.swift +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/CommissioningViewModel.swift @@ -17,14 +17,34 @@ import Foundation +import os.log class CommissioningViewModel: ObservableObject { + let Log = Logger(subsystem: "com.matter.casting", + category: "CommissioningViewModel") + @Published var udcRequestSent: Bool?; @Published var commisisoningWindowOpened: Bool?; - + + @Published var commisisoningComplete: Bool?; + func prepareForCommissioning(selectedCommissioner: DiscoveredNodeData?) { - // TBD: Call openBasicCommissioningWindow() and get Onboarding payload + if let castingServerBridge = CastingServerBridge.getSharedInstance() + { + castingServerBridge.openBasicCommissioningWindow( + { (result: Bool) -> () in + // commissioning complete handler code + self.Log.info("Commissioning status: \(result)") + self.commisisoningComplete = result + }, + clientQueue: DispatchQueue.main, + commissioningWindowRequestedHandler: { (result: Bool) -> () in + self.commisisoningWindowOpened = result + }) + } + + // TBD: Get Onboarding payload // Send User directed commissioning request if a commissioner with a known IP addr was selected if(selectedCommissioner != nil && selectedCommissioner!.numIPs > 0) diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentLauncherView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentLauncherView.swift new file mode 100644 index 00000000000000..120b6ee1a903d6 --- /dev/null +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentLauncherView.swift @@ -0,0 +1,72 @@ +/** + * + * Copyright (c) 2020-2022 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. + */ + +import SwiftUI + +struct ContentLauncherView: View { + @StateObject var viewModel = ContentLauncherViewModel() + + @State private var contentUrl: String = "" + @State private var contentDisplayStr: String = "" + + var body: some View { + VStack(alignment: .leading) { + HStack() { + Text("Content URL") + + TextField( + "https://www.test.com/videoid", + text: $contentUrl + ) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .border(.secondary) + } + + HStack() { + Text("Display string") + + TextField( + "Test video", + text: $contentDisplayStr + ) + .textInputAutocapitalization(.never) + .disableAutocorrection(true) + .border(.secondary) + } + + Button("Launch URL!") { + viewModel.launchUrl(contentUrl: contentUrl, contentDisplayStr: contentDisplayStr) + } + .background(Color.blue) + .foregroundColor(Color.white) + .cornerRadius(4) + .border(Color.black, width: 1) + .padding() + + Text(viewModel.status ?? "") + } + .navigationTitle("Content Launcher") + .frame(minWidth: 0, maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .top) + } +} + +struct ContentLauncherView_Previews: PreviewProvider { + static var previews: some View { + ContentLauncherView() + } +} diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentLauncherViewModel.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentLauncherViewModel.swift new file mode 100644 index 00000000000000..2f42037bbb0aad --- /dev/null +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentLauncherViewModel.swift @@ -0,0 +1,56 @@ +/** + * + * Copyright (c) 2020-2022 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. + */ + + +import Foundation +import os.log + +class ContentLauncherViewModel: ObservableObject { + let Log = Logger(subsystem: "com.matter.casting", + category: "ContentLauncherViewModel") + + @Published var status: String?; + + func launchUrl(contentUrl: String?, contentDisplayStr: String?) + { + if ((contentUrl != nil && !contentUrl!.isEmpty) && (contentDisplayStr != nil && !contentDisplayStr!.isEmpty)) + { + if let castingServerBridge = CastingServerBridge.getSharedInstance() + { + castingServerBridge + .contentLauncherLaunchUrl(contentUrl!, + contentDisplayStr: contentDisplayStr!, + launchUrlResponseCallback: + { (result: Bool) -> () in + self.Log.info("ContentLauncherViewModel.launchUrl.launchUrlResponseCallback result \(result)") + self.status = result ? "Launched URL successfully" : "Launch URL failure!" + }, + clientQueue: DispatchQueue.main, + launchUrlRequestSentHandler: + { (result: Bool) -> () in + self.Log.info("ContentLauncherViewModel.launchUrl.launcUrlRequestSentHandler result \(result)") + self.status = result ? "Sent Launch URL request" : "Failed to send Launch URL request!" + }) + } + } + else + { + Log.debug("ContentLauncherViewModel.launchUrl input(s) missing!") + self.status = "Missing input parameter(s)!" + } + } +} diff --git a/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentView.swift b/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentView.swift index 13d7255a3d0838..69b67095c60751 100644 --- a/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentView.swift +++ b/examples/tv-casting-app/darwin/TvCasting/TvCasting/ContentView.swift @@ -20,7 +20,7 @@ import SwiftUI struct ContentView: View { var body: some View { NavigationView { - CommissionerDiscoveryView() + CommissionerDiscoveryView() } } } diff --git a/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h b/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h index a63471244806fc..999e1236e0ff4e 100644 --- a/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h +++ b/examples/tv-casting-app/tv-casting-common/include/CHIPProjectAppConfig.h @@ -48,3 +48,6 @@ #define CHIP_DEVICE_CONFIG_DEVICE_NAME "Test TV casting app" #define CHIP_DEVICE_CONFIG_ENABLE_PAIRING_AUTOSTART 0 + +// Enable some test-only interaction model APIs. +#define CONFIG_BUILD_FOR_HOST_UNIT_TEST 1