From e33b334307e4086041f3b736eb89071b7dcd54d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Sa=C3=BAl=20Ibarra=20Corretg=C3=A9?= Date: Tue, 11 Jun 2019 15:38:33 +0200 Subject: [PATCH] rn: add SDK API to set user information At the moment it includes: - display name - email - avatar URL This information is used *only* if no token was specified. --- .../java/org/jitsi/meet/MainActivity.java | 1 + .../meet/sdk/JitsiMeetConferenceOptions.java | 20 ++++ .../org/jitsi/meet/sdk/JitsiMeetUserInfo.java | 107 ++++++++++++++++++ ios/sdk/sdk.xcodeproj/project.pbxproj | 10 ++ ios/sdk/src/JitsiMeetConferenceOptions.h | 10 ++ ios/sdk/src/JitsiMeetConferenceOptions.m | 9 ++ ios/sdk/src/JitsiMeetUserInfo+Private.h | 23 ++++ ios/sdk/src/JitsiMeetUserInfo.h | 38 +++++++ ios/sdk/src/JitsiMeetUserInfo.m | 55 +++++++++ react/features/app/components/App.native.js | 18 ++- 10 files changed, 285 insertions(+), 6 deletions(-) create mode 100644 android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetUserInfo.java create mode 100644 ios/sdk/src/JitsiMeetUserInfo+Private.h create mode 100644 ios/sdk/src/JitsiMeetUserInfo.h create mode 100644 ios/sdk/src/JitsiMeetUserInfo.m diff --git a/android/app/src/main/java/org/jitsi/meet/MainActivity.java b/android/app/src/main/java/org/jitsi/meet/MainActivity.java index db22d9963..0c2b3369f 100644 --- a/android/app/src/main/java/org/jitsi/meet/MainActivity.java +++ b/android/app/src/main/java/org/jitsi/meet/MainActivity.java @@ -28,6 +28,7 @@ import android.view.KeyEvent; import org.jitsi.meet.sdk.JitsiMeet; import org.jitsi.meet.sdk.JitsiMeetActivity; import org.jitsi.meet.sdk.JitsiMeetConferenceOptions; +import org.jitsi.meet.sdk.JitsiMeetUserInfo; import java.lang.reflect.Method; import java.net.MalformedURLException; diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetConferenceOptions.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetConferenceOptions.java index db1fdee21..7da78e9dc 100644 --- a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetConferenceOptions.java +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetConferenceOptions.java @@ -67,6 +67,11 @@ public class JitsiMeetConferenceOptions implements Parcelable { private Boolean audioOnly; private Boolean videoMuted; + /** + * USer information, to be used when no token is specified. + */ + private JitsiMeetUserInfo userInfo; + /** * Class used to build the immutable {@link JitsiMeetConferenceOptions} object. */ @@ -83,6 +88,8 @@ public class JitsiMeetConferenceOptions implements Parcelable { private Boolean audioOnly; private Boolean videoMuted; + private JitsiMeetUserInfo userInfo; + public Builder() { featureFlags = new Bundle(); } @@ -208,6 +215,12 @@ public class JitsiMeetConferenceOptions implements Parcelable { return this; } + public Builder setUserInfo(JitsiMeetUserInfo userInfo) { + this.userInfo = userInfo; + + return this; + } + /** * Builds the immutable {@link JitsiMeetConferenceOptions} object with the configuration * that this {@link Builder} instance specified. @@ -225,6 +238,7 @@ public class JitsiMeetConferenceOptions implements Parcelable { options.audioMuted = this.audioMuted; options.audioOnly = this.audioOnly; options.videoMuted = this.videoMuted; + options.userInfo = this.userInfo; return options; } @@ -239,6 +253,7 @@ public class JitsiMeetConferenceOptions implements Parcelable { token = in.readString(); colorScheme = in.readBundle(); featureFlags = in.readBundle(); + userInfo = new JitsiMeetUserInfo(in.readBundle()); byte tmpAudioMuted = in.readByte(); audioMuted = tmpAudioMuted == 0 ? null : tmpAudioMuted == 1; byte tmpAudioOnly = in.readByte(); @@ -294,6 +309,10 @@ public class JitsiMeetConferenceOptions implements Parcelable { urlProps.putString("jwt", token); } + if (token == null && userInfo != null) { + props.putBundle("userInfo", userInfo.asBundle()); + } + urlProps.putBundle("config", config); props.putBundle("url", urlProps); @@ -322,6 +341,7 @@ public class JitsiMeetConferenceOptions implements Parcelable { dest.writeString(token); dest.writeBundle(colorScheme); dest.writeBundle(featureFlags); + dest.writeBundle(userInfo.asBundle()); dest.writeByte((byte) (audioMuted == null ? 0 : audioMuted ? 1 : 2)); dest.writeByte((byte) (audioOnly == null ? 0 : audioOnly ? 1 : 2)); dest.writeByte((byte) (videoMuted == null ? 0 : videoMuted ? 1 : 2)); diff --git a/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetUserInfo.java b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetUserInfo.java new file mode 100644 index 000000000..7d59909be --- /dev/null +++ b/android/sdk/src/main/java/org/jitsi/meet/sdk/JitsiMeetUserInfo.java @@ -0,0 +1,107 @@ +/* + * Copyright @ 2019-present 8x8, Inc. + * + * 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. + */ + +package org.jitsi.meet.sdk; + +import android.os.Bundle; + +import java.net.MalformedURLException; +import java.net.URL; + +/** + * This class represents user information to be passed to {@link JitsiMeetConferenceOptions} for + * identifying a user. + */ +public class JitsiMeetUserInfo { + /** + * User's display name. + */ + private String displayName; + + /** + * User's email address. + */ + private String email; + + /** + * User's avatar URL. + */ + private URL avatar; + + public JitsiMeetUserInfo() {} + + public JitsiMeetUserInfo(Bundle b) { + super(); + + if (b.containsKey("displayName")) { + displayName = b.getString("displayName"); + } + + if (b.containsKey("email")) { + email = b.getString("email"); + } + + if (b.containsKey("avatarURL")) { + String avatarURL = b.getString("avatarURL"); + try { + avatar = new URL(avatarURL); + } catch (MalformedURLException e) { + } + } + } + + public String getDisplayName() { + return displayName; + } + + public void setDisplayName(String displayName) { + this.displayName = displayName; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public URL getAvatar() { + return avatar; + } + + public void setAvatar(URL avatar) { + this.avatar = avatar; + } + + Bundle asBundle() { + Bundle b = new Bundle(); + + if (displayName != null) { + b.putString("displayName", displayName); + } + + if (email != null) { + b.putString("email", email); + } + + if (avatar != null) { + b.putString("avatarURL", avatar.toString()); + } + + return b; + } +} diff --git a/ios/sdk/sdk.xcodeproj/project.pbxproj b/ios/sdk/sdk.xcodeproj/project.pbxproj index a34dcf3ce..a24b4ab6b 100644 --- a/ios/sdk/sdk.xcodeproj/project.pbxproj +++ b/ios/sdk/sdk.xcodeproj/project.pbxproj @@ -42,6 +42,8 @@ C69EFA0E209A0F660027712B /* JMCallKitListener.swift in Sources */ = {isa = PBXBuildFile; fileRef = C69EFA0B209A0F660027712B /* JMCallKitListener.swift */; }; C6A34261204EF76800E062DD /* DragGestureController.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6A3425E204EF76800E062DD /* DragGestureController.swift */; }; C6CC49AF207412CF000DFA42 /* PiPViewCoordinator.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */; }; + DE762DB422AFDE76000DEBD6 /* JitsiMeetUserInfo.h in Headers */ = {isa = PBXBuildFile; fileRef = DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */; settings = {ATTRIBUTES = (Public, ); }; }; + DE762DB622AFDE8D000DEBD6 /* JitsiMeetUserInfo.m in Sources */ = {isa = PBXBuildFile; fileRef = DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */; }; DEAD3226220C497000E93636 /* JitsiMeetConferenceOptions.h in Headers */ = {isa = PBXBuildFile; fileRef = DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */; settings = {ATTRIBUTES = (Public, ); }; }; DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */; }; DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */ = {isa = PBXBuildFile; fileRef = DEAFA778229EAD520033A7FA /* RNRootView.m */; }; @@ -94,6 +96,9 @@ C6A3425E204EF76800E062DD /* DragGestureController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DragGestureController.swift; sourceTree = ""; }; C6CC49AE207412CF000DFA42 /* PiPViewCoordinator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PiPViewCoordinator.swift; sourceTree = ""; }; C6F99C13204DB63D0001F710 /* JitsiMeetView+Private.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetView+Private.h"; sourceTree = ""; }; + DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetUserInfo.h; sourceTree = ""; }; + DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetUserInfo.m; sourceTree = ""; }; + DE762DB722AFE166000DEBD6 /* JitsiMeetUserInfo+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetUserInfo+Private.h"; sourceTree = ""; }; DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = JitsiMeetConferenceOptions.h; sourceTree = ""; }; DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = JitsiMeetConferenceOptions.m; sourceTree = ""; }; DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "JitsiMeetConferenceOptions+Private.h"; sourceTree = ""; }; @@ -174,6 +179,9 @@ DEAD3224220C497000E93636 /* JitsiMeetConferenceOptions.h */, DEAD3228220C734300E93636 /* JitsiMeetConferenceOptions+Private.h */, DEAD3225220C497000E93636 /* JitsiMeetConferenceOptions.m */, + DE762DB322AFDE76000DEBD6 /* JitsiMeetUserInfo.h */, + DE762DB722AFE166000DEBD6 /* JitsiMeetUserInfo+Private.h */, + DE762DB522AFDE8D000DEBD6 /* JitsiMeetUserInfo.m */, 0B412F161EDEC65D00B1A0A6 /* JitsiMeetView.h */, 0B412F171EDEC65D00B1A0A6 /* JitsiMeetView.m */, DEAFA777229EAD3B0033A7FA /* RNRootView.h */, @@ -259,6 +267,7 @@ isa = PBXHeadersBuildPhase; buildActionMask = 2147483647; files = ( + DE762DB422AFDE76000DEBD6 /* JitsiMeetUserInfo.h in Headers */, 0B412F181EDEC65D00B1A0A6 /* JitsiMeetView.h in Headers */, 0B93EF7E1EC9DDCD0030D24D /* RCTBridgeWrapper.h in Headers */, 0B412F221EDEF6EA00B1A0A6 /* JitsiMeetViewDelegate.h in Headers */, @@ -484,6 +493,7 @@ 0BB9AD7B1F5EC8F4001C08DB /* CallKit.m in Sources */, 0BB9AD7D1F60356D001C08DB /* AppInfo.m in Sources */, DEAFA779229EAD520033A7FA /* RNRootView.m in Sources */, + DE762DB622AFDE8D000DEBD6 /* JitsiMeetUserInfo.m in Sources */, DEAD3227220C497000E93636 /* JitsiMeetConferenceOptions.m in Sources */, 0B93EF7F1EC9DDCD0030D24D /* RCTBridgeWrapper.m in Sources */, 0BA13D311EE83FF8007BEF7F /* ExternalAPI.m in Sources */, diff --git a/ios/sdk/src/JitsiMeetConferenceOptions.h b/ios/sdk/src/JitsiMeetConferenceOptions.h index d122af15c..bea40e99d 100644 --- a/ios/sdk/src/JitsiMeetConferenceOptions.h +++ b/ios/sdk/src/JitsiMeetConferenceOptions.h @@ -16,6 +16,9 @@ #import +#import "JitsiMeetUserInfo.h" + + @interface JitsiMeetConferenceOptionsBuilder : NSObject /** @@ -60,6 +63,11 @@ */ @property (nonatomic) BOOL welcomePageEnabled; +/** + * Information about the local user. It will be used in absence of a token. + */ +@property (nonatomic, nullable) JitsiMeetUserInfo *userInfo; + - (void)setFeatureFlag:(NSString *_Nonnull)flag withBoolean:(BOOL)value; - (void)setFeatureFlag:(NSString *_Nonnull)flag withValue:(id _Nonnull)value; @@ -82,6 +90,8 @@ @property (nonatomic, readonly) BOOL welcomePageEnabled; +@property (nonatomic, nullable) JitsiMeetUserInfo *userInfo; + + (instancetype _Nonnull)fromBuilder:(void (^_Nonnull)(JitsiMeetConferenceOptionsBuilder *_Nonnull))initBlock; - (instancetype _Nonnull)init NS_UNAVAILABLE; diff --git a/ios/sdk/src/JitsiMeetConferenceOptions.m b/ios/sdk/src/JitsiMeetConferenceOptions.m index be2a90559..4f6d868b6 100644 --- a/ios/sdk/src/JitsiMeetConferenceOptions.m +++ b/ios/sdk/src/JitsiMeetConferenceOptions.m @@ -17,6 +17,7 @@ #import #import "JitsiMeetConferenceOptions+Private.h" +#import "JitsiMeetUserInfo+Private.h" /** * Backwards compatibility: turn the boolean property into a feature flag. @@ -49,6 +50,8 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled"; _audioOnly = nil; _audioMuted = nil; _videoMuted = nil; + + _userInfo = nil; } return self; @@ -163,6 +166,8 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled"; _videoMuted = [builder getVideoMuted]; _featureFlags = [NSDictionary dictionaryWithDictionary:builder.featureFlags]; + + _userInfo = builder.userInfo; } return self; @@ -220,6 +225,10 @@ static NSString *const WelcomePageEnabledFeatureFlag = @"welcomepage.enabled"; urlProps[@"jwt"] = _token; } + if (_token == nil && _userInfo != nil) { + props[@"userInfo"] = [self.userInfo asDict]; + } + urlProps[@"config"] = config; props[@"url"] = urlProps; diff --git a/ios/sdk/src/JitsiMeetUserInfo+Private.h b/ios/sdk/src/JitsiMeetUserInfo+Private.h new file mode 100644 index 000000000..9a46f73c8 --- /dev/null +++ b/ios/sdk/src/JitsiMeetUserInfo+Private.h @@ -0,0 +1,23 @@ +/* + * Copyright @ 2019-present 8x8, Inc. + * + * 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 "JitsiMeetUserInfo.h" + +@interface JitsiMeetUserInfo () + +- (NSMutableDictionary *)asDict; + +@end diff --git a/ios/sdk/src/JitsiMeetUserInfo.h b/ios/sdk/src/JitsiMeetUserInfo.h new file mode 100644 index 000000000..682e5c165 --- /dev/null +++ b/ios/sdk/src/JitsiMeetUserInfo.h @@ -0,0 +1,38 @@ +/* + * Copyright @ 2019-present 8x8, Inc. + * + * 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 JitsiMeetUserInfo : NSObject + +/** + * User display name. + */ +@property (nonatomic, copy, nullable) NSString *displayName; +/** + * User e-mail. + */ +@property (nonatomic, copy, nullable) NSString *email; +/** + * URL for the user avatar. + */ +@property (nonatomic, copy, nullable) NSURL *avatar; + +- (instancetype _Nullable)initWithDisplayName:(NSString *_Nullable)displayName + andEmail:(NSString *_Nullable)email + andAvatar:(NSURL *_Nullable) avatar; + +@end diff --git a/ios/sdk/src/JitsiMeetUserInfo.m b/ios/sdk/src/JitsiMeetUserInfo.m new file mode 100644 index 000000000..3811890cf --- /dev/null +++ b/ios/sdk/src/JitsiMeetUserInfo.m @@ -0,0 +1,55 @@ +/* + * Copyright @ 2019-present 8x8, Inc. + * + * 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 "JitsiMeetUserInfo+Private.h" + +@implementation JitsiMeetUserInfo + +- (instancetype)initWithDisplayName:(NSString *)displayName + andEmail:(NSString *)email + andAvatar:(NSURL *_Nullable) avatar { + self = [super init]; + if (self) { + self.displayName = displayName; + self.email = email; + self.avatar = avatar; + } + + return self; +} + +- (NSDictionary *)asDict { + NSMutableDictionary *dict = [[NSMutableDictionary alloc] init]; + + if (self.displayName != nil) { + dict[@"displayName"] = self.displayName; + } + + if (self.email != nil) { + dict[@"email"] = self.email; + } + + if (self.avatar != nil) { + NSString *avatarURL = [self.avatar absoluteString]; + if (avatarURL != nil) { + dict[@"avatarURL"] = avatarURL; + } + } + + return dict; +} + +@end diff --git a/react/features/app/components/App.native.js b/react/features/app/components/App.native.js index cf8ffc87e..97d92d818 100644 --- a/react/features/app/components/App.native.js +++ b/react/features/app/components/App.native.js @@ -13,6 +13,7 @@ import { AspectRatioDetector, ReducedUIDetector } from '../../base/responsive-ui'; +import { updateSettings } from '../../base/settings'; import '../../google-api'; import '../../mobile/audio-mode'; import '../../mobile/background'; @@ -50,7 +51,12 @@ type Props = AbstractAppProps & { /** * An object with the feature flags. */ - flags: Object + flags: Object, + + /** + * An object with user information (display name, email, avatar URL). + */ + userInfo: ?Object }; /** @@ -88,12 +94,12 @@ export class App extends AbstractApp { super.componentDidMount(); this._init.then(() => { - // We set the color scheme early enough so then we avoid any - // unnecessary re-renders. - this.state.store.dispatch(setColorScheme(this.props.colorScheme)); + // We set these early enough so then we avoid any unnecessary re-renders. + const { dispatch } = this.state.store; - // Ditto for feature flags. - this.state.store.dispatch(updateFlags(this.props.flags)); + dispatch(setColorScheme(this.props.colorScheme)); + dispatch(updateFlags(this.props.flags)); + dispatch(updateSettings(this.props.userInfo || {})); }); }