Skip to content

Commit eecdda0

Browse files
authored
[Calling][Release] Update API for 1.7.0 (#37)
1 parent a946cc8 commit eecdda0

File tree

13 files changed

+7123
-39
lines changed

13 files changed

+7123
-39
lines changed

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -32,23 +32,23 @@ Link to Authentication Endpoint Sample: [link](https://docs.microsoft.com/en-us/
3232

3333
## Run Sample App
3434

35-
Navigate to `AzureCommunicationUIDemoApp/`:
35+
Navigate to `demo/`:
3636

3737
1. Run `yarn install`
3838

3939
Install iOS app dependencies:
40-
1. In Terminal, navigate to `AzureCommunicationUIDemoApp/ios/`:
40+
1. In Terminal, navigate to `demo/ios/`:
4141
2. Run `pod install --repo-update`
4242

4343
Build android app dependencies:
44-
1. In Terminal, navigate to `AzureCommunicationUIDemoApp/android/`:
44+
1. In Terminal, navigate to `demo/android/`:
4545
2. Run `./gradlew build`
4646

47-
Navigate back to `AzureCommunicationUIDemoApp/`
47+
Navigate back to `demo/`
4848
1. Run `yarn react-native start`
49-
2. Open another Terminal, navigate to `AzureCommunicationUIDemoApp/` folder, and run `yarn react-native run-ios` or `yarn react-native run-android`
49+
2. Open another Terminal, navigate to `demo/` folder, and run `yarn react-native run-ios` or `yarn react-native run-android`
5050

51-
Alternatively, you can also run the iOS app by launching Xcode from the `.xcworkspace` file, and run the app with scheme `AzureCommunicationUIDemoApp` on your simulator or iOS device.
51+
Alternatively, you can also run the iOS app by launching Xcode from the `.xcworkspace` file, and run the app with scheme `demo` on your simulator or iOS device.
5252

5353
To run Android app, you can also launch Android Studio and run on Android emulator or Android device after syncing up gradle. There are two ways to sync gradle either with a command in the android folder`./gradlew build` or via android studio.
5454

@@ -60,7 +60,7 @@ To integrate the native UI Library with React Native in this sample, a few key s
6060
After installing the package and dependencies with CocoaPods from the step above, modify the Podfile in the `/ios` filder as such:
6161
```ruby
6262
platform :ios, '14.0'
63-
target 'AzureCommunicationUIDemoApp' do
63+
target 'demo' do
6464
use_frameworks!
6565
pod 'AzureCommunicationUICalling', '1.2.0'
6666
...
@@ -162,6 +162,6 @@ Depending on what type of Call/Meeting you would like to setup, use the appropri
162162
## React native - native app bridging
163163
In order to support the communication between React Native and native Azure Communication UI library, bridging is needed for both iOS and Android. Please refer to the following bridging file guide for iOS and Android.
164164

165-
[iOS bridging file guide](AzureCommunicationUIDemoApp/ios/README.md)
165+
[iOS bridging file guide](demo/ios/README.md)
166166

167-
[Android bridging file guide](AzureCommunicationUIDemoApp/android/README.md)
167+
[Android bridging file guide](demo/android/README.md)

demo/App.tsx

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,15 @@ const App = () => {
3838
const [subtitle, onChangeSubtitle] = useState('');
3939
const [meetingInput, onChangeMeetingInput] = useState('');
4040
const [isRightToLeft, onChangeIsRightToLeft] = useState(false);
41+
const [disableLeaveCallConfirmation, onChangeDisableLeaveCallConfirmation] = useState(false);
4142
const [localAvatar, onLocalAvatarSet] = useState('');
4243
const [remoteAvatar, onRemoteAvatarSet] = useState('');
4344
const [modalVisible, setModalVisible] = useState(false);
4445
const [selectedLanguage, setSelectedLanguage] = useState('en');
4546
const [isGroupCall, setIsGroupCall] = useState(true);
4647
const [localesArray, setLocalesArray] = useState([]);
4748
const toggleIsRightToLeftSwitch = () => onChangeIsRightToLeft(!isRightToLeft);
49+
const disableLeaveCallConfirmationSwitch = () => onChangeDisableLeaveCallConfirmation(!disableLeaveCallConfirmation);
4850

4951
React.useLayoutEffect(() => {
5052
navigation.setOptions({
@@ -112,14 +114,14 @@ const App = () => {
112114
const remoteAvatarImageResource = resolveAvatarSource(remoteAvatar);
113115
await RNAzureCommunicationUICalling.startCallComposite(
114116
// local options
115-
{"displayName": displayName, "title": title, "subtitle": subtitle},
117+
{"displayName": displayName, "title": title, "subtitle": subtitle, "disableLeaveCallConfirmation": disableLeaveCallConfirmation},
116118
localAvatarImageResource,
117119
// remote options
118120
{"token": tokenInput, "meeting": meetingInput},
119121
remoteAvatarImageResource,
120122
// localization options
121-
{"locale": selectedLanguage, "layout": isRightToLeft}
122-
// {"setupOrientation": "PORTRAIT", "callOrientation": "PORTRAIT"}
123+
{"locale": selectedLanguage, "layout": isRightToLeft},
124+
{"setupOrientation": "PORTRAIT", "callOrientation": "PORTRAIT"}
123125
);
124126
} catch (e) {
125127
Alert.alert('Error', e.message, [{ text: 'Dismiss' }]);
@@ -298,6 +300,14 @@ const App = () => {
298300
<Text style={styles.settingsHeaderText}>Remote Participant View Data</Text>
299301
<AvatarsView setAvatar={remoteAvatar} onAvatarSet={setRemoteAvatar} />
300302
</View>
303+
304+
<View style={styles.settingsSwitchToggleContainer}>
305+
<Text>Disable leave call confirmation</Text>
306+
<Switch
307+
onValueChange={disableLeaveCallConfirmationSwitch.bind(this)}
308+
value={isRightToLeft}
309+
/>
310+
</View>
301311
</View>
302312
</ScrollView>
303313
</Modal>

demo/android/README.md

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
# Android Bridging
2+
Create a new Java file `RNAzureCommunicationUICalling` in the `AzureCommunicationUIDemoApp` folder. This file will contain all of the methods that will call the native Android UI Library.
3+
See [RNAzureCommunicationUICalling.java](app/src/main/java/com/azurecommunicationuidemoapp/RNAzureCommunicationUICalling.java)
4+
5+
6+
To allow the React Native javascript end to access a funciton, wrap it with `@ReactMethod`. This will allow the javascript end to call the function which will be used to launch the native UI Library.
7+
8+
Here we create a method `startCallComposite` and launch the calling compsite within it. We then wrap the method with `@ReactMethod` to allow the JavaScript end to be able to call this function which will then launch our calling composite.
9+
10+
```java
11+
@ReactMethod
12+
public void startCallComposite(String displayName, String tokenInput, String meetingInput, ReadableMap localAvatarImageResource, String selectedLanguage, boolean isRightToLeft, ReadableMap remoteAvatarImageResource, Promise promise) {
13+
14+
CallComposite callComposite = new CallCompositeBuilder().build();
15+
16+
...
17+
18+
//lauching native calling composite
19+
callComposite.launch(context, remoteOptions);
20+
}
21+
```
22+
23+
For the the full `startCallComposite` function, see [RNAzureCommunicationUICalling.java](https://github.com/Azure-Samples/communication-services-ui-library-react-native/blob/main/AzureCommunicationUIDemoApp/android/app/src/main/java/com/azurecommunicationuidemoapp/RNAzureCommunicationUICalling.java#L61)
24+
25+
26+
After creating `RNAzureCommunicationUICalling`, create another Java file called `RNAzureCommunicationUICallingPackage` in the `AzureCommunicationUIDemoApp` folder.
27+
See [RNAzureCommunicationUICallingPackage.java](app/src/main/java/com/azurecommunicationuidemoapp/RNAzureCommunicationUICallingPackage.java)
28+
29+
`RNAzureCommunicationUICallingPackage` implements `ReactPackage` which provides us with an interface to register our `RNAzureCommunicationUICalling` module in React Native. This will allow us to reference the module from the javascript end.
30+
31+
```java
32+
public class RNAzureCommunicationUICallingPackage implements ReactPackage {
33+
34+
@Override
35+
public List<ViewManager> createViewManagers(ReactApplicationContext reactContext) {
36+
return Collections.emptyList();
37+
}
38+
39+
@Override
40+
public List<NativeModule> createNativeModules(
41+
ReactApplicationContext reactContext) {
42+
List<NativeModule> modules = new ArrayList<>();
43+
44+
modules.add(new RNAzureCommunicationUICalling(reactContext));
45+
return modules;
46+
}
47+
}
48+
```
49+
50+
# Running the Code
51+
You can either run your app on an Android emulator with opening the project in Android Studio or run the app through CLI.
52+
53+
54+
55+
# Environment Variables
56+
The React Native tools require some environment variables to be set up in order to build apps with native code.
57+
58+
### Mac
59+
60+
Add the following lines to your $HOME/.bash_profile or $HOME/.bashrc (if you are using zsh then ~/.zprofile or ~/.zshrc) config file:
61+
- export ANDROID_SDK_ROOT=$HOME/Library/Android/sdk
62+
- export PATH=$PATH:$ANDROID_SDK_ROOT/emulator
63+
- export PATH=$PATH:$ANDROID_SDK_ROOT/platform-tools
64+
65+
### Windows
66+
- Open the Windows Control Panel.
67+
- Click on User Accounts, then click User Accounts again
68+
- Click on Change my environment variables
69+
70+
Click on New... to create a new ANDROID_HOME user variable that points to the path to your Android SDK.
71+
72+
#### Example
73+
```groovy
74+
%LOCALAPPDATA%\Android\Sdk
75+
76+
```
77+
78+
Click on New... to create a new JAVE_HOME user variable that points to the path to your JDK:
79+
80+
#### Example
81+
```groovy
82+
org.gradle.java.home=C:\\Program Files\\OpenJDK\\openjdk-11.0.16_8
83+
84+
```
85+
86+
87+
88+
## Android Studio
89+
You can build and run your app on an Android emulator by creating a new Android Virtual Device (AVD) > Click Run.
90+
91+
For more information: [Run Your App](https://developer.android.com/training/basics/firstapp/running-app)
92+
93+
Tap Start Experience.
94+
95+
Accept audio permissions and select device, mic, and video settings.
96+
97+
Tap Start Call.
98+
Alternatively, you could also run the React Native application through CLI. First, you need to start Metro, the JavaScript bundler that ships with React Native.
99+
100+
To start the Metro Bundler, go to the root folder of the React Native project (root folder) and run the following:
101+
102+
```ruby
103+
npx react-native start
104+
```
105+
106+
Start your Android Virtual Device (AVD) or connect your own. Check if the device is running:
107+
```ruby
108+
adb devices
109+
```
110+
111+
Let Metro Bundler run in its own terminal. Open a new terminal in the same directory and run the following:
112+
```ruby
113+
npx react-native run-android
114+
```
115+
116+
117+
# UI Library functionality
118+
119+
120+
## Launching Composite
121+
The React native library supports all the same features as the native [UI composite](https://github.com/Azure/communication-ui-library-android). Call `startCallComposite` on the `RNAzureCommunicationUICalling` module from your React Native Javascript code, wrapping with `try-catch` statement to handle any errors.
122+
123+
```cs
124+
try {
125+
await RNAzureCommunicationUICalling.startCallComposite(
126+
// local options
127+
{"displayName": displayName, "title": title, "subtitle": subtitle},
128+
localAvatarImageResource,
129+
// remote options
130+
{"token": tokenInput, "meeting": meetingInput},
131+
remoteAvatarImageResource,
132+
// localization options
133+
{"locale": selectedLanguage, "layout": isRightToLeft}
134+
);
135+
} catch (e) {
136+
console.log(`startCallComposite error: ${e.message}`)
137+
}
138+
};
139+
```
140+
141+
### Setup group call or Teams meeting options
142+
Depending on what type of Call/Meeting you would like to setup, use the appropriate meeting input. Replace `meetingInput` with either your group call ID or Teams meeting url.
143+
144+
### Apply theme configuration
145+
146+
To change the primary color of composite, create a new theme style in `src/main/res/values/styles.xml` and `src/main/res/values-night/styles.xml` by considering AzureCommunicationUICalling.Theme as parent theme. To apply theme, inject the theme ID in CallCompositeBuilder.
147+
148+
149+
```xml
150+
<style name="MyCompany.CallComposite" parent="AzureCommunicationUICalling.Theme">
151+
<item name="azure_communication_ui_calling_primary_color">#27AC22</item>
152+
<item name="azure_communication_ui_calling_primary_color_tint10">#5EC65A</item>
153+
<item name="azure_communication_ui_calling_primary_color_tint20">#A7E3A5</item>
154+
<item name="azure_communication_ui_calling_primary_color_tint30">#CEF0CD</item>
155+
</style>
156+
```
157+
To apply theme, inject the theme ID in CallCompositeBuilder.
158+
```java
159+
CallComposite callComposite =
160+
new CallCompositeBuilder()
161+
.theme(R.style.MyCompany_CallComposite)
162+
.build();
163+
```
164+
165+
166+
# Limitations
167+
168+
Communication between JavaScript and Swift/Kotlin is limited by the available methods (Callbacks/Promises/Events) provided by the React Native framework. Implementing TokenRefresher in JavaScript is not possible due to this limitation.
169+
Only one of the callback events (resolver / rejector) can be called. If one is called the other one will not be called.
170+
171+
The React Native team is currently working on the re-architecture of the Native Module system, replacing it with TurboModules. It helps facilitate more efficient type-safe communication between JavaScript and native without relying on the React Native bridge. We could revisit our bridging module and update our wrapper API in the future once they have released TurboModules. More info in the highlighted section here: [Android Native Modules · React Native](https://reactnative.dev/docs/new-architecture-app-modules-android)

demo/android/app/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ repositories {
119119
}
120120

121121
dependencies {
122-
implementation 'com.azure.android:azure-communication-ui-calling:1.6.0'
122+
implementation 'com.azure.android:azure-communication-ui-calling:1.7.0'
123123
// The version of react-native is set by the React Native Gradle Plugin
124124
implementation("com.facebook.react:react-android")
125125

demo/android/app/src/main/java/com/demo/RNAzureCommunicationUICalling.java

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@
2626
import com.azure.android.communication.ui.calling.models.CallCompositeParticipantViewData;
2727
import com.azure.android.communication.ui.calling.models.CallCompositeSetupScreenViewData;
2828
import com.azure.android.communication.ui.calling.models.CallCompositeRemoteOptions;
29+
import com.azure.android.communication.ui.calling.models.CallCompositeCallScreenControlBarOptions;
30+
import com.azure.android.communication.ui.calling.models.CallCompositeCallScreenOptions;
2931
import com.azure.android.communication.ui.calling.models.CallCompositeSupportedLocale;
32+
import com.azure.android.communication.ui.calling.models.CallCompositeLeaveCallConfirmationMode;
3033
import com.azure.android.communication.ui.calling.models.CallCompositeSupportedScreenOrientation;
3134
import com.azure.android.communication.ui.calling.models.CallCompositeTeamsMeetingLinkLocator;
3235
import com.azure.android.communication.ui.calling.models.CallCompositeUserReportedIssueEvent;
@@ -87,6 +90,7 @@ public void startCallComposite(ReadableMap localOptions,
8790
String displayName = localOptions.getString("displayName");
8891
String title = localOptions.getString("title");
8992
String subtitle = localOptions.getString("subtitle");
93+
boolean disableLeaveCallConfirmation = localOptions.getBoolean("disableLeaveCallConfirmation");
9094

9195
// remote options
9296
String tokenInput = remoteOptions.getString("token");
@@ -97,10 +101,10 @@ public void startCallComposite(ReadableMap localOptions,
97101
boolean isRightToLeft = localizationOptions.getBoolean("layout");
98102

99103
if (URLUtil.isValidUrl(tokenInput.trim())) {
100-
getCommunicationToken(tokenInput, displayName, meetingInput, localAvatarImageResource, title, subtitle, selectedLanguage, isRightToLeft, remoteAvatarImageResource, orientationOptions, promise);
104+
getCommunicationToken(tokenInput, displayName, meetingInput, localAvatarImageResource, title, subtitle, selectedLanguage, isRightToLeft, disableLeaveCallConfirmation, remoteAvatarImageResource, orientationOptions, promise);
101105
} else {
102106
mToken = tokenInput;
103-
launchComposite(displayName, meetingInput, localAvatarImageResource, title, subtitle, selectedLanguage, isRightToLeft, remoteAvatarImageResource, orientationOptions, promise);
107+
launchComposite(displayName, meetingInput, localAvatarImageResource, title, subtitle, selectedLanguage, isRightToLeft, disableLeaveCallConfirmation, remoteAvatarImageResource, orientationOptions, promise);
104108
}
105109
}
106110

@@ -152,7 +156,7 @@ public void getDebugInfo(Promise promise) {
152156
public void removeListeners(Integer count) {}
153157

154158
public void launchComposite(String displayName, String meetingInput, ReadableMap localAvatarImageResource,
155-
String title, String subtitle, String selectedLanguage, boolean isRightToLeft, ReadableMap remoteAvatarImageResource,
159+
String title, String subtitle, String selectedLanguage, boolean isRightToLeft, boolean disableLeaveCallConfirmation, ReadableMap remoteAvatarImageResource,
156160
ReadableMap orientationOptions,
157161
Promise promise) {
158162
Context context = getCurrentActivity();
@@ -164,11 +168,22 @@ public void launchComposite(String displayName, String meetingInput, ReadableMap
164168

165169
int layoutDirection = isRightToLeft ? LayoutDirection.RTL : LayoutDirection.LTR;
166170

171+
CallCompositeCallScreenControlBarOptions callScreenControlBarOptions = new CallCompositeCallScreenControlBarOptions();
172+
if (disableLeaveCallConfirmation) {
173+
callScreenControlBarOptions.setLeaveCallConfirmation(CallCompositeLeaveCallConfirmationMode.ALWAYS_DISABLED);
174+
} else {
175+
callScreenControlBarOptions.setLeaveCallConfirmation(CallCompositeLeaveCallConfirmationMode.ALWAYS_ENABLED);
176+
}
177+
178+
CallCompositeCallScreenOptions callScreenOptions = new CallCompositeCallScreenOptions();
179+
callScreenOptions.setControlBarOptions(callScreenControlBarOptions);
180+
167181
CallComposite callComposite = new CallCompositeBuilder()
168182
.localization(new CallCompositeLocalizationOptions(Locale.forLanguageTag(selectedLanguage), layoutDirection))
169183
.setupScreenOrientation(getCompositeDefinedOrientation(setupOrientation))
170184
.callScreenOrientation(getCompositeDefinedOrientation(callOrientation))
171185
.multitasking(new CallCompositeMultitaskingOptions(true))
186+
.callScreenOptions(callScreenOptions)
172187
.build();
173188

174189
try {
@@ -297,7 +312,7 @@ private String fetchToken() {
297312
}
298313

299314
private void getCommunicationToken(String tokenInput, String displayName, String meetingInput, ReadableMap localAvatarImageResource, String title, String subtitle, String selectedLanguage,
300-
boolean isRightToLeft, ReadableMap remoteAvatarImageResource,
315+
boolean isRightToLeft, boolean disableLeaveCallConfirmation, ReadableMap remoteAvatarImageResource,
301316
ReadableMap orientationOptions,
302317
Promise promise) {
303318
Thread thread = new Thread(() -> {
@@ -313,7 +328,7 @@ private void getCommunicationToken(String tokenInput, String displayName, String
313328
mToken = json.getString("token");
314329

315330
new Handler(Looper.getMainLooper()).post(() -> {
316-
launchComposite(displayName, meetingInput, localAvatarImageResource, title, subtitle, selectedLanguage, isRightToLeft, remoteAvatarImageResource, orientationOptions, promise);
331+
launchComposite(displayName, meetingInput, localAvatarImageResource, title, subtitle, selectedLanguage, isRightToLeft, disableLeaveCallConfirmation, remoteAvatarImageResource, orientationOptions, promise);
317332
});
318333

319334
} catch (Exception e) {

demo/ios/Podfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ require Pod::Executable.execute_command('node', ['-p',
55
{paths: [process.argv[1]]},
66
)', __dir__]).strip
77

8-
platform :ios, '14.0'
8+
platform :ios, '15.0'
99
prepare_react_native_project!
1010

1111
# If you are using a `react-native-flipper` your iOS build will fail when `NO_FLIPPER=1` is set.
@@ -27,7 +27,7 @@ end
2727

2828
target 'demo' do
2929
use_frameworks!
30-
pod 'AzureCommunicationUICalling', '1.6.0'
30+
pod 'AzureCommunicationUICalling', '1.7.0'
3131
config = use_native_modules!
3232

3333
# Flags change depending on the env values.

0 commit comments

Comments
 (0)