[iOS] Add support for Siri shortcuts

This is mostly implemented in the app, with the needed support in the SDK. Since
the app needs to donate intents and deal with creating NSUserActivity objects it
doesn't feel right to do this in a library. Instead, we donate the intents from
the app, but the SDK is ready to extract conference URLs from any intent which
was registered as a conference activity.

This also opens the door for eventually adding Handoff support.
pull/3766/head jitsi-meet_3467
Saúl Ibarra Corretgé 6 years ago committed by Zoltan Bettenbuk
parent 4898f81596
commit 889644f7bd
  1. 2
      ios/app/app.entitlements
  2. 5
      ios/app/app.xcodeproj/project.pbxproj
  3. 5
      ios/app/src/AppDelegate.m
  4. 4
      ios/app/src/Info.plist
  5. 7
      ios/app/src/Types.h
  6. 47
      ios/app/src/ViewController.m
  7. 1
      ios/sdk/src/JitsiMeetView+Private.h
  8. 2
      ios/sdk/src/JitsiMeetView.h
  9. 19
      ios/sdk/src/JitsiMeetView.m

@ -7,5 +7,7 @@
<string>applinks:beta.meet.jit.si</string>
<string>applinks:meet.jit.si</string>
</array>
<key>com.apple.developer.siri</key>
<true/>
</dict>
</plist>

@ -43,6 +43,7 @@
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ViewController.h; sourceTree = "<group>"; };
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = ViewController.m; sourceTree = "<group>"; };
0B412F201EDEE95300B1A0A6 /* Main.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; name = Main.storyboard; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
0BBD021F212EB69D00CCB19F /* Types.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Types.h; sourceTree = "<group>"; };
0BD6B4361EF82A6B00D1F4CD /* WebRTC.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebRTC.framework; path = "../../node_modules/react-native-webrtc/ios/WebRTC.framework"; sourceTree = "<group>"; };
13B07F961A680F5B00A75B9A /* jitsi-meet.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "jitsi-meet.app"; sourceTree = BUILT_PRODUCTS_DIR; };
13B07FAF1A68108700A75B9A /* AppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppDelegate.h; sourceTree = "<group>"; };
@ -96,6 +97,7 @@
13B07FB11A68108700A75B9A /* LaunchScreen.xib */,
13B07FB71A68108700A75B9A /* main.m */,
0B412F201EDEE95300B1A0A6 /* Main.storyboard */,
0BBD021F212EB69D00CCB19F /* Types.h */,
0B412F1D1EDEE6E800B1A0A6 /* ViewController.h */,
0B412F1E1EDEE6E800B1A0A6 /* ViewController.m */,
);
@ -173,6 +175,9 @@
com.apple.SafariKeychain = {
enabled = 1;
};
com.apple.Siri = {
enabled = 1;
};
};
};
};

@ -17,6 +17,7 @@
#import "AppDelegate.h"
#import "FIRUtilities.h"
#import "Types.h"
#import <JitsiMeet/JitsiMeet.h>
@ -37,6 +38,10 @@
[Fabric with:@[[Crashlytics class]]];
}
// Set the conference activity type defined in this application.
// This cannot be defined by the SDK.
JitsiMeetView.conferenceActivityType = JitsiMeetConferenceActivityType;
return [JitsiMeetView application:application
didFinishLaunchingWithOptions:launchOptions];
}

@ -97,5 +97,9 @@
<false/>
<key>firebase_crashlytics_collection_enabled</key>
<string>false</string>
<key>NSUserActivityTypes</key>
<array>
<string>org.jitsi.JitsiMeet.ios.conference</string>
</array>
</dict>
</plist>

@ -0,0 +1,7 @@
#import <Foundation/Foundation.h>
// This must match what's defined in the NSUserActivityTypes array in the
// Info.plist file.
static NSString *const JitsiMeetConferenceActivityType
= @"org.jitsi.JitsiMeet.ios.conference";

@ -14,8 +14,16 @@
* limitations under the License.
*/
#import <Availability.h>
#import <CoreSpotlight/CoreSpotlight.h>
#import <MobileCoreServices/MobileCoreServices.h>
#import "Types.h"
#import "ViewController.h"
// Needed for NSUserActivity suggestedInvocationPhrase
@import Intents;
/**
* The query to perform through JMAddPeopleController when the InviteButton is
* tapped in order to exercise the public API of the feature invite. If nil, the
@ -33,11 +41,10 @@ static NSString * const ADD_PEOPLE_CONTROLLER_QUERY = nil;
[super viewDidLoad];
JitsiMeetView *view = (JitsiMeetView *) self.view;
view.delegate = self;
#ifdef DEBUG
view.delegate = self;
// inviteController
JMInviteController *inviteController = view.inviteController;
inviteController.delegate = self;
@ -56,12 +63,13 @@ static NSString * const ADD_PEOPLE_CONTROLLER_QUERY = nil;
[view loadURL:nil];
}
#if DEBUG
// JitsiMeetViewDelegate
- (void)_onJitsiMeetViewDelegateEvent:(NSString *)name
withData:(NSDictionary *)data {
#if DEBUG
NSLog(
@"[%s:%d] JitsiMeetViewDelegate %@ %@",
__FILE__, __LINE__, name, data);
@ -70,6 +78,7 @@ static NSString * const ADD_PEOPLE_CONTROLLER_QUERY = nil;
[NSThread isMainThread],
@"JitsiMeetViewDelegate %@ method invoked on a non-main thread",
name);
#endif
}
- (void)conferenceFailed:(NSDictionary *)data {
@ -78,6 +87,36 @@ static NSString * const ADD_PEOPLE_CONTROLLER_QUERY = nil;
- (void)conferenceJoined:(NSDictionary *)data {
[self _onJitsiMeetViewDelegateEvent:@"CONFERENCE_JOINED" withData:data];
// Register a NSUserActivity for this conference so it can be invoked as a
// Siri shortcut. This is only supported in iOS >= 12.
#ifdef __IPHONE_12_0
if (@available(iOS 12.0, *)) {
NSUserActivity *userActivity
= [[NSUserActivity alloc] initWithActivityType:JitsiMeetConferenceActivityType];
NSString *urlStr = data[@"url"];
NSURL *url = [NSURL URLWithString:urlStr];
NSString *conference = [url.pathComponents lastObject];
userActivity.title = [NSString stringWithFormat:@"Join %@", conference];
userActivity.suggestedInvocationPhrase = @"Join my Jitsi meeting";
userActivity.userInfo = @{@"url": urlStr};
[userActivity setEligibleForSearch:YES];
[userActivity setEligibleForPrediction:YES];
[userActivity setPersistentIdentifier:urlStr];
// Subtitle
CSSearchableItemAttributeSet *attributes
= [[CSSearchableItemAttributeSet alloc] initWithItemContentType:(NSString *)kUTTypeItem];
attributes.contentDescription = urlStr;
userActivity.contentAttributeSet = attributes;
self.userActivity = userActivity;
[userActivity becomeCurrent];
}
#endif
}
- (void)conferenceLeft:(NSDictionary *)data {
@ -96,6 +135,8 @@ static NSString * const ADD_PEOPLE_CONTROLLER_QUERY = nil;
[self _onJitsiMeetViewDelegateEvent:@"LOAD_CONFIG_ERROR" withData:data];
}
#if DEBUG
// JMInviteControllerDelegate
- (void)beginAddPeople:(JMAddPeopleController *)addPeopleController {

@ -18,6 +18,7 @@
@interface JitsiMeetView ()
+ (NSDictionary *)conferenceURLFromUserActivity:(NSUserActivity *)userActivity;
+ (instancetype)viewForExternalAPIScope:(NSString *)externalAPIScope;
@end

@ -22,6 +22,8 @@
@interface JitsiMeetView : UIView
@property (class, copy, nonatomic, nullable) NSString *conferenceActivityType;
@property (copy, nonatomic, nullable) NSURL *defaultURL;
@property (nonatomic, nullable, weak) id<JitsiMeetViewDelegate> delegate;

@ -88,6 +88,8 @@ void registerFatalErrorHandler() {
@dynamic pictureInPictureEnabled;
static NSString *_conferenceActivityType;
static RCTBridgeWrapper *bridgeWrapper;
/**
@ -277,6 +279,16 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
[self loadURLObject:urlString ? @{ @"url": urlString } : nil];
}
#pragma conferenceActivityType getter / setter
+ (NSString *)conferenceActivityType {
return _conferenceActivityType;
}
+ (void) setConferenceActivityType:(NSString *)conferenceActivityType {
_conferenceActivityType = conferenceActivityType;
}
#pragma pictureInPictureEnabled getter / setter
- (void) setPictureInPictureEnabled:(BOOL)pictureInPictureEnabled {
@ -358,6 +370,13 @@ static NSMapTable<NSString *, JitsiMeetView *> *views;
@"url": url
};
}
} else if (_conferenceActivityType && [activityType isEqualToString:_conferenceActivityType]) {
// App was started by continuing a registered NSUserActivity (SiriKit, Handoff, ...)
NSString *url;
if ((url = userActivity.userInfo[@"url"])) {
return @{ @"url" : url };
}
}
return nil;

Loading…
Cancel
Save