From a76450bf50cecfe6a1c7141d9d3cdf5797a836b8 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Wed, 26 Jul 2017 16:59:14 +0300 Subject: [PATCH 01/22] Enable Siri in Capabilities tab --- Riot.xcodeproj/project.pbxproj | 3 +++ Riot/Riot.entitlements | 2 ++ 2 files changed, 5 insertions(+) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index c7682c902..20ff5aefe 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -2118,6 +2118,9 @@ com.apple.Push = { enabled = 1; }; + com.apple.Siri = { + enabled = 1; + }; }; }; F094A9BD1B78D8F000B1FBBF = { diff --git a/Riot/Riot.entitlements b/Riot/Riot.entitlements index 4cbad96a1..3a9826778 100644 --- a/Riot/Riot.entitlements +++ b/Riot/Riot.entitlements @@ -11,5 +11,7 @@ applinks:riot.im applinks:www.riot.im + com.apple.developer.siri + From 0d8cb75620ed81fa330f4abe1baa85d33b9141dc Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Wed, 26 Jul 2017 17:08:00 +0300 Subject: [PATCH 02/22] Add IntentHandler --- Riot.xcodeproj/project.pbxproj | 134 +++++++++++++++++++++++++++++++++ SiriIntents/Info.plist | 42 +++++++++++ SiriIntents/IntentHandler.h | 21 ++++++ SiriIntents/IntentHandler.m | 111 +++++++++++++++++++++++++++ 4 files changed, 308 insertions(+) create mode 100644 SiriIntents/Info.plist create mode 100644 SiriIntents/IntentHandler.h create mode 100644 SiriIntents/IntentHandler.m diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 20ff5aefe..124ff3656 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -43,6 +43,8 @@ 32D392191EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */; }; 32FD0A3D1EB0CD9B0072B066 /* BugReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */; }; 32FD0A3E1EB0CD9B0072B066 /* BugReportViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */; }; + 92E963B51F28D907008FDAF5 /* IntentHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 92E963B41F28D907008FDAF5 /* IntentHandler.m */; }; + 92E963B91F28D907008FDAF5 /* SiriIntents.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 92E963B11F28D906008FDAF5 /* SiriIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E2EAC1A4FBD6FE5228584591 /* libPods-Riot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D8737F782E108CFD6908691 /* libPods-Riot.a */; }; F0131DE51F2200D600CBF707 /* RiotSplitViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */; }; F02C1A861E8EB04C0045A404 /* PeopleViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = F02C1A841E8EB04C0045A404 /* PeopleViewController.m */; }; @@ -485,6 +487,13 @@ /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ + 92E963B71F28D907008FDAF5 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = F094A99A1B78D8F000B1FBBF /* Project object */; + proxyType = 1; + remoteGlobalIDString = 92E963B01F28D906008FDAF5; + remoteInfo = SiriIntents; + }; F094A9BF1B78D8F000B1FBBF /* PBXContainerItemProxy */ = { isa = PBXContainerItemProxy; containerPortal = F094A99A1B78D8F000B1FBBF /* Project object */; @@ -494,6 +503,20 @@ }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 92E963BD1F28D907008FDAF5 /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 92E963B91F28D907008FDAF5 /* SiriIntents.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 1129C74A281B080432B1A1A1 /* Pods-Riot.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Riot.debug.xcconfig"; path = "Pods/Target Support Files/Pods-Riot/Pods-Riot.debug.xcconfig"; sourceTree = ""; }; 24B5103C1EFA7083004C6AD2 /* ReadReceiptsViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ReadReceiptsViewController.h; sourceTree = ""; }; @@ -545,6 +568,10 @@ 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugReportViewController.m; sourceTree = ""; }; 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BugReportViewController.xib; sourceTree = ""; }; 7D8737F782E108CFD6908691 /* libPods-Riot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Riot.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 92E963B11F28D906008FDAF5 /* SiriIntents.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SiriIntents.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 92E963B31F28D907008FDAF5 /* IntentHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IntentHandler.h; sourceTree = ""; }; + 92E963B41F28D907008FDAF5 /* IntentHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IntentHandler.m; sourceTree = ""; }; + 92E963B61F28D907008FDAF5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; F0131DE31F2200D600CBF707 /* RiotSplitViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RiotSplitViewController.h; sourceTree = ""; }; F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RiotSplitViewController.m; sourceTree = ""; }; F02C1A831E8EB04C0045A404 /* PeopleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeopleViewController.h; sourceTree = ""; }; @@ -1111,6 +1138,13 @@ /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ + 92E963AE1F28D906008FDAF5 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F094A99F1B78D8F000B1FBBF /* Frameworks */ = { isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; @@ -1190,6 +1224,16 @@ name = Frameworks; sourceTree = ""; }; + 92E963B21F28D907008FDAF5 /* SiriIntents */ = { + isa = PBXGroup; + children = ( + 92E963B31F28D907008FDAF5 /* IntentHandler.h */, + 92E963B41F28D907008FDAF5 /* IntentHandler.m */, + 92E963B61F28D907008FDAF5 /* Info.plist */, + ); + path = SiriIntents; + sourceTree = ""; + }; E1451F540F8BC02A7FB7AA31 /* Pods */ = { isa = PBXGroup; children = ( @@ -2045,6 +2089,7 @@ children = ( F083BB081E7009EC00A9B29C /* Riot */, F083BB021E7005FD00A9B29C /* RiotTests */, + 92E963B21F28D907008FDAF5 /* SiriIntents */, F094A9A31B78D8F000B1FBBF /* Products */, E1451F540F8BC02A7FB7AA31 /* Pods */, 6567B0BBF3C05D7F7A7F79CC /* Frameworks */, @@ -2056,6 +2101,7 @@ children = ( F094A9A21B78D8F000B1FBBF /* Riot.app */, F094A9BE1B78D8F000B1FBBF /* RiotTests.xctest */, + 92E963B11F28D906008FDAF5 /* SiriIntents.appex */, ); name = Products; sourceTree = ""; @@ -2063,6 +2109,23 @@ /* End PBXGroup section */ /* Begin PBXNativeTarget section */ + 92E963B01F28D906008FDAF5 /* SiriIntents */ = { + isa = PBXNativeTarget; + buildConfigurationList = 92E963BA1F28D907008FDAF5 /* Build configuration list for PBXNativeTarget "SiriIntents" */; + buildPhases = ( + 92E963AD1F28D906008FDAF5 /* Sources */, + 92E963AE1F28D906008FDAF5 /* Frameworks */, + 92E963AF1F28D906008FDAF5 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = SiriIntents; + productName = SiriIntents; + productReference = 92E963B11F28D906008FDAF5 /* SiriIntents.appex */; + productType = "com.apple.product-type.app-extension"; + }; F094A9A11B78D8F000B1FBBF /* Riot */ = { isa = PBXNativeTarget; buildConfigurationList = F094A9C81B78D8F000B1FBBF /* Build configuration list for PBXNativeTarget "Riot" */; @@ -2073,10 +2136,12 @@ F094A9A01B78D8F000B1FBBF /* Resources */, 44C35695CFA4F9799C449367 /* [CP] Copy Pods Resources */, 381DA4CC07D2104BFA23E45A /* [CP] Embed Pods Frameworks */, + 92E963BD1F28D907008FDAF5 /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + 92E963B81F28D907008FDAF5 /* PBXTargetDependency */, ); name = Riot; productName = Vector; @@ -2110,6 +2175,11 @@ LastUpgradeCheck = 0800; ORGANIZATIONNAME = matrix.org; TargetAttributes = { + 92E963B01F28D906008FDAF5 = { + CreatedOnToolsVersion = 8.3.3; + DevelopmentTeam = 7J4U792NQT; + ProvisioningStyle = Automatic; + }; F094A9A11B78D8F000B1FBBF = { CreatedOnToolsVersion = 6.2; DevelopmentTeam = 7J4U792NQT; @@ -2148,11 +2218,19 @@ targets = ( F094A9A11B78D8F000B1FBBF /* Riot */, F094A9BD1B78D8F000B1FBBF /* RiotTests */, + 92E963B01F28D906008FDAF5 /* SiriIntents */, ); }; /* End PBXProject section */ /* Begin PBXResourcesBuildPhase section */ + 92E963AF1F28D906008FDAF5 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; F094A9A01B78D8F000B1FBBF /* Resources */ = { isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; @@ -2558,6 +2636,14 @@ /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ + 92E963AD1F28D906008FDAF5 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 92E963B51F28D907008FDAF5 /* IntentHandler.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; F094A99E1B78D8F000B1FBBF /* Sources */ = { isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; @@ -2709,6 +2795,11 @@ /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ + 92E963B81F28D907008FDAF5 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 92E963B01F28D906008FDAF5 /* SiriIntents */; + targetProxy = 92E963B71F28D907008FDAF5 /* PBXContainerItemProxy */; + }; F094A9C01B78D8F000B1FBBF /* PBXTargetDependency */ = { isa = PBXTargetDependency; target = F094A9A11B78D8F000B1FBBF /* Riot */; @@ -2832,6 +2923,41 @@ /* End PBXVariantGroup section */ /* Begin XCBuildConfiguration section */ + 92E963BB1F28D907008FDAF5 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + DEBUG_INFORMATION_FORMAT = dwarf; + DEVELOPMENT_TEAM = 7J4U792NQT; + INFOPLIST_FILE = SiriIntents/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = im.vector.app.SiriIntents; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Debug; + }; + 92E963BC1F28D907008FDAF5 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + DEVELOPMENT_TEAM = 7J4U792NQT; + INFOPLIST_FILE = SiriIntents/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 10.3; + LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; + PRODUCT_BUNDLE_IDENTIFIER = im.vector.app.SiriIntents; + PRODUCT_NAME = "$(TARGET_NAME)"; + SKIP_INSTALL = YES; + }; + name = Release; + }; F094A9C61B78D8F000B1FBBF /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { @@ -2993,6 +3119,14 @@ /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ + 92E963BA1F28D907008FDAF5 /* Build configuration list for PBXNativeTarget "SiriIntents" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 92E963BB1F28D907008FDAF5 /* Debug */, + 92E963BC1F28D907008FDAF5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + }; F094A99D1B78D8F000B1FBBF /* Build configuration list for PBXProject "Riot" */ = { isa = XCConfigurationList; buildConfigurations = ( diff --git a/SiriIntents/Info.plist b/SiriIntents/Info.plist new file mode 100644 index 000000000..f0d8ab808 --- /dev/null +++ b/SiriIntents/Info.plist @@ -0,0 +1,42 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + SiriIntents + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + IntentsRestrictedWhileLocked + + IntentsSupported + + INSendMessageIntent + INSearchForMessagesIntent + INSetMessageAttributeIntent + + + NSExtensionPointIdentifier + com.apple.intents-service + NSExtensionPrincipalClass + IntentHandler + + + diff --git a/SiriIntents/IntentHandler.h b/SiriIntents/IntentHandler.h new file mode 100644 index 000000000..66da341a6 --- /dev/null +++ b/SiriIntents/IntentHandler.h @@ -0,0 +1,21 @@ +/* + Copyright 2017 Vector Creations Ltd + + 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 + +@interface IntentHandler : INExtension + +@end diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m new file mode 100644 index 000000000..5e3fa7d20 --- /dev/null +++ b/SiriIntents/IntentHandler.m @@ -0,0 +1,111 @@ +/* + Copyright 2017 Vector Creations Ltd + + 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 "IntentHandler.h" + +#import "MXKAccountManager.h" +#import "MXKAccount.h" +#import "MXSession.h" +#import "MXFileStore.h" +//#import + +// As an example, this class is set up to handle Message intents. +// You will want to replace this or add other intents as appropriate. +// The intents you wish to handle must be declared in the extension's Info.plist. + +// You can test your example integration by saying things to Siri like: +// "Send a message using " +// " John saying hello" +// "Search for messages in " + +@interface IntentHandler () + +@end + +@implementation IntentHandler + +- (id)handlerForIntent:(INIntent *)intent { + id handler = nil; + + if ([intent isKindOfClass:INStartAudioCallIntent.class]) + { + handler = self; + } + + return handler; +} + +#pragma mark - INStartAudioCallIntentHandling + +- (void)resolveContactsForStartAudioCall:(INStartAudioCallIntent *)intent withCompletion:(void (^)(NSArray * _Nonnull))completion +{ + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + MXSession *session = [[MXSession alloc] initWithMatrixRestClient:account.mxRestClient]; + [session setStore:[[MXFileStore alloc] init] success:^{ + NSLog(@"Super"); + } failure:^(NSError *error) { + NSLog(@"Fail"); + }]; + + // NSArray *recipients = intent.contacts; + // // If no recipients were provided we'll need to prompt for a value. + // if (recipients.count == 0) { + // completion(@[[INPersonResolutionResult needsValue]]); + // return; + // } + // NSMutableArray *resolutionResults = [NSMutableArray array]; + // + // for (INPerson *recipient in recipients) { + // NSArray *matchingContacts = @[recipient]; // Implement your contact matching logic here to create an array of matching contacts + // if (matchingContacts.count > 1) { + // // We need Siri's help to ask user to pick one from the matches. + // [resolutionResults addObject:[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:matchingContacts]]; + // + // } else if (matchingContacts.count == 1) { + // // We have exactly one matching contact + // [resolutionResults addObject:[INPersonResolutionResult successWithResolvedPerson:recipient]]; + // } else { + // // We have no contacts matching the description provided + // [resolutionResults addObject:[INPersonResolutionResult unsupported]]; + // } + // } + // + // completion(resolutionResults); +} + +- (void)confirmStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion +{ + INStartAudioCallIntentResponse *response = nil; + +#if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined MX_CALL_STACK_JINGLE + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:userActivity]; +#else + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; +#endif + + completion(response); +} + +- (void)handleStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion +{ + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; + // userActivity.userInfo = @{ @"handle": [NSString stringWithFormat:@"TGCA%d", next.firstObject.userId] }; + INStartAudioCallIntentResponse *response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp userActivity:userActivity]; + completion(response); +} + +@end From 7c2cdee0e4f6b17b2e42cffa91486c8c3e4861c3 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 29 Jul 2017 16:28:52 +0300 Subject: [PATCH 03/22] Update plist file for intents extension --- SiriIntents/Info.plist | 74 ++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 38 deletions(-) diff --git a/SiriIntents/Info.plist b/SiriIntents/Info.plist index f0d8ab808..87df545b8 100644 --- a/SiriIntents/Info.plist +++ b/SiriIntents/Info.plist @@ -1,42 +1,40 @@ - - CFBundleDevelopmentRegion - en - CFBundleDisplayName - SiriIntents - CFBundleExecutable - $(EXECUTABLE_NAME) - CFBundleIdentifier - $(PRODUCT_BUNDLE_IDENTIFIER) - CFBundleInfoDictionaryVersion - 6.0 - CFBundleName - $(PRODUCT_NAME) - CFBundlePackageType - XPC! - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - NSExtension - - NSExtensionAttributes - - IntentsRestrictedWhileLocked - - IntentsSupported - - INSendMessageIntent - INSearchForMessagesIntent - INSetMessageAttributeIntent - - - NSExtensionPointIdentifier - com.apple.intents-service - NSExtensionPrincipalClass - IntentHandler - - + + CFBundleDevelopmentRegion + en + CFBundleDisplayName + SiriIntents + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + XPC! + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + NSExtension + + NSExtensionAttributes + + IntentsRestrictedWhileLocked + + IntentsSupported + + INStartAudioCallIntent + + + NSExtensionPointIdentifier + com.apple.intents-service + NSExtensionPrincipalClass + IntentHandler + + From cedb3f39514bfe25c91c6239e7f89a12aa5adc6a Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 29 Jul 2017 16:30:31 +0300 Subject: [PATCH 04/22] Update project file --- Riot.xcodeproj/project.pbxproj | 58 ++++++++++++++++++++++++++++++++++ 1 file changed, 58 insertions(+) diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 124ff3656..0c45d567d 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -43,6 +43,8 @@ 32D392191EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32D392171EB9B7AB009A2BAF /* DirectoryServerDetailTableViewCell.xib */; }; 32FD0A3D1EB0CD9B0072B066 /* BugReportViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */; }; 32FD0A3E1EB0CD9B0072B066 /* BugReportViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */; }; + 7A5A792D23877A47F33ADF07 /* libPods-SiriIntents.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 372FB22949C5652FF01D9793 /* libPods-SiriIntents.a */; }; + 9297595D1F2C8D4500535D3B /* Intents.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9297595C1F2C8D4500535D3B /* Intents.framework */; }; 92E963B51F28D907008FDAF5 /* IntentHandler.m in Sources */ = {isa = PBXBuildFile; fileRef = 92E963B41F28D907008FDAF5 /* IntentHandler.m */; }; 92E963B91F28D907008FDAF5 /* SiriIntents.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 92E963B11F28D906008FDAF5 /* SiriIntents.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; E2EAC1A4FBD6FE5228584591 /* libPods-Riot.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 7D8737F782E108CFD6908691 /* libPods-Riot.a */; }; @@ -567,11 +569,16 @@ 32FD0A3A1EB0CD9B0072B066 /* BugReportViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = BugReportViewController.h; sourceTree = ""; }; 32FD0A3B1EB0CD9B0072B066 /* BugReportViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = BugReportViewController.m; sourceTree = ""; }; 32FD0A3C1EB0CD9B0072B066 /* BugReportViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = BugReportViewController.xib; sourceTree = ""; }; + 372FB22949C5652FF01D9793 /* libPods-SiriIntents.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-SiriIntents.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 47EC817AA9DA11B39694B1E0 /* Pods-SiriIntents.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SiriIntents.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SiriIntents/Pods-SiriIntents.debug.xcconfig"; sourceTree = ""; }; 7D8737F782E108CFD6908691 /* libPods-Riot.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-Riot.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 9297595B1F2C8A9A00535D3B /* SiriIntents.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = SiriIntents.entitlements; sourceTree = ""; }; + 9297595C1F2C8D4500535D3B /* Intents.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Intents.framework; path = System/Library/Frameworks/Intents.framework; sourceTree = SDKROOT; }; 92E963B11F28D906008FDAF5 /* SiriIntents.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = SiriIntents.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 92E963B31F28D907008FDAF5 /* IntentHandler.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = IntentHandler.h; sourceTree = ""; }; 92E963B41F28D907008FDAF5 /* IntentHandler.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = IntentHandler.m; sourceTree = ""; }; 92E963B61F28D907008FDAF5 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + E22DCA997BE673CD67E839B9 /* Pods-SiriIntents.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SiriIntents.release.xcconfig"; path = "Pods/Target Support Files/Pods-SiriIntents/Pods-SiriIntents.release.xcconfig"; sourceTree = ""; }; F0131DE31F2200D600CBF707 /* RiotSplitViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = RiotSplitViewController.h; sourceTree = ""; }; F0131DE41F2200D600CBF707 /* RiotSplitViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = RiotSplitViewController.m; sourceTree = ""; }; F02C1A831E8EB04C0045A404 /* PeopleViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PeopleViewController.h; sourceTree = ""; }; @@ -1142,6 +1149,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 9297595D1F2C8D4500535D3B /* Intents.framework in Frameworks */, + 7A5A792D23877A47F33ADF07 /* libPods-SiriIntents.a in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -1219,7 +1228,9 @@ 6567B0BBF3C05D7F7A7F79CC /* Frameworks */ = { isa = PBXGroup; children = ( + 9297595C1F2C8D4500535D3B /* Intents.framework */, 7D8737F782E108CFD6908691 /* libPods-Riot.a */, + 372FB22949C5652FF01D9793 /* libPods-SiriIntents.a */, ); name = Frameworks; sourceTree = ""; @@ -1230,6 +1241,7 @@ 92E963B31F28D907008FDAF5 /* IntentHandler.h */, 92E963B41F28D907008FDAF5 /* IntentHandler.m */, 92E963B61F28D907008FDAF5 /* Info.plist */, + 9297595B1F2C8A9A00535D3B /* SiriIntents.entitlements */, ); path = SiriIntents; sourceTree = ""; @@ -1239,6 +1251,8 @@ children = ( 1129C74A281B080432B1A1A1 /* Pods-Riot.debug.xcconfig */, F9D678EF54918C036FDEDBF9 /* Pods-Riot.release.xcconfig */, + 47EC817AA9DA11B39694B1E0 /* Pods-SiriIntents.debug.xcconfig */, + E22DCA997BE673CD67E839B9 /* Pods-SiriIntents.release.xcconfig */, ); name = Pods; sourceTree = ""; @@ -2113,9 +2127,11 @@ isa = PBXNativeTarget; buildConfigurationList = 92E963BA1F28D907008FDAF5 /* Build configuration list for PBXNativeTarget "SiriIntents" */; buildPhases = ( + B1481C3FD667495EEBD83C1D /* [CP] Check Pods Manifest.lock */, 92E963AD1F28D906008FDAF5 /* Sources */, 92E963AE1F28D906008FDAF5 /* Frameworks */, 92E963AF1F28D906008FDAF5 /* Resources */, + D7AE9EDB223D9ED53DF15ABF /* [CP] Copy Pods Resources */, ); buildRules = ( ); @@ -2179,6 +2195,11 @@ CreatedOnToolsVersion = 8.3.3; DevelopmentTeam = 7J4U792NQT; ProvisioningStyle = Automatic; + SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; + }; }; F094A9A11B78D8F000B1FBBF = { CreatedOnToolsVersion = 6.2; @@ -2633,6 +2654,36 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; showEnvVarsInLog = 0; }; + B1481C3FD667495EEBD83C1D /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Check Pods Manifest.lock"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n"; + showEnvVarsInLog = 0; + }; + D7AE9EDB223D9ED53DF15ABF /* [CP] Copy Pods Resources */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputPaths = ( + ); + name = "[CP] Copy Pods Resources"; + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-SiriIntents/Pods-SiriIntents-resources.sh\"\n"; + showEnvVarsInLog = 0; + }; /* End PBXShellScriptBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -2925,12 +2976,15 @@ /* Begin XCBuildConfiguration section */ 92E963BB1F28D907008FDAF5 /* Debug */ = { isa = XCBuildConfiguration; + baseConfigurationReference = 47EC817AA9DA11B39694B1E0 /* Pods-SiriIntents.debug.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = SiriIntents/SiriIntents.entitlements; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = 7J4U792NQT; + ENABLE_BITCODE = NO; INFOPLIST_FILE = SiriIntents/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; @@ -2942,13 +2996,16 @@ }; 92E963BC1F28D907008FDAF5 /* Release */ = { isa = XCBuildConfiguration; + baseConfigurationReference = E22DCA997BE673CD67E839B9 /* Pods-SiriIntents.release.xcconfig */; buildSettings = { CLANG_ANALYZER_NONNULL = YES; CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CODE_SIGN_ENTITLEMENTS = SiriIntents/SiriIntents.entitlements; COPY_PHASE_STRIP = NO; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = 7J4U792NQT; + ENABLE_BITCODE = NO; INFOPLIST_FILE = SiriIntents/Info.plist; IPHONEOS_DEPLOYMENT_TARGET = 10.3; LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @executable_path/../../Frameworks"; @@ -3126,6 +3183,7 @@ 92E963BC1F28D907008FDAF5 /* Release */, ); defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; }; F094A99D1B78D8F000B1FBBF /* Build configuration list for PBXProject "Riot" */ = { isa = XCConfigurationList; From d5366c85b92b10f0caa860ca44cc088666cbb1fb Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 29 Jul 2017 16:34:51 +0300 Subject: [PATCH 05/22] Update App group info --- Riot.xcodeproj/project.pbxproj | 3 +++ Riot/Riot.entitlements | 4 ++++ SiriIntents/SiriIntents.entitlements | 10 ++++++++++ 3 files changed, 17 insertions(+) create mode 100644 SiriIntents/SiriIntents.entitlements diff --git a/Riot.xcodeproj/project.pbxproj b/Riot.xcodeproj/project.pbxproj index 0c45d567d..95556f166 100644 --- a/Riot.xcodeproj/project.pbxproj +++ b/Riot.xcodeproj/project.pbxproj @@ -2206,6 +2206,9 @@ DevelopmentTeam = 7J4U792NQT; ProvisioningStyle = Automatic; SystemCapabilities = { + com.apple.ApplicationGroups.iOS = { + enabled = 1; + }; com.apple.Push = { enabled = 1; }; diff --git a/Riot/Riot.entitlements b/Riot/Riot.entitlements index 3a9826778..4094cf66e 100644 --- a/Riot/Riot.entitlements +++ b/Riot/Riot.entitlements @@ -13,5 +13,9 @@ com.apple.developer.siri + com.apple.security.application-groups + + group.org.matrix + diff --git a/SiriIntents/SiriIntents.entitlements b/SiriIntents/SiriIntents.entitlements new file mode 100644 index 000000000..95ba9ff62 --- /dev/null +++ b/SiriIntents/SiriIntents.entitlements @@ -0,0 +1,10 @@ + + + + + com.apple.security.application-groups + + group.org.matrix + + + From 18a2576b9c17027b0c72b81e82df226464b8bf93 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sun, 30 Jul 2017 16:19:27 +0300 Subject: [PATCH 06/22] Update IntentHandler --- SiriIntents/IntentHandler.m | 171 +++++++++++++++++++++++------------- 1 file changed, 112 insertions(+), 59 deletions(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 5e3fa7d20..ea9d8a943 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -16,20 +16,10 @@ #import "IntentHandler.h" -#import "MXKAccountManager.h" #import "MXKAccount.h" -#import "MXSession.h" +#import "MXKAccountManager.h" #import "MXFileStore.h" -//#import - -// As an example, this class is set up to handle Message intents. -// You will want to replace this or add other intents as appropriate. -// The intents you wish to handle must be declared in the extension's Info.plist. - -// You can test your example integration by saying things to Siri like: -// "Send a message using " -// " John saying hello" -// "Search for messages in " +#import "MXSession.h" @interface IntentHandler () @@ -37,74 +27,137 @@ @implementation IntentHandler -- (id)handlerForIntent:(INIntent *)intent { - id handler = nil; - - if ([intent isKindOfClass:INStartAudioCallIntent.class]) - { - handler = self; - } - - return handler; +- (id)handlerForIntent:(INIntent *)intent +{ + return self; } #pragma mark - INStartAudioCallIntentHandling - (void)resolveContactsForStartAudioCall:(INStartAudioCallIntent *)intent withCompletion:(void (^)(NSArray * _Nonnull))completion { - MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; - MXSession *session = [[MXSession alloc] initWithMatrixRestClient:account.mxRestClient]; - [session setStore:[[MXFileStore alloc] init] success:^{ - NSLog(@"Super"); - } failure:^(NSError *error) { - NSLog(@"Fail"); - }]; - - // NSArray *recipients = intent.contacts; - // // If no recipients were provided we'll need to prompt for a value. - // if (recipients.count == 0) { - // completion(@[[INPersonResolutionResult needsValue]]); - // return; - // } - // NSMutableArray *resolutionResults = [NSMutableArray array]; - // - // for (INPerson *recipient in recipients) { - // NSArray *matchingContacts = @[recipient]; // Implement your contact matching logic here to create an array of matching contacts - // if (matchingContacts.count > 1) { - // // We need Siri's help to ask user to pick one from the matches. - // [resolutionResults addObject:[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:matchingContacts]]; - // - // } else if (matchingContacts.count == 1) { - // // We have exactly one matching contact - // [resolutionResults addObject:[INPersonResolutionResult successWithResolvedPerson:recipient]]; - // } else { - // // We have no contacts matching the description provided - // [resolutionResults addObject:[INPersonResolutionResult unsupported]]; - // } - // } - // - // completion(resolutionResults); + NSArray *contacts = intent.contacts; + if (contacts.count == 0) + { + completion(@[[INPersonResolutionResult needsValue]]); + return; + } + else + { + // We don't iterate over array of contacts from passed intent + // since it's hard to imagine scenario with several callee + // so we just extract the first one + INPerson *callee = contacts.firstObject; + + // Check if the user has selected right callee among several candidates from previous resolution process run + if (callee.customIdentifier && callee.customIdentifier.length) + { + completion(@[[INPersonResolutionResult successWithResolvedPerson:callee]]); + return; + } + + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + if (account) + { + MXSession *session = [[MXSession alloc] initWithMatrixRestClient:account.mxRestClient]; + [session setStore:[[MXFileStore alloc] init] success:^{ + + // Find all users with whom direct chats are existed + NSMutableSet *directUserIDs = [NSMutableSet set]; + for (MXRoom *room in session.rooms) + { + if (room.directUserId) + [directUserIDs addObject:room.directUserId]; + } + + NSMutableArray *matchingPersons = [NSMutableArray array]; + for (NSString *userID in directUserIDs.allObjects) + { + MXUser *user = [session userWithUserId:userID]; + if (!user.displayname) + continue; + + if (!NSEqualRanges([user.displayname rangeOfString:callee.displayName options:NSCaseInsensitiveSearch], (NSRange){NSNotFound,0})) + { + INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; + INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle + nameComponents:nil + displayName:user.displayname + image:nil + contactIdentifier:nil + customIdentifier:user.userId]; + + [matchingPersons addObject:person]; + } + } + + if (matchingPersons.count == 0) + { + completion(@[[INPersonResolutionResult unsupported]]); + } + else if (matchingPersons.count == 1) + { + completion(@[[INPersonResolutionResult successWithResolvedPerson:matchingPersons.firstObject]]); + } + else + { + completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:matchingPersons]]); + } + + } failure:^(NSError *error) { + // TODO: Maybe handle this in another way + completion(@[[INPersonResolutionResult unsupported]]); + }]; + } + else + { + // If user hasn't logged in yet just pass a blank INPerson instance and handle this situation in confirmStartAudioCall:completion: + completion(@[[INPersonResolutionResult successWithResolvedPerson:[INPerson new]]]); + } + } } - (void)confirmStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion { INStartAudioCallIntentResponse *response = nil; + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + if (account) + { #if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined MX_CALL_STACK_JINGLE - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:userActivity]; + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:userActivity]; #else - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; #endif + } + else + { + // User hasn't logged in + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureRequiringAppLaunch userActivity:nil]; + } completion(response); } - (void)handleStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion { - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; - // userActivity.userInfo = @{ @"handle": [NSString stringWithFormat:@"TGCA%d", next.firstObject.userId] }; - INStartAudioCallIntentResponse *response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp userActivity:userActivity]; + INStartAudioCallIntentResponse *response = nil; + + INPerson *person = intent.contacts.firstObject; + if (person && person.customIdentifier) + { + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; + userActivity.userInfo = @{ @"userID" : person.customIdentifier }; + + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp + userActivity:userActivity]; + } + else + { + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailure userActivity:nil]; + } + completion(response); } From ffd6b96d50ba209f216740eafdbbd02446515209 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sun, 30 Jul 2017 19:49:19 +0300 Subject: [PATCH 07/22] Add NSSiriUsageDescription to Info.plist --- Riot/Info.plist | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Riot/Info.plist b/Riot/Info.plist index 5346ea088..fde652af4 100644 --- a/Riot/Info.plist +++ b/Riot/Info.plist @@ -49,6 +49,8 @@ The microphone is used to take videos, make calls. NSPhotoLibraryUsageDescription The photo library is used to send photos and videos. + NSSiriUsageDescription + Use Siri to make calls UIBackgroundModes audio From bfbeb4c549de277d422e5587abbbff9b0859153a Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Tue, 1 Aug 2017 16:05:53 +0300 Subject: [PATCH 08/22] Update IntentHandler to reflect last changes in MatrixSDK --- SiriIntents/IntentHandler.m | 93 +++++++++++++++++++++---------------- 1 file changed, 53 insertions(+), 40 deletions(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index ea9d8a943..0190db099 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -27,6 +27,16 @@ @implementation IntentHandler +- (instancetype)init +{ + self = [super init]; + if (self) + { + [MXSDKOptions sharedInstance].applicationGroupIdentifier = @"group.org.matrix"; + } + return self; +} + - (id)handlerForIntent:(INIntent *)intent { return self; @@ -59,55 +69,58 @@ MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; if (account) { - MXSession *session = [[MXSession alloc] initWithMatrixRestClient:account.mxRestClient]; - [session setStore:[[MXFileStore alloc] init] success:^{ + MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:account.mxCredentials]; + [fileStore asyncRoomsSummaries:^(NSArray * _Nonnull roomsSummaries) { // Find all users with whom direct chats are existed NSMutableSet *directUserIDs = [NSMutableSet set]; - for (MXRoom *room in session.rooms) + for (MXRoomSummary *summary in roomsSummaries) { - if (room.directUserId) - [directUserIDs addObject:room.directUserId]; + if (summary.isDirect) + [directUserIDs addObject:summary.directUserId]; } - NSMutableArray *matchingPersons = [NSMutableArray array]; - for (NSString *userID in directUserIDs.allObjects) - { - MXUser *user = [session userWithUserId:userID]; - if (!user.displayname) - continue; + [fileStore asyncUsers:^(NSArray * _Nonnull users) { - if (!NSEqualRanges([user.displayname rangeOfString:callee.displayName options:NSCaseInsensitiveSearch], (NSRange){NSNotFound,0})) + // Find users with whom we have a direct chat and whose display name contains string presented us by Siri + NSMutableArray *matchingPersons = [NSMutableArray array]; + for (MXUser *user in users) { - INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; - INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle - nameComponents:nil - displayName:user.displayname - image:nil - contactIdentifier:nil - customIdentifier:user.userId]; - - [matchingPersons addObject:person]; + if ([directUserIDs containsObject:user.userId]) + { + if (!user.displayname) + continue; + + if (!NSEqualRanges([user.displayname rangeOfString:callee.displayName options:NSCaseInsensitiveSearch], (NSRange){NSNotFound,0})) + { + INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; + INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle + nameComponents:nil + displayName:user.displayname + image:nil + contactIdentifier:nil + customIdentifier:user.userId]; + + [matchingPersons addObject:person]; + } + } } - } - - if (matchingPersons.count == 0) - { - completion(@[[INPersonResolutionResult unsupported]]); - } - else if (matchingPersons.count == 1) - { - completion(@[[INPersonResolutionResult successWithResolvedPerson:matchingPersons.firstObject]]); - } - else - { - completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:matchingPersons]]); - } - - } failure:^(NSError *error) { - // TODO: Maybe handle this in another way - completion(@[[INPersonResolutionResult unsupported]]); - }]; + + if (matchingPersons.count == 0) + { + completion(@[[INPersonResolutionResult unsupported]]); + } + else if (matchingPersons.count == 1) + { + completion(@[[INPersonResolutionResult successWithResolvedPerson:matchingPersons.firstObject]]); + } + else + { + completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:matchingPersons]]); + } + + } failure:nil]; + } failure:nil]; } else { From fcf2948bc1d5984824e972d0109d603daac84aa2 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Fri, 4 Aug 2017 10:59:46 +0300 Subject: [PATCH 09/22] Set App Group ID to MXSDKOptions singleton instance --- Riot/AppDelegate.m | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index b29c573b3..63a6e687a 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1447,6 +1447,9 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN // Use UIKit BackgroundTask for handling background tasks in the SDK sdkOptions.backgroundModeHandler = [[MXUIKitBackgroundModeHandler alloc] init]; + // Use shared container to share data with app extensions + sdkOptions.applicationGroupIdentifier = @"group.org.matrix"; + // Disable long press on event in bubble cells [MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent:YES]; From 28f854e1d7e37455d73ccd2fd9a8a1ef0df3e410 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Fri, 25 Aug 2017 23:43:38 +0300 Subject: [PATCH 10/22] Improvements in resolution process --- SiriIntents/IntentHandler.m | 122 ++++++++++++++++++++++++++---------- 1 file changed, 88 insertions(+), 34 deletions(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 0190db099..9a5c146d9 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -59,12 +59,19 @@ // so we just extract the first one INPerson *callee = contacts.firstObject; - // Check if the user has selected right callee among several candidates from previous resolution process run + // If this method is called after selection of the appropriate user, it will hold userId of an user to whom we must call + NSString *selectedUserId; + + // Check if the user has selected appropriate room among several candidates from previous resolution process run if (callee.customIdentifier && callee.customIdentifier.length) { completion(@[[INPersonResolutionResult successWithResolvedPerson:callee]]); return; } + else + { + selectedUserId = callee.personHandle.value; + } MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; if (account) @@ -72,61 +79,108 @@ MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:account.mxCredentials]; [fileStore asyncRoomsSummaries:^(NSArray * _Nonnull roomsSummaries) { - // Find all users with whom direct chats are existed - NSMutableSet *directUserIDs = [NSMutableSet set]; + // Contains userIds of all users with whom the current user has direct chats + NSMutableArray *directUserIds = [NSMutableArray array]; + + // Contains room summaries for all direct rooms connected with particular userId + NSMutableDictionary *> *roomSummaries = [NSMutableDictionary dictionary]; + for (MXRoomSummary *summary in roomsSummaries) { + // TODO: We also need to check if joined room members count equals 2 + // It is pointlessly to save rooms with 1 joined member or room with more than 2 joined members if (summary.isDirect) - [directUserIDs addObject:summary.directUserId]; + { + NSString *diretUserId = summary.directUserId; + + // Collect room summaries only for specified user + if (selectedUserId && ![diretUserId isEqualToString:selectedUserId]) + continue; + + // Save userId + [directUserIds addObject:diretUserId]; + + // Save associated with diretUserId room summary + NSMutableArray *userRoomSummaries = roomSummaries[diretUserId]; + if (userRoomSummaries) + [userRoomSummaries addObject:summary]; + else + roomSummaries[diretUserId] = [NSMutableArray arrayWithObject:summary]; + } } - [fileStore asyncUsers:^(NSArray * _Nonnull users) { + [fileStore UsersWithUserIds:directUserIds success:^(NSArray * _Nonnull users) { - // Find users with whom we have a direct chat and whose display name contains string presented us by Siri - NSMutableArray *matchingPersons = [NSMutableArray array]; + // Find users whose display name contains string presented us by Siri + NSMutableArray *matchingUsers = [NSMutableArray array]; for (MXUser *user in users) { - if ([directUserIDs containsObject:user.userId]) + if (!user.displayname) + continue; + + if (!NSEqualRanges([callee.displayName rangeOfString:user.displayname options:NSCaseInsensitiveSearch], (NSRange){NSNotFound,0})) { - if (!user.displayname) - continue; + [matchingUsers addObject:user]; + } + } + + NSMutableArray *persons = [NSMutableArray array]; + + if (matchingUsers.count == 1) + { + MXUser *user = matchingUsers.firstObject; + + // Provide to the user a list of direct rooms to choose from + NSArray *summaries = roomSummaries[user.userId]; + for (MXRoomSummary *summary in summaries) + { + INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; - if (!NSEqualRanges([user.displayname rangeOfString:callee.displayName options:NSCaseInsensitiveSearch], (NSRange){NSNotFound,0})) - { - INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; - INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle - nameComponents:nil - displayName:user.displayname - image:nil - contactIdentifier:nil - customIdentifier:user.userId]; - - [matchingPersons addObject:person]; - } + NSString *displayName = summary.displayname ? summary.displayname : user.displayname; + + INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle + nameComponents:nil + displayName:displayName + image:nil + contactIdentifier:nil + customIdentifier:summary.roomId]; + + [persons addObject:person]; + } + } + else if (matchingUsers.count > 1) + { + // Provide to the user a list of users to choose from + // This is the case when there are several users with the same name + for (MXUser *user in matchingUsers) + { + INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; + INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle + nameComponents:nil + displayName:user.displayname + image:nil + contactIdentifier:nil + customIdentifier:nil]; + + [persons addObject:person]; } } - if (matchingPersons.count == 0) + if (persons.count == 0) { completion(@[[INPersonResolutionResult unsupported]]); } - else if (matchingPersons.count == 1) + else if (persons.count == 1) { - completion(@[[INPersonResolutionResult successWithResolvedPerson:matchingPersons.firstObject]]); + completion(@[[INPersonResolutionResult successWithResolvedPerson:persons.firstObject]]); } else { - completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:matchingPersons]]); + completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:persons]]); } - - } failure:nil]; + }]; } failure:nil]; } - else - { - // If user hasn't logged in yet just pass a blank INPerson instance and handle this situation in confirmStartAudioCall:completion: - completion(@[[INPersonResolutionResult successWithResolvedPerson:[INPerson new]]]); - } } } @@ -161,7 +215,7 @@ if (person && person.customIdentifier) { NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; - userActivity.userInfo = @{ @"userID" : person.customIdentifier }; + userActivity.userInfo = @{ @"roomID" : person.customIdentifier }; response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp userActivity:userActivity]; From 77d481d00855f59190a5e35548ce3cba67ac4518 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 15:03:32 +0300 Subject: [PATCH 11/22] Handle multiple contacts passed to intent --- SiriIntents/IntentHandler.m | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 9a5c146d9..48a7d41d5 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -65,7 +65,14 @@ // Check if the user has selected appropriate room among several candidates from previous resolution process run if (callee.customIdentifier && callee.customIdentifier.length) { - completion(@[[INPersonResolutionResult successWithResolvedPerson:callee]]); + // If callee will have the same name as one of the contact in the system contacts app + // Siri will pass us this contact in the intent.contacts array and we must provide the same count of + // resolution results as elements count in the intent.contact. + // So we just pass the same result at all iterations + NSMutableArray *resolutionResults = [NSMutableArray array]; + for (NSInteger i = 0; i < contacts.count; ++i) + [resolutionResults addObject:[INPersonResolutionResult successWithResolvedPerson:callee]]; + completion(resolutionResults); return; } else From 232eab3960a18e80ec16e5ab5a1b007e6bd5d7d6 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 15:11:03 +0300 Subject: [PATCH 12/22] Update comments --- SiriIntents/IntentHandler.m | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 48a7d41d5..a455a82f4 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -62,7 +62,7 @@ // If this method is called after selection of the appropriate user, it will hold userId of an user to whom we must call NSString *selectedUserId; - // Check if the user has selected appropriate room among several candidates from previous resolution process run + // Check if the user has selected right room among several direct rooms from previous resolution process run if (callee.customIdentifier && callee.customIdentifier.length) { // If callee will have the same name as one of the contact in the system contacts app @@ -77,6 +77,7 @@ } else { + // This resolution process run after selecting appropriate user among suggested user list selectedUserId = callee.personHandle.value; } @@ -143,6 +144,7 @@ { INPersonHandle *personHandle = [[INPersonHandle alloc] initWithValue:user.userId type:INPersonHandleTypeUnknown]; + // For rooms we try to use room display name NSString *displayName = summary.displayname ? summary.displayname : user.displayname; INPerson *person = [[INPerson alloc] initWithPersonHandle:personHandle From f61731a9eeca4778d19ef6ca5561cd5f49488abe Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 15:14:18 +0300 Subject: [PATCH 13/22] Use set for directUserIds --- SiriIntents/IntentHandler.m | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index a455a82f4..ed50d2cb6 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -88,7 +88,8 @@ [fileStore asyncRoomsSummaries:^(NSArray * _Nonnull roomsSummaries) { // Contains userIds of all users with whom the current user has direct chats - NSMutableArray *directUserIds = [NSMutableArray array]; + // Use set to avoid duplicates + NSMutableSet *directUserIds = [NSMutableSet set]; // Contains room summaries for all direct rooms connected with particular userId NSMutableDictionary *> *roomSummaries = [NSMutableDictionary dictionary]; @@ -117,7 +118,7 @@ } } - [fileStore UsersWithUserIds:directUserIds success:^(NSArray * _Nonnull users) { + [fileStore UsersWithUserIds:directUserIds.allObjects success:^(NSArray * _Nonnull users) { // Find users whose display name contains string presented us by Siri NSMutableArray *matchingUsers = [NSMutableArray array]; From acf4e965f4b1e0aafc60abc109fc649f5af1e098 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 15:19:59 +0300 Subject: [PATCH 14/22] Fix method name --- SiriIntents/IntentHandler.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index ed50d2cb6..d4b97acdb 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -118,7 +118,7 @@ } } - [fileStore UsersWithUserIds:directUserIds.allObjects success:^(NSArray * _Nonnull users) { + [fileStore asyncUsersWithUserIds:directUserIds.allObjects success:^(NSArray * _Nonnull users) { // Find users whose display name contains string presented us by Siri NSMutableArray *matchingUsers = [NSMutableArray array]; From 3ab16243daa58904b0753097eb93f9799e17c688 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 15:32:12 +0300 Subject: [PATCH 15/22] Change application group id --- Riot/AppDelegate.m | 2 +- SiriIntents/IntentHandler.m | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/AppDelegate.m b/Riot/AppDelegate.m index 63a6e687a..da95f73e2 100644 --- a/Riot/AppDelegate.m +++ b/Riot/AppDelegate.m @@ -1448,7 +1448,7 @@ NSString *const kAppDelegateNetworkStatusDidChangeNotification = @"kAppDelegateN sdkOptions.backgroundModeHandler = [[MXUIKitBackgroundModeHandler alloc] init]; // Use shared container to share data with app extensions - sdkOptions.applicationGroupIdentifier = @"group.org.matrix"; + sdkOptions.applicationGroupIdentifier = @"group.im.vector"; // Disable long press on event in bubble cells [MXKRoomBubbleTableViewCell disableLongPressGestureOnEvent:YES]; diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index d4b97acdb..53bea36c0 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -32,7 +32,7 @@ self = [super init]; if (self) { - [MXSDKOptions sharedInstance].applicationGroupIdentifier = @"group.org.matrix"; + [MXSDKOptions sharedInstance].applicationGroupIdentifier = @"group.im.vector"; } return self; } From 92bcbb60272921d68076245261c99d67a4ca85b5 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 15:40:40 +0300 Subject: [PATCH 16/22] Update entitlements --- Riot/Riot.entitlements | 2 +- SiriIntents/SiriIntents.entitlements | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Riot/Riot.entitlements b/Riot/Riot.entitlements index 4094cf66e..edee34b65 100644 --- a/Riot/Riot.entitlements +++ b/Riot/Riot.entitlements @@ -15,7 +15,7 @@ com.apple.security.application-groups - group.org.matrix + group.im.vector diff --git a/SiriIntents/SiriIntents.entitlements b/SiriIntents/SiriIntents.entitlements index 95ba9ff62..e540aaaec 100644 --- a/SiriIntents/SiriIntents.entitlements +++ b/SiriIntents/SiriIntents.entitlements @@ -4,7 +4,7 @@ com.apple.security.application-groups - group.org.matrix + group.im.vector From 2442d1091d0b20fb128d57f531d8dd99752386ce Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 15:41:15 +0300 Subject: [PATCH 17/22] Update Siri usage description --- Riot/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Info.plist b/Riot/Info.plist index fde652af4..10aa3a880 100644 --- a/Riot/Info.plist +++ b/Riot/Info.plist @@ -50,7 +50,7 @@ NSPhotoLibraryUsageDescription The photo library is used to send photos and videos. NSSiriUsageDescription - Use Siri to make calls + Siri is used to perform calls even from the lock screen UIBackgroundModes audio From 544177fc93bba97e375fcc16863750a625f49da0 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 16:04:31 +0300 Subject: [PATCH 18/22] Update Podfile --- Podfile | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/Podfile b/Podfile index dee294bb5..bc29ced59 100644 --- a/Podfile +++ b/Podfile @@ -5,10 +5,9 @@ source 'https://github.com/CocoaPods/Specs.git' target "Riot" do - # Different flavours of pods to MatrixKit # The tagged version on which this version of Riot has been built -pod 'MatrixKit', '0.5.2' +pod 'MatrixKit', '0.6.2' # The lastest release available on the CocoaPods repository #pod 'MatrixKit' @@ -41,3 +40,33 @@ pod 'DTCoreText', :inhibit_warnings => true end +target "SiriIntents" do + +pod 'GoogleAnalytics' +# The Google WebRTC stack +pod 'WebRTC', '58.17.16937' +# OLMKit for crypto +pod 'OLMKit' +#pod 'OLMKit', :path => '../olm/OLMKit.podspec' +pod 'Realm', '~> 2.8.1' + +# The tagged version on which this version of Riot share extension has been built +pod 'MatrixKit/AppExtension', '0.6.2' + +# The lastest release available on the CocoaPods repository +#pod 'MatrixKit/AppExtension' + +# The develop branch version +#pod 'MatrixSDK', :git => 'https://github.com/matrix-org/matrix-ios-sdk.git', :branch => 'develop' +#pod 'MatrixKit/AppExtension', :git => 'https://github.com/matrix-org/matrix-ios-kit.git', :branch => 'develop' + +# The one used for developing both MatrixSDK and MatrixKit +# Note that MatrixSDK must be cloned into a folder called matrix-ios-sdk next to the MatrixKit folder +#pod 'MatrixSDK', :path => '../matrix-ios-sdk/MatrixSDK.podspec' +#pod 'MatrixKit/AppExtension', :path => '../matrix-ios-kit/MatrixKit.podspec' + +# Remove warnings from "bad" pods +pod 'OLMKit', :inhibit_warnings => true +pod 'cmark', :inhibit_warnings => true + +end From 283e2b4944f19f8eecf2152ebdcd7a8f85f5987e Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 16:35:35 +0300 Subject: [PATCH 19/22] Update async method signature --- SiriIntents/IntentHandler.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 53bea36c0..8ccd42f5d 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -188,7 +188,7 @@ { completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:persons]]); } - }]; + } failure:nil]; } failure:nil]; } } From e5a7dcf9c23cab9e1942aaa7de90024d488c792d Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Sat, 26 Aug 2017 17:46:12 +0300 Subject: [PATCH 20/22] Add video calls support --- SiriIntents/Info.plist | 1 + SiriIntents/IntentHandler.m | 149 +++++++++++++++++++++++++----------- 2 files changed, 104 insertions(+), 46 deletions(-) diff --git a/SiriIntents/Info.plist b/SiriIntents/Info.plist index 87df545b8..9fa5f075e 100644 --- a/SiriIntents/Info.plist +++ b/SiriIntents/Info.plist @@ -29,6 +29,7 @@ IntentsSupported INStartAudioCallIntent + INStartVideoCallIntent NSExtensionPointIdentifier diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 8ccd42f5d..8b561895b 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -21,7 +21,7 @@ #import "MXFileStore.h" #import "MXSession.h" -@interface IntentHandler () +@interface IntentHandler () @end @@ -46,7 +46,108 @@ - (void)resolveContactsForStartAudioCall:(INStartAudioCallIntent *)intent withCompletion:(void (^)(NSArray * _Nonnull))completion { - NSArray *contacts = intent.contacts; + [self resolveContacts:intent.contacts withCompletion:completion]; +} + +- (void)confirmStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion +{ + INStartAudioCallIntentResponse *response = nil; + + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + if (account) + { +#if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined MX_CALL_STACK_JINGLE + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:userActivity]; +#else + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; +#endif + } + else + { + // User hasn't logged in + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureRequiringAppLaunch userActivity:nil]; + } + + completion(response); +} + +- (void)handleStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion +{ + INStartAudioCallIntentResponse *response = nil; + + INPerson *person = intent.contacts.firstObject; + if (person && person.customIdentifier) + { + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; + userActivity.userInfo = @{ @"roomID" : person.customIdentifier }; + + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp + userActivity:userActivity]; + } + else + { + response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailure userActivity:nil]; + } + + completion(response); +} + +#pragma mark - INStartVideoCallIntentHandling + +- (void)resolveContactsForStartVideoCall:(INStartVideoCallIntent *)intent withCompletion:(void (^)(NSArray * _Nonnull))completion +{ + [self resolveContacts:intent.contacts withCompletion:completion]; +} + +- (void)confirmStartVideoCall:(INStartVideoCallIntent *)intent completion:(void (^)(INStartVideoCallIntentResponse * _Nonnull))completion +{ + INStartVideoCallIntentResponse *response = nil; + + MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; + if (account) + { +#if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined MX_CALL_STACK_JINGLE + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartVideoCallIntent.class)]; + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeReady userActivity:userActivity]; +#else + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; +#endif + } + else + { + // User hasn't logged in + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailureRequiringAppLaunch userActivity:nil]; + } + + completion(response); +} + +- (void)handleStartVideoCall:(INStartVideoCallIntent *)intent completion:(void (^)(INStartVideoCallIntentResponse * _Nonnull))completion +{ + INStartVideoCallIntentResponse *response = nil; + + INPerson *person = intent.contacts.firstObject; + if (person && person.customIdentifier) + { + NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartVideoCallIntent.class)]; + userActivity.userInfo = @{ @"roomID" : person.customIdentifier }; + + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeContinueInApp + userActivity:userActivity]; + } + else + { + response = [[INStartVideoCallIntentResponse alloc] initWithCode:INStartVideoCallIntentResponseCodeFailure userActivity:nil]; + } + + completion(response); +} + +#pragma mark - Private + +- (void)resolveContacts:(nullable NSArray *)contacts withCompletion:(void (^)(NSArray * _Nonnull))completion +{ if (contacts.count == 0) { completion(@[[INPersonResolutionResult needsValue]]); @@ -194,48 +295,4 @@ } } -- (void)confirmStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion -{ - INStartAudioCallIntentResponse *response = nil; - - MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject; - if (account) - { -#if defined MX_CALL_STACK_OPENWEBRTC || defined MX_CALL_STACK_ENDPOINT || defined MX_CALL_STACK_JINGLE - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeReady userActivity:userActivity]; -#else - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureCallingServiceNotAvailable userActivity:nil]; -#endif - } - else - { - // User hasn't logged in - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailureRequiringAppLaunch userActivity:nil]; - } - - completion(response); -} - -- (void)handleStartAudioCall:(INStartAudioCallIntent *)intent completion:(void (^)(INStartAudioCallIntentResponse * _Nonnull))completion -{ - INStartAudioCallIntentResponse *response = nil; - - INPerson *person = intent.contacts.firstObject; - if (person && person.customIdentifier) - { - NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass(INStartAudioCallIntent.class)]; - userActivity.userInfo = @{ @"roomID" : person.customIdentifier }; - - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeContinueInApp - userActivity:userActivity]; - } - else - { - response = [[INStartAudioCallIntentResponse alloc] initWithCode:INStartAudioCallIntentResponseCodeFailure userActivity:nil]; - } - - completion(response); -} - @end From 026f98f16802ca55232803984d3b3e8fd378696e Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Thu, 31 Aug 2017 11:11:09 +0300 Subject: [PATCH 21/22] Simplify customIdentifier check --- SiriIntents/IntentHandler.m | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SiriIntents/IntentHandler.m b/SiriIntents/IntentHandler.m index 8b561895b..c21d84401 100644 --- a/SiriIntents/IntentHandler.m +++ b/SiriIntents/IntentHandler.m @@ -164,7 +164,7 @@ NSString *selectedUserId; // Check if the user has selected right room among several direct rooms from previous resolution process run - if (callee.customIdentifier && callee.customIdentifier.length) + if (callee.customIdentifier.length) { // If callee will have the same name as one of the contact in the system contacts app // Siri will pass us this contact in the intent.contacts array and we must provide the same count of From 74cc9bc43d0664b9b9161923787be59053065617 Mon Sep 17 00:00:00 2001 From: Denis Morozov Date: Thu, 31 Aug 2017 11:12:18 +0300 Subject: [PATCH 22/22] Update Siri usage description --- Riot/Info.plist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Riot/Info.plist b/Riot/Info.plist index 10aa3a880..893054b9a 100644 --- a/Riot/Info.plist +++ b/Riot/Info.plist @@ -50,7 +50,7 @@ NSPhotoLibraryUsageDescription The photo library is used to send photos and videos. NSSiriUsageDescription - Siri is used to perform calls even from the lock screen + Siri is used to perform calls even from the lock screen. UIBackgroundModes audio