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 1 commit
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
Prev Previous commit
Next Next commit
Move the implementation of INSendMessageIntentHandling to a dedicat…
…ed class (#6203)
  • Loading branch information
wtimme committed Jun 30, 2022
commit 4507b66391cddb8cf6de96db8319f4bc3c64f871
127 changes: 3 additions & 124 deletions SiriIntents/IntentHandler.m
Original file line number Diff line number Diff line change
Expand Up @@ -21,22 +21,17 @@
#import "ContactResolver.h"
#import "StartAudioCallIntentHandler.h"
#import "StartVideoCallIntentHandler.h"
#import "SendMessageIntentHandler.h"

#if __has_include(<MatrixSDK/MXJingleCallStack.h>)
#define CALL_STACK_JINGLE
#endif

@interface IntentHandler () <INSendMessageIntentHandling>
@interface IntentHandler ()

// Build Settings
@property (nonatomic) id<Configurable> configuration;

/**
The room that is currently being used to send a message. This is to ensure a
strong ref is maintained on the `MXRoom` until sending has completed.
*/
@property (nonatomic) MXRoom *selectedRoom;

@property (nonatomic) id<ContactResolving> contactResolver;
@property (nonatomic) id<INStartAudioCallIntentHandling> startAudioCallIntentHandler;
@property (nonatomic) id<INStartVideoCallIntentHandling> startVideoCallIntentHandler;
Expand All @@ -54,7 +49,7 @@ - (instancetype)init
_contactResolver = [[ContactResolver alloc] init];
_startAudioCallIntentHandler = [[StartAudioCallIntentHandler alloc] init];
_startVideoCallIntentHandler = [[StartVideoCallIntentHandler alloc] init];
_sendMessageIntentHandler = self;
_sendMessageIntentHandler = [[SendMessageIntentHandler alloc] init];

// Set static application settings
_configuration = [CommonConfiguration new];
Expand Down Expand Up @@ -95,120 +90,4 @@ - (id)handlerForIntent:(INIntent *)intent
return self;
}

#pragma mark - INSendMessageIntentHandling

- (void)resolveRecipientsForSendMessage:(INSendMessageIntent *)intent completion:(void (^)(NSArray<INSendMessageRecipientResolutionResult *> * _Nonnull))completion
{
[self.contactResolver resolveContacts:intent.recipients withCompletion:completion];
}

- (void)resolveContentForSendMessage:(INSendMessageIntent *)intent withCompletion:(void (^)(INStringResolutionResult * _Nonnull))completion
{
NSString *message = intent.content;
if (message && ![message isEqualToString:@""])
completion([INStringResolutionResult successWithResolvedString:message]);
else
completion([INStringResolutionResult needsValue]);
}

- (void)confirmSendMessage:(INSendMessageIntent *)intent completion:(void (^)(INSendMessageIntentResponse * _Nonnull))completion
{
INSendMessageIntentResponse *response = nil;

MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
if (account)
{
NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendMessageIntent class])];
response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeReady userActivity:userActivity];
}
else
{
// User hasn't logged in
response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeFailureRequiringAppLaunch userActivity:nil];
}

completion(response);
}

- (void)handleSendMessage:(INSendMessageIntent *)intent completion:(void (^)(INSendMessageIntentResponse * _Nonnull))completion
{
void (^completeWithCode)(INSendMessageIntentResponseCode) = ^(INSendMessageIntentResponseCode code) {
NSUserActivity *userActivity = nil;
if (code == INSendMessageIntentResponseCodeSuccess)
userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendMessageIntent class])];
INSendMessageIntentResponse *response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeSuccess
userActivity:userActivity];
completion(response);
};

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

BOOL isEncrypted = NO;
for (id<MXRoomSummaryProtocol> summary in summaries)
{
if ([summary.roomId isEqualToString:roomID])
{
isEncrypted = summary.isEncrypted;
break;
}
}

if (isEncrypted)
{
[MXFileStore setPreloadOptions:0];

MXSession *session = [[MXSession alloc] initWithMatrixRestClient:account.mxRestClient];
MXWeakify(session);
[session setStore:fileStore success:^{
MXStrongifyAndReturnIfNil(session);

self.selectedRoom = [MXRoom loadRoomFromStore:fileStore withRoomId:roomID matrixSession:session];

// Do not warn for unknown devices. We have cross-signing now
session.crypto.warnOnUnknowDevices = NO;

MXWeakify(self);
[self.selectedRoom sendTextMessage:intent.content
threadId:nil
success:^(NSString *eventId) {
completeWithCode(INSendMessageIntentResponseCodeSuccess);
MXStrongifyAndReturnIfNil(self);
self.selectedRoom = nil;
} failure:^(NSError *error) {
completeWithCode(INSendMessageIntentResponseCodeFailure);
MXStrongifyAndReturnIfNil(self);
self.selectedRoom = nil;
}];

} failure:^(NSError *error) {
completeWithCode(INSendMessageIntentResponseCodeFailure);
}];

return;
}

[account.mxRestClient sendTextMessageToRoom:roomID
threadId:nil
text:intent.content
success:^(NSString *eventId) {
completeWithCode(INSendMessageIntentResponseCodeSuccess);
}
failure:^(NSError *error) {
completeWithCode(INSendMessageIntentResponseCodeFailure);
}];

}];
}
else
{
completeWithCode(INSendMessageIntentResponseCodeFailure);
}
}

@end
26 changes: 26 additions & 0 deletions SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.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 Intents;

NS_ASSUME_NONNULL_BEGIN

@interface SendMessageIntentHandler : NSObject <INSendMessageIntentHandling>

@end

NS_ASSUME_NONNULL_END
159 changes: 159 additions & 0 deletions SiriIntents/IntentHandlers/SendMessage/SendMessageIntentHandler.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
//
// 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 "SendMessageIntentHandler.h"
#import "ContactResolver.h"
#import "MXKAccountManager.h"

@interface SendMessageIntentHandler ()

@property (nonatomic) id<ContactResolving> contactResolver;

/**
The room that is currently being used to send a message. This is to ensure a
strong ref is maintained on the `MXRoom` until sending has completed.
*/
@property (nonatomic) MXRoom *selectedRoom;

@end

@implementation SendMessageIntentHandler

- (instancetype)init {
wtimme marked this conversation as resolved.
Show resolved Hide resolved
if (self = [super init]) {
_contactResolver = [[ContactResolver alloc] init];
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think it makes sense to have multiple contact resolvers, or might it be an idea to create one in IntentHandler to pass into the initialisers?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the question! There are three options that I see:

  • Let every intent handler have their own instance of ContactResolver (as is the case in this branch right now)
  • Let the IntentHandler create one single instance and pass it to the intent handlers during initialization. The intent handlers keep a strong reference, so the IntentHandler does not need to manage the instance of ContactResolver
  • Add a singleton property (ContactResolver.shared) to ContactResolver

What is your preference? Is there a fourth option that is even better? Thanks in advance for your input!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would lean towards option 2 in this instance. If the resolver ever gained a cache or stored properties it would help keep the memory usage down in the extension (very early optimisation, I know 😅).

}

return self;
}

#pragma mark - INSendMessageIntentHandling

- (void)resolveRecipientsForSendMessage:(INSendMessageIntent *)intent completion:(void (^)(NSArray<INSendMessageRecipientResolutionResult *> * _Nonnull))completion
{
[self.contactResolver resolveContacts:intent.recipients withCompletion:completion];
}

- (void)resolveContentForSendMessage:(INSendMessageIntent *)intent withCompletion:(void (^)(INStringResolutionResult * _Nonnull))completion
{
NSString *message = intent.content;
if (message && ![message isEqualToString:@""])
completion([INStringResolutionResult successWithResolvedString:message]);
else
completion([INStringResolutionResult needsValue]);
}

- (void)confirmSendMessage:(INSendMessageIntent *)intent completion:(void (^)(INSendMessageIntentResponse * _Nonnull))completion
{
INSendMessageIntentResponse *response = nil;

MXKAccount *account = [MXKAccountManager sharedManager].activeAccounts.firstObject;
if (account)
{
NSUserActivity *userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendMessageIntent class])];
response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeReady userActivity:userActivity];
}
else
{
// User hasn't logged in
response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeFailureRequiringAppLaunch userActivity:nil];
}

completion(response);
}

- (void)handleSendMessage:(INSendMessageIntent *)intent completion:(void (^)(INSendMessageIntentResponse * _Nonnull))completion
{
void (^completeWithCode)(INSendMessageIntentResponseCode) = ^(INSendMessageIntentResponseCode code) {
NSUserActivity *userActivity = nil;
if (code == INSendMessageIntentResponseCodeSuccess)
userActivity = [[NSUserActivity alloc] initWithActivityType:NSStringFromClass([INSendMessageIntent class])];
INSendMessageIntentResponse *response = [[INSendMessageIntentResponse alloc] initWithCode:INSendMessageIntentResponseCodeSuccess
userActivity:userActivity];
completion(response);
};

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

BOOL isEncrypted = NO;
for (id<MXRoomSummaryProtocol> summary in summaries)
{
if ([summary.roomId isEqualToString:roomID])
{
isEncrypted = summary.isEncrypted;
break;
}
}

if (isEncrypted)
{
[MXFileStore setPreloadOptions:0];

MXSession *session = [[MXSession alloc] initWithMatrixRestClient:account.mxRestClient];
MXWeakify(session);
[session setStore:fileStore success:^{
MXStrongifyAndReturnIfNil(session);

self.selectedRoom = [MXRoom loadRoomFromStore:fileStore withRoomId:roomID matrixSession:session];

// Do not warn for unknown devices. We have cross-signing now
session.crypto.warnOnUnknowDevices = NO;

MXWeakify(self);
[self.selectedRoom sendTextMessage:intent.content
threadId:nil
success:^(NSString *eventId) {
completeWithCode(INSendMessageIntentResponseCodeSuccess);
MXStrongifyAndReturnIfNil(self);
self.selectedRoom = nil;
} failure:^(NSError *error) {
completeWithCode(INSendMessageIntentResponseCodeFailure);
MXStrongifyAndReturnIfNil(self);
self.selectedRoom = nil;
}];

} failure:^(NSError *error) {
completeWithCode(INSendMessageIntentResponseCodeFailure);
}];

return;
}

[account.mxRestClient sendTextMessageToRoom:roomID
threadId:nil
text:intent.content
success:^(NSString *eventId) {
completeWithCode(INSendMessageIntentResponseCodeSuccess);
}
failure:^(NSError *error) {
completeWithCode(INSendMessageIntentResponseCodeFailure);
}];

}];
}
else
{
completeWithCode(INSendMessageIntentResponseCodeFailure);
}
}

@end