Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Target SiriIntents: Split IntentHandler into smaller files (#6203) #6365

Merged
merged 18 commits into from
Jul 6, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2c44e1b
Add protocol `ContactResolving`
wtimme Jun 30, 2022
0fda5d7
Let the `IntentHandler` implement `ContactResolving` (#6203)
wtimme Jun 30, 2022
02a56db
Prepare the separation of the contact resolver from the intents handl…
wtimme Jun 30, 2022
f414324
Move the implementation of `ContactResolving` to a dedicated class (#…
wtimme Jun 30, 2022
c21e8e3
Move `ContactResolver` to a dedicated file (#6203)
wtimme Jun 30, 2022
0880e91
Prepare the separation of the `StartAudioCallIntentHandler` from `Int…
wtimme Jun 30, 2022
6977afb
Move the implementation of `INStartAudioCallIntentHandling` to a dedi…
wtimme Jun 30, 2022
6abb4ff
Prepare the separation of the `StartVideoCallIntentHandler` from `Int…
wtimme Jun 30, 2022
6ca57b6
Move the implementation of `INStartVideoCallIntentHandling` to a dedi…
wtimme Jun 30, 2022
52114cc
Prepare the separation of the `SendMessageIntentHandler` from `Intent…
wtimme Jun 30, 2022
4507b66
Move the implementation of `INSendMessageIntentHandling` to a dedicat…
wtimme Jun 30, 2022
e4b12b9
Remove unused property (#6203)
wtimme Jun 30, 2022
3a3b1a9
Return `nil` if the requested intent cannot be handled (#6203)
wtimme Jun 30, 2022
b6ff75b
Initialize the intent handlers _after_ everything else is configured …
wtimme Jun 30, 2022
5a63be1
Add changelog entry
wtimme Jun 30, 2022
3ac7f0a
Move curly braces in Objective-C to dedicated lines
wtimme Jul 2, 2022
4cf16e9
Inject the `ContactResolver` into the intent handlers during initiali…
wtimme Jul 5, 2022
6e761f8
Prefer forward-declaration over import in Objective-C header files
wtimme Jul 5, 2022
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions SiriIntents/ContactResolver/ContactResolver.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//
// Copyright 2022 New Vector 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 <Foundation/Foundation.h>
#import "GeneratedInterface-Swift.h"

NS_ASSUME_NONNULL_BEGIN

@interface ContactResolver: NSObject <ContactResolving>

@end

NS_ASSUME_NONNULL_END
177 changes: 177 additions & 0 deletions SiriIntents/ContactResolver/ContactResolver.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
//
// Copyright 2022 New Vector 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 "ContactResolver.h"
@import Intents;
#import "MXKAccountManager.h"

@implementation ContactResolver

- (void)resolveContacts:(nullable NSArray<INPerson *> *)contacts
withCompletion:(void (^)(NSArray<INPersonResolutionResult *> * _Nonnull))completion
{
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;

// 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 right room among several direct rooms from previous resolution process run
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
// 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
{
// This resolution process run after selecting appropriate user among suggested user list
selectedUserId = callee.personHandle.value;
}

MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
if (account)
{
MXFileStore *fileStore = [[MXFileStore alloc] initWithCredentials:account.mxCredentials];
[fileStore.roomSummaryStore fetchAllSummaries:^(NSArray<id<MXRoomSummaryProtocol>> * _Nonnull summaries) {

// Contains userIds of all users with whom the current user has direct chats
// Use set to avoid duplicates
NSMutableSet<NSString *> *directUserIds = [NSMutableSet set];

// Contains room summaries for all direct rooms connected with particular userId
NSMutableDictionary<NSString *, NSMutableArray<id<MXRoomSummaryProtocol>> *> *roomSummaries = [NSMutableDictionary dictionary];

for (id<MXRoomSummaryProtocol> summary in summaries)
{
// 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)
{
NSString *directUserId = summary.directUserId;

// Collect room summaries only for specified user
if (selectedUserId && ![directUserId isEqualToString:selectedUserId])
continue;

// Save userId
[directUserIds addObject:directUserId];

// Save associated with diretUserId room summary
NSMutableArray<id<MXRoomSummaryProtocol>> *userRoomSummaries = roomSummaries[directUserId];
if (userRoomSummaries)
[userRoomSummaries addObject:summary];
else
roomSummaries[directUserId] = [NSMutableArray arrayWithObject:summary];
}
}

[fileStore asyncUsersWithUserIds:directUserIds.allObjects success:^(NSArray<MXUser *> * _Nonnull users) {

// Find users whose display name contains string presented us by Siri
NSMutableArray<MXUser *> *matchingUsers = [NSMutableArray array];
for (MXUser *user in users)
{
if (!user.displayname)
continue;

if (!NSEqualRanges([callee.displayName rangeOfString:user.displayname options:NSCaseInsensitiveSearch], (NSRange){NSNotFound,0}))
{
[matchingUsers addObject:user];
}
}

NSMutableArray<INPerson *> *persons = [NSMutableArray array];

if (matchingUsers.count == 1)
{
MXUser *user = matchingUsers.firstObject;

// Provide to the user a list of direct rooms to choose from
NSArray<id<MXRoomSummaryProtocol>> *summaries = roomSummaries[user.userId];
for (id<MXRoomSummaryProtocol> summary in summaries)
{
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
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 (persons.count == 0)
{
completion(@[[INPersonResolutionResult unsupported]]);
}
else if (persons.count == 1)
{
completion(@[[INPersonResolutionResult successWithResolvedPerson:persons.firstObject]]);
}
else
{
completion(@[[INPersonResolutionResult disambiguationWithPeopleToDisambiguate:persons]]);
}
} failure:nil];
}];
}
else
{
completion(@[[INPersonResolutionResult notRequired]]);
}
}
}

@end
22 changes: 22 additions & 0 deletions SiriIntents/ContactResolver/ContactResolving.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
//
// Copyright 2022 New Vector 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 Intents

@objc protocol ContactResolving {
func resolveContacts(_ contacts: [INPerson]?,
withCompletion completion: @escaping ([INPersonResolutionResult]) -> Void)
}
Loading