├── .github ├── CODEOWNERS ├── CODE_OF_CONDUCT.md ├── ISSUE_TEMPLATE.md └── PULL_REQUEST_TEMPLATE.md ├── .gitignore ├── AzureCalling ├── app │ ├── .gitignore │ ├── build.gradle │ ├── checkstyle.gradle │ ├── checkstyle │ │ └── checkstyle.xml │ ├── debug.keystore │ ├── proguard-rules.pro │ └── src │ │ └── main │ │ ├── AndroidManifest.xml │ │ ├── assets │ │ └── appSettings.properties │ │ ├── colors.xml │ │ ├── ic_launcher-playstore.png │ │ ├── java │ │ └── com │ │ │ └── azure │ │ │ └── samples │ │ │ └── communication │ │ │ └── calling │ │ │ ├── AzureCalling.java │ │ │ ├── contracts │ │ │ ├── Constants.java │ │ │ └── SampleErrorMessages.java │ │ │ ├── externals │ │ │ ├── authentication │ │ │ │ ├── AADAuthHandler.java │ │ │ │ ├── AuthenticationToken.java │ │ │ │ └── UserProfile.java │ │ │ └── calling │ │ │ │ ├── CallingContext.java │ │ │ │ └── TokenService.java │ │ │ ├── utilities │ │ │ ├── AppSettings.java │ │ │ └── MSGraphRequestWrapper.java │ │ │ └── views │ │ │ ├── activities │ │ │ ├── IntroViewActivity.java │ │ │ ├── InvitationActivity.java │ │ │ ├── JoinCallActivity.java │ │ │ ├── SignInActivity.java │ │ │ └── StartCallActivity.java │ │ │ ├── components │ │ │ └── ErrorInfoBar.java │ │ │ └── fragments │ │ │ ├── AbstractBaseFragment.java │ │ │ ├── GroupMeetingFragment.java │ │ │ └── TeamsMeetingFragment.java │ │ └── res │ │ ├── color-night │ │ ├── button_filled.xml │ │ ├── default_icon_tint.xml │ │ ├── selector_button_filled_text.xml │ │ ├── selector_button_outlined.xml │ │ └── selector_button_outlined_text.xml │ │ ├── color │ │ ├── button_filled.xml │ │ ├── default_icon_tint.xml │ │ ├── selector_button_filled_text.xml │ │ ├── selector_button_outlined.xml │ │ ├── selector_button_outlined_text.xml │ │ ├── selector_primary.xml │ │ └── selector_secondary.xml │ │ ├── drawable-v24 │ │ └── ic_launcher_foreground.xml │ │ ├── drawable │ │ ├── bordered_button_background.xml │ │ ├── button_background.xml │ │ ├── button_filled.xml │ │ ├── ic_acs_vector.xml │ │ ├── ic_display_name.xml │ │ ├── ic_launcher_background.xml │ │ ├── ic_shape.xml │ │ ├── ic_teams_link.xml │ │ ├── layout_background.xml │ │ ├── rounded_corners.xml │ │ ├── selected_tab_left.xml │ │ ├── snackbar_background.xml │ │ ├── tab_background_normal.xml │ │ ├── tab_background_normal_blue.xml │ │ └── tabview_background.xml │ │ ├── layout │ │ ├── activity_intro_view.xml │ │ ├── activity_invitation.xml │ │ ├── activity_join_call.xml │ │ ├── activity_signin.xml │ │ ├── activity_start_call.xml │ │ ├── fragment_group_meeting.xml │ │ ├── fragment_teams_meeting.xml │ │ ├── landing_layout.xml │ │ └── view_intro_actionbar.xml │ │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ ├── ic_launcher_foreground.png │ │ └── ic_launcher_round.png │ │ ├── raw │ │ └── auth_config_single_account.json │ │ ├── values-night │ │ ├── colors.xml │ │ └── themes.xml │ │ ├── values │ │ ├── colors.xml │ │ ├── strings.xml │ │ └── themes.xml │ │ └── xml │ │ ├── backup_rules.xml │ │ └── data_extraction_rules.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── settings.gradle ├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── SCC-SPOOL-CallingSample-Android-PR-YMLyml.yml └── docs └── images ├── aadOverview.png ├── androidConfigurationImage.png ├── intro-page-android.png └── landing-page-android.png /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | # These owners will be the default owners for everything in 2 | # the repo. Unless a later match takes precedence 3 | * @Azure-Samples/acs-ui-android-reviewer-1 @Azure-Samples/acs-ui-android-reviewer-2 @Azure-Samples/acs-ui-android-reviewer-3 4 | -------------------------------------------------------------------------------- /.github/CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Microsoft Open Source Code of Conduct 2 | 3 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 4 | 5 | Resources: 6 | 7 | - [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) 8 | - [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 9 | - Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns 10 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 4 | > Please provide us with the following information: 5 | > --------------------------------------------------------------- 6 | 7 | ### This issue is for a: (mark with an `x`) 8 | ``` 9 | - [ ] bug report -> please search issues before submitting 10 | - [ ] feature request 11 | - [ ] documentation issue or request 12 | - [ ] regression (a behavior that used to work and stopped in a new release) 13 | ``` 14 | 15 | ### Minimal steps to reproduce 16 | > 17 | 18 | ### Any log messages given by the failure 19 | > 20 | 21 | ### Expected/desired behavior 22 | > 23 | 24 | ### OS and Version? 25 | > Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) 26 | 27 | ### Versions 28 | > 29 | 30 | ### Mention any other details that might be useful 31 | 32 | > --------------------------------------------------------------- 33 | > Thanks! We'll be in touch soon. 34 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ## Purpose 2 | 3 | * ... 4 | 5 | ## Does this introduce a breaking change? 6 | 7 | ``` 8 | [ ] Yes 9 | [ ] No 10 | ``` 11 | 12 | ## Pull Request Type 13 | What kind of change does this Pull Request introduce? 14 | 15 | 16 | ``` 17 | [ ] Bugfix 18 | [ ] Feature 19 | [ ] Code style update (formatting, local variables) 20 | [ ] Refactoring (no functional changes, no api changes) 21 | [ ] Documentation content changes 22 | [ ] Other... Please describe: 23 | ``` 24 | 25 | ## How to Test 26 | * Get the code 27 | 28 | ``` 29 | git clone [repo-address] 30 | cd [repo-name] 31 | git checkout [branch-name] 32 | npm install 33 | ``` 34 | 35 | * Test the code 36 | 37 | ``` 38 | ``` 39 | 40 | ## What to Check 41 | Verify that the following are valid 42 | * ... 43 | 44 | ## Other Information 45 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Built application files 2 | *.apk 3 | *.aar 4 | *.ap_ 5 | *.aab 6 | 7 | # Files for the ART/Dalvik VM 8 | *.dex 9 | 10 | # Java class files 11 | *.class 12 | 13 | # Generated files 14 | bin/ 15 | gen/ 16 | out/ 17 | # Uncomment the following line in case you need and you don't have the release build type files in your app 18 | **/release/* 19 | 20 | # Gradle files 21 | **/.gradle/* 22 | build/ 23 | 24 | # DS_Store files 25 | *.DS_Store 26 | 27 | # Local configuration file (sdk path, etc) 28 | local.properties 29 | appSettings.properties 30 | 31 | # Proguard folder generated by Eclipse 32 | proguard/ 33 | 34 | # Log Files 35 | *.log 36 | 37 | # Android Studio Navigation editor temp files 38 | .navigation/ 39 | 40 | # Android Studio captures folder 41 | captures/ 42 | 43 | # IntelliJ 44 | *.iml 45 | **/.idea/* 46 | 47 | # Keystore files 48 | # Uncomment the following lines if you do not want to check your keystore files in. 49 | #*.jks 50 | #*.keystore 51 | 52 | # External native build folder generated in Android Studio 2.2 and later 53 | .externalNativeBuild 54 | .cxx/ 55 | 56 | # Google Services (e.g. APIs or Firebase) 57 | # google-services.json 58 | 59 | # Freeline 60 | freeline.py 61 | freeline/ 62 | freeline_project_description.json 63 | 64 | # fastlane 65 | fastlane/report.xml 66 | fastlane/Preview.html 67 | fastlane/screenshots 68 | fastlane/test_output 69 | fastlane/readme.md 70 | 71 | # Version control 72 | vcs.xml 73 | 74 | # lint 75 | lint/intermediates/ 76 | lint/generated/ 77 | lint/outputs/ 78 | lint/tmp/ 79 | # lint/reports/ 80 | -------------------------------------------------------------------------------- /AzureCalling/app/.gitignore: -------------------------------------------------------------------------------- 1 | /build -------------------------------------------------------------------------------- /AzureCalling/app/build.gradle: -------------------------------------------------------------------------------- 1 | plugins { 2 | id 'com.android.application' 3 | } 4 | 5 | android { 6 | compileSdk 34 7 | 8 | defaultConfig { 9 | applicationId "com.azure.samples.communication.calling" 10 | minSdk 26 11 | targetSdk 34 12 | versionCode 1 13 | versionName "1.0" 14 | testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" 15 | } 16 | 17 | signingConfigs { 18 | debug { 19 | storeFile file('debug.keystore') 20 | storePassword "android" 21 | keyAlias "androiddebugkey" 22 | keyPassword "android" 23 | } 24 | release { 25 | storeFile file(String.valueOf(System.getenv("KEYSTORE_FILEPATH"))) 26 | storePassword System.getenv("KEYSTORE_PASSWORD") 27 | keyAlias System.getenv("KEY_ALIAS") 28 | keyPassword System.getenv("KEY_PASSWORD") 29 | } 30 | } 31 | 32 | buildTypes { 33 | release { 34 | minifyEnabled false 35 | proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' 36 | } 37 | } 38 | compileOptions { 39 | sourceCompatibility JavaVersion.VERSION_1_8 40 | targetCompatibility JavaVersion.VERSION_1_8 41 | } 42 | 43 | packagingOptions { 44 | pickFirst 'META-INF/*' 45 | exclude 'META-INF/DEPENDENCIES' 46 | exclude 'META-INF/LICENSE' 47 | exclude 'META-INF/LICENSE.txt' 48 | exclude 'META-INF/LICENSE.md' 49 | exclude 'META-INF/license.txt' 50 | exclude 'META-INF/NOTICE' 51 | exclude 'META-INF/NOTICE.txt' 52 | exclude 'META-INF/NOTICE.md' 53 | exclude 'META-INF/notice.txt' 54 | exclude 'META-INF/ASL2.0' 55 | exclude 'META-INF/INDEX.LIST' 56 | exclude 'META-INF/jersey-module-version' 57 | } 58 | } 59 | 60 | dependencies { 61 | 62 | implementation 'androidx.appcompat:appcompat:1.6.1' 63 | implementation 'com.google.android.material:material:1.9.0' 64 | implementation 'androidx.constraintlayout:constraintlayout:2.1.4' 65 | implementation 'com.azure.android:azure-communication-ui-calling:1.6.0' 66 | implementation 'com.microsoft.identity.client:msal:4.7.0' 67 | implementation 'com.microsoft.fluentui:FluentUIAndroid:0.1.35' 68 | implementation 'com.android.volley:volley:1.2.1' 69 | } 70 | -------------------------------------------------------------------------------- /AzureCalling/app/checkstyle.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'checkstyle' 2 | 3 | checkstyle { 4 | description 'Check codeing standards' 5 | configFile file('checkstyle/checkstyle.xml') 6 | } 7 | 8 | task checkstyle(type: Checkstyle) { 9 | source 'src' 10 | include '**/*.java' 11 | exclude '**/gen/**' 12 | exclude '**/R.java' 13 | exclude '**/BuildConfig.java' 14 | classpath = files() 15 | } -------------------------------------------------------------------------------- /AzureCalling/app/checkstyle/checkstyle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 83 | 84 | 85 | 86 | 87 | 88 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 136 | 137 | 138 | 139 | 140 | 142 | 143 | 144 | 145 | 146 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 166 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 194 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 269 | 270 | 271 | 272 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | -------------------------------------------------------------------------------- /AzureCalling/app/debug.keystore: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/debug.keystore -------------------------------------------------------------------------------- /AzureCalling/app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # You can control the set of applied configuration files using the 3 | # proguardFiles setting in build.gradle. 4 | # 5 | # For more details, see 6 | # http://developer.android.com/guide/developing/tools/proguard.html 7 | 8 | # If your project uses WebView with JS, uncomment the following 9 | # and specify the fully qualified class name to the JavaScript interface 10 | # class: 11 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 12 | # public *; 13 | #} 14 | 15 | # Uncomment this to preserve the line number information for 16 | # debugging stack traces. 17 | #-keepattributes SourceFile,LineNumberTable 18 | 19 | # If you keep the line number information, uncomment this to 20 | # hide the original source file name. 21 | #-renamesourcefileattribute SourceFile -------------------------------------------------------------------------------- /AzureCalling/app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 17 | 22 | 25 | 26 | 30 | 33 | 34 | 38 | 41 | 42 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | 58 | 60 | 61 | 62 | 63 | 64 | 65 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/assets/appSettings.properties: -------------------------------------------------------------------------------- 1 | # the address of the azure function used for access token generation 2 | communicationTokenFetchUrl= 3 | 4 | # to turn on Azure Active Directory authentication. (true/false) 5 | isAADAuthEnabled=false 6 | 7 | # multiple scopes can be entered as scopes = a,b,c (no space after ,) 8 | aadScopes= 9 | 10 | # Microsoft Graph API endpoint 11 | graphURL= 12 | 13 | # The tenant-id of Azure Active Directory (Optional) 14 | tenant= 15 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | 11 | @android:color/white 12 | @android:color/white 13 | @color/grey400 14 | #0078D4 15 | #C7E0F4 16 | @color/grey950 17 | @color/grey700 18 | @color/grey500 19 | @color/grey300 20 | @android:color/white 21 | @color/grey900 22 | #F9D9D9 23 | #A52121 24 | 25 | @android:color/black 26 | @android:color/white 27 | #E1E1E1 28 | 29 | @android:color/white 30 | @android:color/black 31 | 32 | #FF141414 33 | #212121 34 | #FF303030 35 | #FF6E6E6E 36 | #FF919191 37 | #FFACACAC 38 | #D9303030 39 | #FFF1F1F1 40 | #605E5C 41 | 42 | @android:color/black 43 | @android:color/white 44 | 45 | #0063B1 46 | #F1F1F1 47 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/AzureCalling.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling; 5 | 6 | import android.app.Application; 7 | import com.azure.samples.communication.calling.externals.authentication.AADAuthHandler; 8 | import com.azure.samples.communication.calling.utilities.AppSettings; 9 | import com.azure.samples.communication.calling.externals.calling.CallingContext; 10 | import com.azure.samples.communication.calling.externals.calling.TokenService; 11 | 12 | public class AzureCalling extends Application { 13 | 14 | public static final String IN_CALL_CHANNEL_ID = "IN_CALL"; 15 | 16 | private AppSettings appSettings; 17 | private CallingContext callingContext; 18 | private AADAuthHandler aadAuthHandler; 19 | private TokenService tokenService; 20 | 21 | @Override 22 | public void onCreate() { 23 | super.onCreate(); 24 | 25 | initializeDependencies(); 26 | } 27 | 28 | private void initializeDependencies() { 29 | this.appSettings = new AppSettings(getApplicationContext()); 30 | final String tokenGenerationAddress = appSettings.getCommunicationToken(); 31 | this.aadAuthHandler = new AADAuthHandler(appSettings); 32 | this.tokenService = new TokenService( 33 | getApplicationContext(), tokenGenerationAddress, () -> aadAuthHandler.getAccessToken()); 34 | } 35 | 36 | public void createCallingContext() { 37 | this.callingContext = new CallingContext(getApplicationContext(), 38 | () -> tokenService.getCommunicationTokenAsync().get()); 39 | } 40 | 41 | public CallingContext getCallingContext() { 42 | return this.callingContext; 43 | } 44 | 45 | public AADAuthHandler getAadAuthHandler() { 46 | return aadAuthHandler; 47 | } 48 | 49 | public AppSettings getAppSettings() { 50 | return appSettings; 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/contracts/Constants.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.contracts; 5 | 6 | public class Constants { 7 | public static final String ACS_SHARED_PREF = "ACS_SHARED_PREF"; 8 | public static final String ACS_GROUPCALL_ID = "ACS_GROUPCALL_ID"; 9 | public static final String ACS_MEETING_LINK = "ACS_MEETING_LINK"; 10 | 11 | public static final String IS_LOGGED_IN = "IS_LOGGED_IN"; 12 | 13 | public static final String DISPLAY_NAME = "displayName"; 14 | public static final String USERNAME = "username"; 15 | public static final String GIVEN_NAME = "givenName"; 16 | public static final String ID = "id"; 17 | 18 | public static final String START_CALL = "Start a call"; 19 | public static final String JOIN_CALL = "Join"; 20 | public static final String INVITE_ANOTHER_DEVICE = "Invite another device?"; 21 | } 22 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/contracts/SampleErrorMessages.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.contracts; 5 | 6 | public class SampleErrorMessages { 7 | public static final String GROUP_ID_REQUIRED = "Group call ID is required"; 8 | public static final String GROUP_ID_INVALID = "Group call ID is not valid"; 9 | public static final String DISPLAY_NAME_REQUIRED = "Display Name is required"; 10 | public static final String TEAMS_LINK_REQUIRED = "Teams Link is required"; 11 | public static final String TEAMS_LINK_INVALID = "Teams Link is not valid"; 12 | public static final String CALL_COMPOSITE_JOIN_CALL_FAILED = "Could not join call"; 13 | public static final String CALL_COMPOSITE_END_CALL_FAILED = "Could not end call"; 14 | public static final String CALL_COMPOSITE_TOKEN_EXPIRED = "ACS Token has expired"; 15 | public static final String USER_LOGIN_CANCEL = "User cancelled login"; 16 | } 17 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/externals/authentication/AADAuthHandler.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.externals.authentication; 5 | 6 | import static com.azure.samples.communication.calling.contracts.Constants.DISPLAY_NAME; 7 | import static com.azure.samples.communication.calling.contracts.Constants.GIVEN_NAME; 8 | import static com.azure.samples.communication.calling.contracts.Constants.ID; 9 | import static com.azure.samples.communication.calling.contracts.SampleErrorMessages.USER_LOGIN_CANCEL; 10 | 11 | import android.app.Activity; 12 | import android.os.Build; 13 | import android.util.Log; 14 | import androidx.annotation.NonNull; 15 | import androidx.annotation.Nullable; 16 | import androidx.annotation.RequiresApi; 17 | import com.android.volley.Response; 18 | import com.android.volley.VolleyError; 19 | import com.azure.samples.communication.calling.R; 20 | import com.azure.samples.communication.calling.utilities.AppSettings; 21 | import com.azure.samples.communication.calling.utilities.MSGraphRequestWrapper; 22 | import com.microsoft.identity.client.AcquireTokenSilentParameters; 23 | import com.microsoft.identity.client.AuthenticationCallback; 24 | import com.microsoft.identity.client.AzureCloudInstance; 25 | import com.microsoft.identity.client.IAccount; 26 | import com.microsoft.identity.client.IAuthenticationResult; 27 | import com.microsoft.identity.client.IPublicClientApplication; 28 | import com.microsoft.identity.client.ISingleAccountPublicClientApplication; 29 | import com.microsoft.identity.client.PublicClientApplication; 30 | import com.microsoft.identity.client.SilentAuthenticationCallback; 31 | import com.microsoft.identity.client.exception.MsalException; 32 | 33 | import org.json.JSONException; 34 | import org.json.JSONObject; 35 | 36 | import java.util.Arrays; 37 | import java.util.function.Consumer; 38 | 39 | public class AADAuthHandler { 40 | 41 | private static final String LOG_TAG = AADAuthHandler.class.getSimpleName(); 42 | 43 | private final AppSettings appSettings; 44 | private ISingleAccountPublicClientApplication mSingleAccountApp; 45 | private String accessToken = null; 46 | 47 | public AADAuthHandler(final AppSettings appSettings) { 48 | this.appSettings = appSettings; 49 | } 50 | 51 | public void signIn(final Activity activity, final Consumer callback) { 52 | if (mSingleAccountApp == null) { 53 | return; 54 | } 55 | mSingleAccountApp.signIn(activity, null, appSettings.getAADScopes(), new AuthenticationCallback() { 56 | 57 | @Override 58 | public void onSuccess(final IAuthenticationResult authenticationResult) { 59 | accessToken = authenticationResult.getAccessToken(); 60 | appSettings.getAuthenticationToken().setToken(accessToken); 61 | callGraphAPI(activity, (object) -> { 62 | callback.accept(object); 63 | }); 64 | } 65 | 66 | @Override 67 | public void onError(final MsalException exception) { 68 | Log.e(LOG_TAG, exception.getMessage()); 69 | } 70 | 71 | @Override 72 | public void onCancel() { 73 | Log.d(LOG_TAG, USER_LOGIN_CANCEL); 74 | } 75 | }); 76 | } 77 | 78 | private void findUserProfile(final Activity activity, final Consumer authCallback) { 79 | 80 | MSGraphRequestWrapper.callGraphAPIUsingVolley( 81 | activity, 82 | appSettings.getGraphUrl() + "/me", 83 | appSettings.getAuthenticationToken().getToken(), 84 | new Response.Listener() { 85 | @Override 86 | public void onResponse(final JSONObject response) { 87 | 88 | try { 89 | final UserProfile userProfile = appSettings.getUserProfile(); 90 | userProfile.setDisplayName(response.get(DISPLAY_NAME).toString()); 91 | userProfile.setUsername(response.get(DISPLAY_NAME).toString()); 92 | userProfile.setGivenName(response.get(GIVEN_NAME).toString()); 93 | userProfile.setId(response.get(ID).toString()); 94 | 95 | authCallback.accept(userProfile); 96 | } catch (JSONException e) { 97 | e.printStackTrace(); 98 | } 99 | } 100 | }, 101 | new Response.ErrorListener() { 102 | @Override 103 | public void onErrorResponse(final VolleyError error) { 104 | //Log.e(LOG_TAG, error.getMessage()); 105 | authCallback.accept(error); 106 | } 107 | } 108 | ); 109 | } 110 | 111 | public void callGraphAPI(final Activity activity, final Consumer authCallback) { 112 | 113 | if (accessToken == null || accessToken.length() == 0) { 114 | findUserProfile(activity, authCallback); 115 | } else { 116 | findUserProfile(activity, authCallback); 117 | } 118 | } 119 | 120 | public String getAccessToken() { 121 | return appSettings.getAuthenticationToken().getToken(); 122 | } 123 | 124 | private void signingOut(final Consumer signoutCallback) { 125 | mSingleAccountApp.signOut(new ISingleAccountPublicClientApplication.SignOutCallback() { 126 | @Override 127 | public void onSignOut() { 128 | signoutCallback.accept(true); 129 | } 130 | 131 | @Override 132 | public void onError(@NonNull final MsalException exception) { 133 | Log.e(LOG_TAG, exception.getMessage()); 134 | signoutCallback.accept(false); 135 | } 136 | }); 137 | } 138 | 139 | public void signOut(final Activity activity, final Consumer signOutCallback) { 140 | if (mSingleAccountApp == null) { 141 | signOutCallback.accept(false); 142 | } else { 143 | signingOut(signOutCallback); 144 | } 145 | } 146 | 147 | private void acquireTokenSilently(final Activity activity, 148 | final IAccount account, 149 | final Consumer callback) { 150 | 151 | mSingleAccountApp.acquireTokenSilentAsync( 152 | new AcquireTokenSilentParameters( 153 | new AcquireTokenSilentParameters 154 | .Builder() 155 | .withScopes(Arrays.asList(appSettings.getAADScopes())) 156 | .fromAuthority(AzureCloudInstance.AzurePublic, 157 | appSettings.getTenantId()) 158 | .forAccount(account) 159 | .withCallback(new SilentAuthenticationCallback() { 160 | @Override 161 | public void onSuccess(final IAuthenticationResult authenticationResult) { 162 | accessToken = authenticationResult.getAccessToken(); 163 | appSettings.getAuthenticationToken().setToken(accessToken); 164 | callGraphAPI(activity, (object) -> { 165 | callback.accept(object); 166 | }); 167 | } 168 | 169 | @Override 170 | public void onError(final MsalException exception) { 171 | acquireToken(activity, (object) -> { 172 | callback.accept(object); 173 | }); 174 | Log.e(LOG_TAG, exception.getMessage()); 175 | } 176 | }).self())); 177 | } 178 | 179 | private void acquireToken(final Activity activity, final Consumer callback) { 180 | 181 | mSingleAccountApp.acquireToken(activity, appSettings.getAADScopes(), new AuthenticationCallback() { 182 | @RequiresApi(api = Build.VERSION_CODES.N) 183 | @Override 184 | public void onSuccess(final IAuthenticationResult authenticationResult) { 185 | accessToken = authenticationResult.getAccessToken(); 186 | appSettings.getAuthenticationToken().setToken(accessToken); 187 | callGraphAPI(activity, callback); 188 | } 189 | 190 | @Override 191 | public void onError(final MsalException exception) { 192 | Log.e(LOG_TAG, exception.getMessage()); 193 | } 194 | 195 | @Override 196 | public void onCancel() { 197 | Log.d(LOG_TAG, "User cancelled login."); 198 | } 199 | }); 200 | } 201 | 202 | private void getCurrentAccount(final Activity activity, 203 | final boolean isLoggedIn, 204 | final Consumer aadCallback) { 205 | if (mSingleAccountApp == null) { 206 | return; 207 | } 208 | 209 | mSingleAccountApp.getCurrentAccountAsync(new ISingleAccountPublicClientApplication.CurrentAccountCallback() { 210 | @RequiresApi(api = Build.VERSION_CODES.N) 211 | @Override 212 | public void onAccountLoaded(@Nullable final IAccount activeAccount) { 213 | if (activeAccount == null) { 214 | aadCallback.accept(new Boolean(false)); 215 | } else { 216 | if (isLoggedIn) { 217 | acquireTokenSilently(activity, activeAccount, (object) -> { 218 | aadCallback.accept(object); 219 | }); 220 | } else { 221 | acquireToken(activity, (object) -> { 222 | aadCallback.accept(object); 223 | }); 224 | } 225 | } 226 | } 227 | 228 | @RequiresApi(api = Build.VERSION_CODES.N) 229 | @Override 230 | public void onAccountChanged( 231 | @Nullable final IAccount priorAccount, 232 | @Nullable final IAccount currentAccount) { 233 | if (currentAccount == null) { 234 | // Perform a cleanup task as the signed-in account changed. 235 | aadCallback.accept(new Boolean(false)); 236 | } 237 | } 238 | 239 | @Override 240 | public void onError(@NonNull final MsalException exception) { 241 | Log.e(LOG_TAG, exception.getMessage()); 242 | } 243 | }); 244 | } 245 | 246 | 247 | //When app comes to the foreground, load existing account to determine if user is signed in 248 | public void loadAccount(final Activity activity, final boolean isLoggedIn, final Consumer aadCallback) { 249 | PublicClientApplication.createSingleAccountPublicClientApplication(activity.getApplicationContext(), 250 | R.raw.auth_config_single_account, 251 | new IPublicClientApplication.ISingleAccountApplicationCreatedListener() { 252 | @Override 253 | public void onCreated(final ISingleAccountPublicClientApplication application) { 254 | mSingleAccountApp = application; 255 | getCurrentAccount(activity, isLoggedIn, aadCallback); 256 | } 257 | 258 | @Override 259 | public void onError(final MsalException exception) { 260 | Log.e(LOG_TAG, exception.getMessage()); 261 | } 262 | }); 263 | } 264 | } 265 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/externals/authentication/AuthenticationToken.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.externals.authentication; 5 | 6 | public class AuthenticationToken { 7 | private String token; 8 | 9 | public String getToken() { 10 | return token; 11 | } 12 | 13 | public void setToken(final String token) { 14 | this.token = token; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/externals/authentication/UserProfile.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.externals.authentication; 5 | 6 | public class UserProfile { 7 | private String username = ""; 8 | private String displayName = ""; 9 | private String givenName; 10 | private String surname; 11 | private String email; 12 | private String id; 13 | 14 | public String getUsername() { 15 | return username; 16 | } 17 | 18 | public void setUsername(final String username) { 19 | this.username = username; 20 | } 21 | 22 | public String getDisplayName() { 23 | return displayName; 24 | } 25 | 26 | public void setDisplayName(final String displayName) { 27 | this.displayName = displayName; 28 | } 29 | 30 | public String getGivenName() { 31 | return givenName; 32 | } 33 | 34 | public void setGivenName(final String givenName) { 35 | this.givenName = givenName; 36 | } 37 | 38 | public String getSurname() { 39 | return surname; 40 | } 41 | 42 | public void setSurname(final String surname) { 43 | this.surname = surname; 44 | } 45 | 46 | public String getEmail() { 47 | return email; 48 | } 49 | 50 | public void setEmail(final String email) { 51 | this.email = email; 52 | } 53 | 54 | public String getId() { 55 | return id; 56 | } 57 | 58 | public void setId(final String id) { 59 | this.id = id; 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/externals/calling/CallingContext.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.externals.calling; 5 | 6 | import android.content.Context; 7 | import com.azure.android.communication.common.CommunicationTokenCredential; 8 | import com.azure.android.communication.common.CommunicationTokenRefreshOptions; 9 | import com.azure.android.communication.ui.calling.models.CallCompositeGroupCallLocator; 10 | import com.azure.android.communication.ui.calling.models.CallCompositeJoinLocator; 11 | import com.azure.android.communication.ui.calling.models.CallCompositeRemoteOptions; 12 | import com.azure.android.communication.ui.calling.models.CallCompositeTeamsMeetingLinkLocator; 13 | 14 | import java.util.UUID; 15 | import java.util.concurrent.Callable; 16 | 17 | public class CallingContext { 18 | 19 | Context appContext; 20 | private final Callable tokenFetcher; 21 | private UUID joinUUID; 22 | private String joinId; 23 | 24 | public CallingContext(final Context applicationContext, final Callable tokenFetcher) { 25 | this.tokenFetcher = tokenFetcher; 26 | appContext = applicationContext; 27 | } 28 | 29 | public CommunicationTokenCredential getCommunicationTokenCredential() { 30 | final CommunicationTokenRefreshOptions communicationTokenRefreshOptions = 31 | new CommunicationTokenRefreshOptions(tokenFetcher, true); 32 | return new CommunicationTokenCredential(communicationTokenRefreshOptions); 33 | } 34 | 35 | public CallCompositeRemoteOptions getCallCompositeRemoteOptions(final String displayName) { 36 | final CallCompositeJoinLocator locator; 37 | joinUUID = UUID.randomUUID(); 38 | joinId = joinUUID.toString(); 39 | locator = new CallCompositeGroupCallLocator(joinUUID); 40 | 41 | return new CallCompositeRemoteOptions(locator, 42 | getCommunicationTokenCredential(), 43 | displayName); 44 | } 45 | 46 | public CallCompositeRemoteOptions getCallCompositeGroupRemoteOptions(final String displayName, 47 | final UUID groupCallID) { 48 | final CallCompositeJoinLocator locator = new CallCompositeGroupCallLocator(groupCallID); 49 | return new CallCompositeRemoteOptions(locator, 50 | getCommunicationTokenCredential(), 51 | displayName); 52 | } 53 | 54 | public CallCompositeRemoteOptions getCallCompositeRemoteOptions(final String displayName, 55 | final String teamsLink) { 56 | final CallCompositeJoinLocator locator = new CallCompositeTeamsMeetingLinkLocator(teamsLink); 57 | return new CallCompositeRemoteOptions(locator, 58 | getCommunicationTokenCredential(), 59 | displayName); 60 | } 61 | 62 | public String getJoinId() { 63 | return joinId; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/externals/calling/TokenService.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.externals.calling; 5 | 6 | import android.content.Context; 7 | import android.util.Log; 8 | import com.android.volley.RequestQueue; 9 | import com.android.volley.Response; 10 | import com.android.volley.toolbox.JsonObjectRequest; 11 | import com.android.volley.toolbox.Volley; 12 | import org.json.JSONException; 13 | import org.json.JSONObject; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | import java.util.concurrent.Callable; 17 | import java.util.concurrent.CompletableFuture; 18 | 19 | public final class TokenService { 20 | private static final String LOG_TAG = TokenService.class.getSimpleName(); 21 | 22 | private final RequestQueue queue; 23 | private final String communicationTokenFetchUrl; 24 | private final Callable getAuthTokenFunction; 25 | 26 | public TokenService( 27 | final Context applicationContext, 28 | final String communicationTokenFetchUrl, 29 | final Callable getAuthTokenFunction) { 30 | this.communicationTokenFetchUrl = communicationTokenFetchUrl; 31 | this.getAuthTokenFunction = getAuthTokenFunction; 32 | this.queue = Volley.newRequestQueue(applicationContext); 33 | } 34 | 35 | public CompletableFuture getCommunicationTokenAsync() { 36 | final CompletableFuture tokenCompletableFuture = new CompletableFuture<>(); 37 | final Response.Listener responseListener = response -> 38 | parseResponse(response, tokenCompletableFuture); 39 | final Response.ErrorListener errorListener = error -> Log.e(LOG_TAG, 40 | "Failed getting communication token", error); 41 | final JsonObjectRequest request = new JsonObjectRequest( 42 | communicationTokenFetchUrl, null, responseListener, errorListener) { 43 | @Override 44 | public Map getHeaders() { 45 | final HashMap headers = new HashMap<>(); 46 | String secureToken = null; 47 | try { 48 | secureToken = getAuthTokenFunction.call(); 49 | } catch (final Exception e) { 50 | e.printStackTrace(); 51 | } 52 | if (secureToken != null) { 53 | headers.put("Authorization", "Bearer " + secureToken); 54 | } 55 | return headers; 56 | } 57 | }; 58 | this.queue.add(request); 59 | 60 | return tokenCompletableFuture; 61 | } 62 | 63 | private void parseResponse(final JSONObject response, final CompletableFuture tokenCompletableFuture) { 64 | try { 65 | final String userToken = response.getString("token"); 66 | tokenCompletableFuture.complete(userToken); 67 | } catch (final JSONException e) { 68 | e.printStackTrace(); 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/utilities/AppSettings.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.utilities; 5 | 6 | import android.content.Context; 7 | 8 | import com.azure.samples.communication.calling.externals.authentication.AuthenticationToken; 9 | import com.azure.samples.communication.calling.externals.authentication.UserProfile; 10 | 11 | import java.io.IOException; 12 | import java.util.Properties; 13 | 14 | public final class AppSettings { 15 | private static final String CONFIG_FILE = "appSettings.properties"; 16 | private static final String TOKEN_URL = "communicationTokenFetchUrl"; 17 | private static final String IS_AAD_AUTH_ENABLED = "isAADAuthEnabled"; 18 | private static final String SCOPES = "aadScopes"; 19 | private static final String GRAPH_URL = "graphURL"; 20 | private static final String TENANT_ID = "tenant"; 21 | private static Context context; 22 | 23 | 24 | private AuthenticationToken authenticationToken; 25 | private UserProfile userProfile; 26 | 27 | private final Properties properties; 28 | 29 | public AppSettings(final Context context) { 30 | this.context = context; 31 | authenticationToken = new AuthenticationToken(); 32 | userProfile = new UserProfile(); 33 | try { 34 | properties = new Properties(); 35 | properties.load(context.getAssets().open(CONFIG_FILE)); 36 | } catch (final IOException e) { 37 | throw new RuntimeException(e); 38 | } 39 | } 40 | 41 | public String getCommunicationToken() { 42 | return properties.getProperty(TOKEN_URL); 43 | } 44 | 45 | public String[] getAADScopes() { 46 | return properties.getProperty(SCOPES).split(","); 47 | } 48 | 49 | public boolean isAADAuthEnabled() { 50 | final String isAadAuthEnabled = properties.getProperty(IS_AAD_AUTH_ENABLED); 51 | return Boolean.parseBoolean(isAadAuthEnabled); 52 | } 53 | 54 | public String getGraphUrl() { 55 | return properties.getProperty(GRAPH_URL); 56 | } 57 | 58 | public String getTenantId() { 59 | return (properties.getProperty(TENANT_ID) == null) ? "" : properties.getProperty(TENANT_ID); 60 | } 61 | 62 | public Context getContext() { 63 | return this.context; 64 | } 65 | 66 | public AuthenticationToken getAuthenticationToken() { 67 | return authenticationToken; 68 | } 69 | 70 | public UserProfile getUserProfile() { 71 | return userProfile; 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/utilities/MSGraphRequestWrapper.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.utilities; 5 | 6 | import android.content.Context; 7 | import android.util.Log; 8 | import androidx.annotation.NonNull; 9 | import com.android.volley.DefaultRetryPolicy; 10 | import com.android.volley.Request; 11 | import com.android.volley.RequestQueue; 12 | import com.android.volley.Response; 13 | import com.android.volley.toolbox.JsonObjectRequest; 14 | import com.android.volley.toolbox.Volley; 15 | 16 | import org.json.JSONObject; 17 | 18 | import java.util.HashMap; 19 | import java.util.Map; 20 | 21 | public class MSGraphRequestWrapper { 22 | 23 | private static final String TAG = MSGraphRequestWrapper.class.getSimpleName(); 24 | 25 | /** 26 | * Use Volley to make an HTTP request with 27 | * 1) a given MSGraph resource URL 28 | * 2) an access token 29 | * to obtain MSGraph data. 30 | **/ 31 | public static void callGraphAPIUsingVolley(@NonNull final Context context, 32 | @NonNull final String graphResourceUrl, 33 | @NonNull final String accessToken, 34 | @NonNull final Response.Listener responseListener, 35 | @NonNull final Response.ErrorListener errorListener) { 36 | /* Make sure we have a token to send to graph */ 37 | if (accessToken == null || accessToken.length() == 0) { 38 | return; 39 | } 40 | 41 | final RequestQueue queue = Volley.newRequestQueue(context); 42 | final JSONObject parameters = new JSONObject(); 43 | 44 | try { 45 | parameters.put("key", "value"); 46 | } catch (Exception e) { 47 | Log.d(TAG, "Failed to put parameters: " + e.toString()); 48 | } 49 | 50 | final JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, graphResourceUrl, 51 | parameters, responseListener, errorListener) { 52 | @Override 53 | public Map getHeaders() { 54 | final Map headers = new HashMap<>(); 55 | headers.put("Authorization", "Bearer " + accessToken); 56 | return headers; 57 | } 58 | }; 59 | 60 | request.setRetryPolicy(new DefaultRetryPolicy( 61 | 3000, 62 | DefaultRetryPolicy.DEFAULT_MAX_RETRIES, 63 | DefaultRetryPolicy.DEFAULT_BACKOFF_MULT)); 64 | queue.add(request); 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/activities/IntroViewActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.activities; 5 | 6 | import static com.azure.samples.communication.calling.contracts.Constants.IS_LOGGED_IN; 7 | import androidx.appcompat.app.ActionBar; 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.SharedPreferences; 12 | import android.os.Bundle; 13 | import android.view.View; 14 | import android.widget.TextView; 15 | import com.azure.samples.communication.calling.AzureCalling; 16 | import com.azure.samples.communication.calling.R; 17 | import com.azure.samples.communication.calling.contracts.Constants; 18 | import com.azure.samples.communication.calling.externals.authentication.AADAuthHandler; 19 | import com.azure.samples.communication.calling.externals.authentication.UserProfile; 20 | import com.azure.samples.communication.calling.utilities.AppSettings; 21 | import com.microsoft.fluentui.persona.AvatarView; 22 | import com.microsoft.fluentui.widget.Button; 23 | 24 | public class IntroViewActivity extends AppCompatActivity { 25 | 26 | private static final String LOG_TAG = IntroViewActivity.class.getSimpleName(); 27 | 28 | private TextView usernameTextView; 29 | private AvatarView avatarView; 30 | private Button signOutButton; 31 | private Button startCallButton; 32 | private Button joinCallButton; 33 | 34 | private AADAuthHandler authHandler; 35 | private AppSettings appSettings; 36 | 37 | private SharedPreferences sharedPreferences; 38 | 39 | private String username; 40 | 41 | @Override 42 | protected void onCreate(final Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_intro_view); 45 | 46 | authHandler = ((AzureCalling) getApplication()).getAadAuthHandler(); 47 | appSettings = ((AzureCalling) getApplication()).getAppSettings(); 48 | 49 | 50 | sharedPreferences = this.getSharedPreferences(Constants.ACS_SHARED_PREF, Context.MODE_PRIVATE); 51 | final boolean isLoggedIn = sharedPreferences.getBoolean(IS_LOGGED_IN, false); 52 | 53 | if (appSettings.isAADAuthEnabled()) { 54 | if (!isLoggedIn) { 55 | sharedPreferences.edit().clear().apply(); 56 | final Intent intent = new Intent(this, SignInActivity.class); 57 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 58 | startActivity(intent); 59 | } else if (isLoggedIn && appSettings.getAuthenticationToken().getToken() == null) { 60 | final View progressOverlay = findViewById(R.id.intro_overlay_loading); 61 | progressOverlay.setVisibility(View.VISIBLE); 62 | authHandler.loadAccount(this, isLoggedIn, (profile) -> { 63 | if (profile instanceof UserProfile) { 64 | ((TextView) findViewById(R.id.username_textview)) 65 | .setText(((UserProfile) profile).getUsername()); 66 | ((AvatarView) findViewById(R.id.avatar_view)) 67 | .setName(((UserProfile) profile).getUsername()); 68 | ((AvatarView) findViewById(R.id.avatar_view)).invalidate(); 69 | } 70 | progressOverlay.setVisibility(View.GONE); 71 | }); 72 | } 73 | } 74 | 75 | 76 | } 77 | 78 | @Override 79 | protected void onResume() { 80 | super.onResume(); 81 | initializeUI(); 82 | } 83 | 84 | @Override 85 | public void onBackPressed() { 86 | super.onBackPressed(); 87 | finish(); 88 | } 89 | 90 | private void initializeUI() { 91 | 92 | username = appSettings.getUserProfile().getUsername(); 93 | 94 | startCallButton = findViewById(R.id.start_call_button); 95 | startCallButton.setOnClickListener(l -> startCall()); 96 | 97 | joinCallButton = findViewById(R.id.join_call_button); 98 | joinCallButton.setOnClickListener(l -> joinCall()); 99 | 100 | final ActionBar actionBar = getSupportActionBar(); 101 | 102 | if (actionBar != null) { 103 | actionBar.setDisplayShowCustomEnabled(true); 104 | actionBar.setCustomView(R.layout.view_intro_actionbar); 105 | } 106 | 107 | signOutButton = findViewById(R.id.signout_button); 108 | if (!appSettings.isAADAuthEnabled()) { 109 | signOutButton.setText(getString(R.string.sign_in)); 110 | } 111 | signOutButton.setOnClickListener(l -> signOut()); 112 | 113 | usernameTextView = findViewById(R.id.username_textview); 114 | usernameTextView.setText(username); 115 | 116 | avatarView = findViewById(R.id.avatar_view); 117 | avatarView.setName(username); 118 | } 119 | 120 | private void signOut() { 121 | 122 | if (!appSettings.isAADAuthEnabled()) { 123 | return; 124 | } 125 | usernameTextView.setText(""); 126 | avatarView.setName(""); 127 | 128 | authHandler.signOut(this, (isSignedOut) -> { 129 | sharedPreferences.edit().clear().apply(); 130 | final Intent intent = new Intent(this, SignInActivity.class); 131 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 132 | startActivity(intent); 133 | }); 134 | } 135 | 136 | private void startCall() { 137 | final Intent intent = new Intent(this, StartCallActivity.class); 138 | startActivity(intent); 139 | } 140 | 141 | private void joinCall() { 142 | final Intent intent = new Intent(this, JoinCallActivity.class); 143 | startActivity(intent); 144 | } 145 | } 146 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/activities/InvitationActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.activities; 5 | 6 | import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP; 7 | import static com.azure.samples.communication.calling.contracts.Constants.INVITE_ANOTHER_DEVICE; 8 | 9 | import androidx.appcompat.app.ActionBar; 10 | import androidx.appcompat.app.AppCompatActivity; 11 | import androidx.core.app.NavUtils; 12 | 13 | import android.app.Activity; 14 | import android.content.Context; 15 | import android.content.Intent; 16 | import android.content.SharedPreferences; 17 | import android.os.Bundle; 18 | import android.view.MenuItem; 19 | 20 | import com.azure.android.communication.ui.calling.CallComposite; 21 | import com.azure.android.communication.ui.calling.CallCompositeBuilder; 22 | import com.azure.android.communication.ui.calling.CallCompositeEventHandler; 23 | import com.azure.android.communication.ui.calling.models.CallCompositeErrorCode; 24 | import com.azure.android.communication.ui.calling.models.CallCompositeErrorEvent; 25 | import com.azure.android.communication.ui.calling.models.CallCompositeMultitaskingOptions; 26 | import com.azure.android.communication.ui.calling.models.CallCompositeRemoteOptions; 27 | import com.azure.samples.communication.calling.AzureCalling; 28 | import com.azure.samples.communication.calling.R; 29 | import com.azure.samples.communication.calling.contracts.Constants; 30 | import com.azure.samples.communication.calling.contracts.SampleErrorMessages; 31 | import com.azure.samples.communication.calling.externals.calling.CallingContext; 32 | import com.azure.samples.communication.calling.utilities.AppSettings; 33 | import com.azure.samples.communication.calling.views.components.ErrorInfoBar; 34 | import com.microsoft.fluentui.widget.Button; 35 | 36 | public class InvitationActivity extends AppCompatActivity { 37 | 38 | private Button startCallButton; 39 | private Button shareCallButton; 40 | 41 | private Activity activity; 42 | private CallingContext callingContext; 43 | private CallCompositeRemoteOptions options; 44 | private SharedPreferences sharedPreferences; 45 | private ErrorInfoBar errorInfoBar; 46 | private AppSettings appSettings; 47 | private CallCompositeEventHandler callCompositeEventHandler = 48 | new CallCompositeEventHandler() { 49 | @Override 50 | public void handle(final CallCompositeErrorEvent eventArgs) { 51 | if (eventArgs.getErrorCode().equals(CallCompositeErrorCode.CALL_JOIN_FAILED)) { 52 | errorInfoBar.displayErrorInfoBar( 53 | activity.getWindow().getDecorView().findViewById(android.R.id.content), 54 | SampleErrorMessages.CALL_COMPOSITE_JOIN_CALL_FAILED); 55 | } else if (eventArgs.getErrorCode().equals(CallCompositeErrorCode.CALL_END_FAILED)) { 56 | errorInfoBar.displayErrorInfoBar( 57 | activity.getWindow().getDecorView().findViewById(android.R.id.content), 58 | SampleErrorMessages.CALL_COMPOSITE_END_CALL_FAILED); 59 | } else if (eventArgs.getErrorCode().equals(CallCompositeErrorCode.TOKEN_EXPIRED)) { 60 | errorInfoBar.displayErrorInfoBar( 61 | activity.getWindow().getDecorView().findViewById(android.R.id.content), 62 | SampleErrorMessages.CALL_COMPOSITE_TOKEN_EXPIRED); 63 | } 64 | } 65 | }; 66 | 67 | @Override 68 | public boolean onOptionsItemSelected(final MenuItem item) { 69 | switch (item.getItemId()) { 70 | case android.R.id.home: 71 | NavUtils.navigateUpFromSameTask(this); 72 | return true; 73 | default: 74 | return super.onOptionsItemSelected(item); 75 | } 76 | } 77 | 78 | @Override 79 | protected void onCreate(final Bundle savedInstanceState) { 80 | super.onCreate(savedInstanceState); 81 | setContentView(R.layout.activity_invitation); 82 | activity = this; 83 | appSettings = ((AzureCalling) getApplication()).getAppSettings(); 84 | final ActionBar ab = getSupportActionBar(); 85 | // Disable the Up button 86 | if (ab != null) { 87 | ab.setDisplayHomeAsUpEnabled(true); 88 | ab.setTitle(INVITE_ANOTHER_DEVICE); 89 | } 90 | 91 | sharedPreferences = this.getSharedPreferences(Constants.ACS_SHARED_PREF, Context.MODE_PRIVATE); 92 | errorInfoBar = new ErrorInfoBar(); 93 | startCallSetup(); 94 | initializeUI(); 95 | } 96 | 97 | private void initializeUI() { 98 | startCallButton = findViewById(R.id.start_call_continue_button); 99 | startCallButton.setOnClickListener(l -> makeCall()); 100 | 101 | shareCallButton = findViewById(R.id.share_button); 102 | shareCallButton.setOnClickListener(l -> openShareDialogue()); 103 | } 104 | 105 | private void startCallSetup() { 106 | ((AzureCalling) getApplicationContext()).createCallingContext(); 107 | callingContext = ((AzureCalling) getApplicationContext()).getCallingContext(); 108 | 109 | options = ((AzureCalling) getApplicationContext()) 110 | .getCallingContext() 111 | .getCallCompositeRemoteOptions(appSettings.getUserProfile().getDisplayName()); 112 | } 113 | 114 | private void openShareDialogue() { 115 | final Intent sendIntent = new Intent(); 116 | sendIntent.setAction(Intent.ACTION_SEND); 117 | sendIntent.putExtra(Intent.EXTRA_TEXT, callingContext.getJoinId()); 118 | sendIntent.putExtra(Intent.EXTRA_TITLE, "Group Call ID"); 119 | sendIntent.setType("text/plain"); 120 | final Intent shareIntent = Intent.createChooser(sendIntent, null); 121 | shareIntent.setFlags(FLAG_ACTIVITY_SINGLE_TOP); 122 | startActivity(shareIntent); 123 | } 124 | 125 | private void makeCall() { 126 | final CallComposite composite = new CallCompositeBuilder() 127 | .multitasking(new CallCompositeMultitaskingOptions(true, true)) 128 | .build(); 129 | composite.addOnErrorEventHandler(callCompositeEventHandler); 130 | composite.launch(this, options); 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/activities/JoinCallActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.activities; 5 | 6 | import static com.azure.samples.communication.calling.contracts.Constants.JOIN_CALL; 7 | 8 | import androidx.appcompat.app.ActionBar; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.core.app.NavUtils; 11 | import androidx.core.content.ContextCompat; 12 | import androidx.core.view.ViewCompat; 13 | import androidx.fragment.app.Fragment; 14 | import androidx.fragment.app.FragmentTransaction; 15 | 16 | import android.content.Context; 17 | import android.graphics.drawable.StateListDrawable; 18 | import android.os.Bundle; 19 | import android.view.MenuItem; 20 | import android.view.View; 21 | import android.view.ViewGroup; 22 | 23 | import com.azure.samples.communication.calling.R; 24 | import com.azure.samples.communication.calling.views.fragments.TeamsMeetingFragment; 25 | import com.azure.samples.communication.calling.views.fragments.GroupMeetingFragment; 26 | import com.google.android.material.tabs.TabLayout; 27 | 28 | public class JoinCallActivity extends AppCompatActivity { 29 | 30 | private TabLayout meetingTabLayout; 31 | private Context context; 32 | private View groupMeetingTab; 33 | private View teamsMeetingTab; 34 | 35 | @Override 36 | public boolean onOptionsItemSelected(final MenuItem item) { 37 | switch (item.getItemId()) { 38 | case android.R.id.home: 39 | NavUtils.navigateUpFromSameTask(this); 40 | return true; 41 | default: 42 | return super.onOptionsItemSelected(item); 43 | } 44 | } 45 | 46 | @Override 47 | protected void onCreate(final Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | setContentView(R.layout.activity_join_call); 50 | 51 | final ActionBar ab = getSupportActionBar(); 52 | // Disable the Up button 53 | if (ab != null) { 54 | ab.setDisplayHomeAsUpEnabled(true); 55 | ab.setTitle(JOIN_CALL); 56 | } 57 | 58 | initializeUI(); 59 | if (savedInstanceState == null) { 60 | showGroupFragment(); 61 | } 62 | } 63 | 64 | private void initializeUI() { 65 | context = this; 66 | meetingTabLayout = findViewById(R.id.meeting_tab_layout); 67 | 68 | groupMeetingTab = ((ViewGroup) meetingTabLayout.getChildAt(0)).getChildAt(0); 69 | teamsMeetingTab = ((ViewGroup) meetingTabLayout.getChildAt(0)).getChildAt(1); 70 | 71 | meetingTabLayout.addOnTabSelectedListener(new TabLayout.OnTabSelectedListener() { 72 | @Override 73 | public void onTabSelected(final TabLayout.Tab tab) { 74 | 75 | if (tab.getText().equals("Group Meeting")) { 76 | showGroupFragment(); 77 | } else { 78 | showTeamsFragment(); 79 | } 80 | } 81 | 82 | @Override 83 | public void onTabUnselected(final TabLayout.Tab tab) { 84 | 85 | } 86 | 87 | @Override 88 | public void onTabReselected(final TabLayout.Tab tab) { 89 | } 90 | }); 91 | } 92 | 93 | private void showTeamsFragment() { 94 | teamsMeetingTab.requestLayout(); 95 | ViewCompat.setBackground(teamsMeetingTab, setImageButtonStateNew(context)); 96 | lauchFragment(TeamsMeetingFragment.class.getName()); 97 | } 98 | 99 | private void showGroupFragment() { 100 | groupMeetingTab.requestLayout(); 101 | ViewCompat.setBackground(groupMeetingTab, setImageButtonStateNew(context)); 102 | lauchFragment(GroupMeetingFragment.class.getName()); 103 | } 104 | 105 | private void lauchFragment(final String fragmentClassName) { 106 | final Fragment fragment = getSupportFragmentManager().getFragmentFactory().instantiate( 107 | getClassLoader(), 108 | fragmentClassName 109 | ); 110 | 111 | final FragmentTransaction transaction = getSupportFragmentManager().beginTransaction(); 112 | 113 | transaction.replace(R.id.calling_fragment_container_view, fragment); 114 | transaction.commit(); 115 | } 116 | 117 | private StateListDrawable setImageButtonStateNew(final Context mContext) { 118 | final StateListDrawable states = new StateListDrawable(); 119 | states.addState(new int[] {android.R.attr.state_selected}, 120 | ContextCompat.getDrawable(mContext, R.drawable.tab_background_normal_blue)); 121 | states.addState(new int[] {-android.R.attr.state_selected}, 122 | ContextCompat.getDrawable(mContext, R.drawable.tab_background_normal)); 123 | 124 | return states; 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/activities/SignInActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.activities; 5 | 6 | import static com.azure.samples.communication.calling.contracts.Constants.IS_LOGGED_IN; 7 | 8 | import androidx.appcompat.app.AppCompatActivity; 9 | import android.content.Context; 10 | import android.content.Intent; 11 | import android.content.SharedPreferences; 12 | import android.os.Bundle; 13 | import android.view.View; 14 | import com.azure.samples.communication.calling.AzureCalling; 15 | import com.azure.samples.communication.calling.R; 16 | import com.azure.samples.communication.calling.contracts.Constants; 17 | import com.azure.samples.communication.calling.externals.authentication.AADAuthHandler; 18 | import com.azure.samples.communication.calling.externals.authentication.UserProfile; 19 | import com.azure.samples.communication.calling.utilities.AppSettings; 20 | import com.microsoft.fluentui.widget.Button; 21 | 22 | public class SignInActivity extends AppCompatActivity { 23 | 24 | private static final String LOG_TAG = SignInActivity.class.getSimpleName(); 25 | 26 | private Button signInButton; 27 | private AADAuthHandler authHandler; 28 | private AppSettings appSettings; 29 | 30 | private SharedPreferences sharedPreferences; 31 | private SharedPreferences.Editor editor; 32 | 33 | @Override 34 | protected void onCreate(final Bundle savedInstanceState) { 35 | super.onCreate(savedInstanceState); 36 | setContentView(R.layout.activity_signin); 37 | 38 | initializeAuth(); 39 | initializeUI(); 40 | } 41 | 42 | @Override 43 | protected void onResume() { 44 | super.onResume(); 45 | 46 | if (!appSettings.isAADAuthEnabled()) { 47 | navigateToIntroView(); 48 | } 49 | } 50 | 51 | @Override 52 | public void onBackPressed() { 53 | finishAffinity(); 54 | } 55 | 56 | private void initializeUI() { 57 | signInButton = findViewById(R.id.sign_in_button); 58 | signInButton.setOnClickListener(l -> onClickSignInButton()); 59 | 60 | if (getSupportActionBar() != null) { 61 | getSupportActionBar().hide(); 62 | } 63 | } 64 | 65 | private void initializeAuth() { 66 | sharedPreferences = this.getSharedPreferences(Constants.ACS_SHARED_PREF, Context.MODE_PRIVATE); 67 | editor = sharedPreferences.edit(); 68 | 69 | authHandler = ((AzureCalling) getApplication()).getAadAuthHandler(); 70 | appSettings = ((AzureCalling) getApplication()).getAppSettings(); 71 | } 72 | 73 | private void toggleProgress(final boolean show) { 74 | final View progressOverlay = findViewById(R.id.overlay_loading); 75 | if (show) { 76 | progressOverlay.setVisibility(View.VISIBLE); 77 | signInButton.setText(R.string.signing_in); 78 | signInButton.setEnabled(false); 79 | } else { 80 | progressOverlay.setVisibility(View.GONE); 81 | signInButton.setText(R.string.sign_in); 82 | signInButton.setEnabled(true); 83 | } 84 | } 85 | 86 | private void navigateToIntroView() { 87 | final Intent intent = new Intent(this, IntroViewActivity.class); 88 | intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP); 89 | toggleProgress(false); 90 | startActivity(intent); 91 | } 92 | 93 | private void cacheSignIn() { 94 | editor.putBoolean(IS_LOGGED_IN, true); 95 | editor.apply(); 96 | } 97 | 98 | private void onClickSignInButton() { 99 | if (appSettings.isAADAuthEnabled()) { 100 | toggleProgress(true); 101 | 102 | authHandler.loadAccount(this, false, (object) -> { 103 | if (object instanceof Boolean) { 104 | authHandler.signIn(this, (profile) -> { 105 | if (profile instanceof UserProfile) { 106 | cacheSignIn(); 107 | navigateToIntroView(); 108 | } 109 | }); 110 | } 111 | if (object instanceof UserProfile) { 112 | cacheSignIn(); 113 | navigateToIntroView(); 114 | } 115 | }); 116 | } 117 | } 118 | } 119 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/activities/StartCallActivity.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.activities; 5 | 6 | import static com.azure.samples.communication.calling.contracts.Constants.START_CALL; 7 | 8 | import androidx.appcompat.app.ActionBar; 9 | import androidx.appcompat.app.AppCompatActivity; 10 | import androidx.core.app.NavUtils; 11 | 12 | import android.content.Intent; 13 | import android.content.SharedPreferences; 14 | import android.os.Bundle; 15 | import android.text.TextUtils; 16 | import android.view.MenuItem; 17 | import android.widget.EditText; 18 | 19 | import com.azure.samples.communication.calling.AzureCalling; 20 | import com.azure.samples.communication.calling.R; 21 | import com.azure.samples.communication.calling.contracts.Constants; 22 | import com.azure.samples.communication.calling.contracts.SampleErrorMessages; 23 | import com.azure.samples.communication.calling.utilities.AppSettings; 24 | import com.azure.samples.communication.calling.views.components.ErrorInfoBar; 25 | import com.microsoft.fluentui.widget.Button; 26 | 27 | public class StartCallActivity extends AppCompatActivity { 28 | 29 | private EditText startCallDisplayName; 30 | private Button startCallNextButton; 31 | private AppSettings appSettings; 32 | private SharedPreferences sharedPreferences; 33 | private SharedPreferences.Editor editor; 34 | 35 | @Override 36 | public boolean onOptionsItemSelected(final MenuItem item) { 37 | switch (item.getItemId()) { 38 | case android.R.id.home: 39 | NavUtils.navigateUpFromSameTask(this); 40 | return true; 41 | default: 42 | return super.onOptionsItemSelected(item); 43 | } 44 | } 45 | 46 | @Override 47 | protected void onCreate(final Bundle savedInstanceState) { 48 | super.onCreate(savedInstanceState); 49 | setContentView(R.layout.activity_start_call); 50 | appSettings = ((AzureCalling) getApplication()).getAppSettings(); 51 | final ActionBar ab = getSupportActionBar(); 52 | // Disable the Up button 53 | if (ab != null) { 54 | ab.setDisplayHomeAsUpEnabled(true); 55 | ab.setTitle(START_CALL); 56 | } 57 | 58 | initializeUI(); 59 | } 60 | 61 | private void initializeUI() { 62 | sharedPreferences = getSharedPreferences(Constants.ACS_SHARED_PREF, MODE_PRIVATE); 63 | editor = sharedPreferences.edit(); 64 | 65 | startCallDisplayName = findViewById(R.id.start_call_display_name); 66 | startCallNextButton = findViewById(R.id.start_call_next_button); 67 | startCallNextButton.setOnClickListener(l -> goToMeetingInvitePage()); 68 | 69 | final String savedDisplayName = appSettings.getUserProfile().getDisplayName(); 70 | if (savedDisplayName.length() > 0) { 71 | startCallDisplayName.setText(savedDisplayName); 72 | } 73 | } 74 | 75 | private void goToMeetingInvitePage() { 76 | final String displayName = startCallDisplayName.getText().toString(); 77 | appSettings.getUserProfile().setDisplayName(displayName); 78 | if (appSettings.getUserProfile().getUsername().isEmpty()) { 79 | appSettings.getUserProfile().setUsername(displayName); 80 | } 81 | if (TextUtils.isEmpty(displayName)) { 82 | new ErrorInfoBar().displayErrorInfoBar( 83 | this.getWindow().getDecorView().findViewById(android.R.id.content), 84 | SampleErrorMessages.DISPLAY_NAME_REQUIRED); 85 | return; 86 | } 87 | 88 | final Intent intent = new Intent(this, InvitationActivity.class); 89 | startActivity(intent); 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/components/ErrorInfoBar.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.components; 5 | 6 | import android.util.DisplayMetrics; 7 | import android.util.TypedValue; 8 | import android.view.View; 9 | import android.view.ViewGroup; 10 | import androidx.core.content.ContextCompat; 11 | import com.azure.samples.communication.calling.R; 12 | import com.microsoft.fluentui.snackbar.Snackbar; 13 | 14 | public class ErrorInfoBar { 15 | 16 | private Snackbar snackbar; 17 | 18 | public void displayErrorInfoBar(final View rootView, final String errorMessage) { 19 | if (errorMessage == null || errorMessage.isEmpty()) { 20 | return; 21 | } 22 | snackbar = Snackbar.Companion.make(rootView, "", Snackbar.LENGTH_LONG, Snackbar.Style.REGULAR); 23 | snackbar.getView() 24 | .setBackground(ContextCompat.getDrawable(rootView.getContext(), R.drawable.snackbar_background)); 25 | final ViewGroup.MarginLayoutParams params = (ViewGroup.MarginLayoutParams) snackbar.getView().getLayoutParams(); 26 | final DisplayMetrics metrics = rootView.getResources().getDisplayMetrics(); 27 | 28 | params.setMargins(params.leftMargin + getDp(16, metrics), 29 | params.topMargin, 30 | params.rightMargin + getDp(16, metrics), 31 | params.bottomMargin + getDp(84, metrics)); 32 | 33 | snackbar.getView().setLayoutParams(params); 34 | snackbar.setAnimationMode(Snackbar.ANIMATION_MODE_FADE); 35 | snackbar.setText((CharSequence) errorMessage); 36 | snackbar.setTextColor(ContextCompat.getColor(rootView.getContext(), R.color.snackbar_text_color)); 37 | snackbar.show(); 38 | } 39 | 40 | public void dismissErrorInfoBar() { 41 | if (snackbar != null && snackbar.isShown()) { 42 | snackbar.dismiss(); 43 | } 44 | } 45 | 46 | private int getDp(final int margin, final DisplayMetrics metrics) { 47 | return (int) TypedValue.applyDimension( 48 | TypedValue.COMPLEX_UNIT_DIP, 49 | margin, 50 | metrics 51 | ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/fragments/AbstractBaseFragment.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.fragments; 5 | 6 | import android.content.Context; 7 | import android.content.SharedPreferences; 8 | import androidx.fragment.app.Fragment; 9 | import com.azure.android.communication.ui.calling.CallCompositeEventHandler; 10 | import com.azure.android.communication.ui.calling.models.CallCompositeErrorCode; 11 | import com.azure.android.communication.ui.calling.models.CallCompositeErrorEvent; 12 | import com.azure.samples.communication.calling.contracts.Constants; 13 | import com.azure.samples.communication.calling.contracts.SampleErrorMessages; 14 | import com.azure.samples.communication.calling.views.components.ErrorInfoBar; 15 | 16 | public abstract class AbstractBaseFragment extends Fragment { 17 | protected CallCompositeEventHandler callCompositeEventHandler = 18 | new CallCompositeEventHandler() { 19 | @Override 20 | public void handle(final CallCompositeErrorEvent eventArgs) { 21 | if (eventArgs.getErrorCode().equals(CallCompositeErrorCode.CALL_JOIN_FAILED)) { 22 | showError(SampleErrorMessages.CALL_COMPOSITE_JOIN_CALL_FAILED); 23 | } else if (eventArgs.getErrorCode().equals(CallCompositeErrorCode.CALL_END_FAILED)) { 24 | showError(SampleErrorMessages.CALL_COMPOSITE_END_CALL_FAILED); 25 | } else if (eventArgs.getErrorCode().equals(CallCompositeErrorCode.TOKEN_EXPIRED)) { 26 | showError(SampleErrorMessages.CALL_COMPOSITE_TOKEN_EXPIRED); 27 | } 28 | } 29 | }; 30 | protected SharedPreferences getSharedPreferences() { 31 | return requireActivity().getSharedPreferences(Constants.ACS_SHARED_PREF, Context.MODE_PRIVATE); 32 | } 33 | protected void showError(final String error) { 34 | new ErrorInfoBar().displayErrorInfoBar(this.getView(), error); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/fragments/GroupMeetingFragment.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.fragments; 5 | 6 | import android.os.Bundle; 7 | import android.text.TextUtils; 8 | import android.view.LayoutInflater; 9 | import android.view.View; 10 | import android.view.ViewGroup; 11 | import android.widget.EditText; 12 | import com.azure.android.communication.ui.calling.CallComposite; 13 | import com.azure.android.communication.ui.calling.CallCompositeBuilder; 14 | import com.azure.android.communication.ui.calling.models.CallCompositeMultitaskingOptions; 15 | import com.azure.android.communication.ui.calling.models.CallCompositeRemoteOptions; 16 | import com.azure.samples.communication.calling.AzureCalling; 17 | import com.azure.samples.communication.calling.R; 18 | import com.azure.samples.communication.calling.contracts.Constants; 19 | import com.azure.samples.communication.calling.contracts.SampleErrorMessages; 20 | import com.azure.samples.communication.calling.externals.calling.CallingContext; 21 | import com.azure.samples.communication.calling.utilities.AppSettings; 22 | import com.microsoft.fluentui.widget.Button; 23 | 24 | import java.util.UUID; 25 | 26 | /** 27 | * Fragment to host group call meetings 28 | */ 29 | public class GroupMeetingFragment extends AbstractBaseFragment { 30 | 31 | private EditText groupMeetingID; 32 | private EditText displayNameEditor; 33 | private AppSettings appSettings; 34 | 35 | public GroupMeetingFragment() { 36 | } 37 | 38 | @Override 39 | public View onCreateView(final LayoutInflater inflater, final ViewGroup container, 40 | final Bundle savedInstanceState) { 41 | appSettings = ((AzureCalling) requireActivity().getApplicationContext()).getAppSettings(); 42 | final View inflatedView = inflater.inflate(R.layout.fragment_group_meeting, container, false); 43 | final Button joinCallButton = inflatedView.findViewById(R.id.group_call_join_next); 44 | joinCallButton.setOnClickListener(l -> joinCall()); 45 | 46 | displayNameEditor = inflatedView.findViewById(R.id.group_call_display_name); 47 | final String savedDisplayName = appSettings.getUserProfile().getDisplayName(); 48 | if (!TextUtils.isEmpty(savedDisplayName)) { 49 | displayNameEditor.setText(savedDisplayName); 50 | } 51 | groupMeetingID = inflatedView.findViewById(R.id.group_call_id); 52 | final String savedGroupCallID = getSharedPreferences().getString(Constants.ACS_GROUPCALL_ID, ""); 53 | if (!TextUtils.isEmpty(savedGroupCallID)) { 54 | groupMeetingID.setText(savedGroupCallID); 55 | } 56 | return inflatedView; 57 | } 58 | 59 | private void joinCall() { 60 | final String displayName = displayNameEditor.getText().toString(); 61 | final String groupCallId = groupMeetingID.getText().toString().trim(); 62 | groupMeetingID.setText(groupCallId); 63 | appSettings.getUserProfile().setDisplayName(displayName); 64 | 65 | if (TextUtils.isEmpty(groupCallId)) { 66 | showError(SampleErrorMessages.GROUP_ID_REQUIRED); 67 | return; 68 | } 69 | if (!isUUID(groupCallId)) { 70 | showError(SampleErrorMessages.GROUP_ID_INVALID); 71 | return; 72 | } 73 | 74 | if (TextUtils.isEmpty(displayName)) { 75 | showError(SampleErrorMessages.DISPLAY_NAME_REQUIRED); 76 | return; 77 | } 78 | getSharedPreferences() 79 | .edit() 80 | .putString(Constants.ACS_GROUPCALL_ID, groupCallId) 81 | .apply(); 82 | 83 | if (appSettings.getUserProfile().getUsername().isEmpty()) { 84 | appSettings.getUserProfile().setUsername(displayName); 85 | } 86 | 87 | final CallComposite composite = new CallCompositeBuilder() 88 | .multitasking(new CallCompositeMultitaskingOptions(true, true)) 89 | .build(); 90 | 91 | final AzureCalling calling = (AzureCalling) requireActivity().getApplicationContext(); 92 | calling.createCallingContext(); 93 | final CallingContext callingContext = calling.getCallingContext(); 94 | 95 | composite.addOnErrorEventHandler(callCompositeEventHandler); 96 | final CallCompositeRemoteOptions remoteOptions = callingContext 97 | .getCallCompositeGroupRemoteOptions(displayName, UUID.fromString(groupCallId)); 98 | composite.launch(requireActivity(), remoteOptions); 99 | } 100 | 101 | private boolean isUUID(final String groupCallID) { 102 | try { 103 | UUID.fromString(groupCallID); 104 | return true; 105 | } catch (Exception ex) { 106 | return false; 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/java/com/azure/samples/communication/calling/views/fragments/TeamsMeetingFragment.java: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | package com.azure.samples.communication.calling.views.fragments; 5 | 6 | import android.os.Bundle; 7 | import android.text.TextUtils; 8 | import android.util.Patterns; 9 | import android.view.LayoutInflater; 10 | import android.view.View; 11 | import android.view.ViewGroup; 12 | import android.webkit.URLUtil; 13 | import android.widget.EditText; 14 | import com.azure.android.communication.ui.calling.CallComposite; 15 | import com.azure.android.communication.ui.calling.CallCompositeBuilder; 16 | import com.azure.android.communication.ui.calling.models.CallCompositeMultitaskingOptions; 17 | import com.azure.android.communication.ui.calling.models.CallCompositeRemoteOptions; 18 | import com.azure.samples.communication.calling.AzureCalling; 19 | import com.azure.samples.communication.calling.R; 20 | import com.azure.samples.communication.calling.contracts.Constants; 21 | import com.azure.samples.communication.calling.contracts.SampleErrorMessages; 22 | import com.azure.samples.communication.calling.externals.calling.CallingContext; 23 | import com.azure.samples.communication.calling.utilities.AppSettings; 24 | import com.microsoft.fluentui.widget.Button; 25 | 26 | /** 27 | * Fragment to host teams call meetings 28 | */ 29 | public class TeamsMeetingFragment extends AbstractBaseFragment { 30 | 31 | private EditText teamsDisplayNameEditor; 32 | private EditText teamsMeetingLink; 33 | private Button teamsJoinMeetingButton; 34 | private AppSettings appSettings; 35 | 36 | public TeamsMeetingFragment() { 37 | } 38 | 39 | @Override 40 | public View onCreateView(final LayoutInflater inflater, final ViewGroup container, 41 | final Bundle savedInstanceState) { 42 | appSettings = ((AzureCalling) requireActivity().getApplicationContext()).getAppSettings(); 43 | final View inflatedView = inflater.inflate(R.layout.fragment_teams_meeting, container, 44 | false); 45 | teamsJoinMeetingButton = inflatedView.findViewById(R.id.teams_call_join_next); 46 | teamsJoinMeetingButton.setOnClickListener(l -> joinTeamsCall()); 47 | 48 | teamsDisplayNameEditor = inflatedView.findViewById(R.id.teams_call_display_name); 49 | 50 | final String savedDisplayName = appSettings.getUserProfile().getDisplayName(); 51 | if (!TextUtils.isEmpty(savedDisplayName)) { 52 | teamsDisplayNameEditor.setText(savedDisplayName); 53 | } 54 | teamsMeetingLink = inflatedView.findViewById(R.id.teams_call_link); 55 | final String savedMeetingLink = getSharedPreferences().getString(Constants.ACS_MEETING_LINK, ""); 56 | if (!TextUtils.isEmpty(savedMeetingLink)) { 57 | teamsMeetingLink.setText(savedMeetingLink); 58 | } 59 | 60 | return inflatedView; 61 | } 62 | 63 | private void joinTeamsCall() { 64 | final String displayName = teamsDisplayNameEditor.getText().toString(); 65 | final String teamsLink = teamsMeetingLink.getText().toString().trim(); 66 | teamsMeetingLink.setText(teamsLink); 67 | appSettings.getUserProfile().setDisplayName(displayName); 68 | if (TextUtils.isEmpty(teamsLink)) { 69 | showError(SampleErrorMessages.TEAMS_LINK_REQUIRED); 70 | return; 71 | } 72 | 73 | if (!isValidWebURL(teamsLink)) { 74 | showError(SampleErrorMessages.TEAMS_LINK_INVALID); 75 | return; 76 | } 77 | 78 | if (TextUtils.isEmpty(displayName)) { 79 | showError(SampleErrorMessages.DISPLAY_NAME_REQUIRED); 80 | return; 81 | } 82 | 83 | getSharedPreferences() 84 | .edit() 85 | .putString(Constants.ACS_MEETING_LINK, teamsLink) 86 | .apply(); 87 | 88 | if (appSettings.getUserProfile().getUsername().isEmpty()) { 89 | appSettings.getUserProfile().setUsername(displayName); 90 | } 91 | final CallComposite composite = new CallCompositeBuilder() 92 | .multitasking(new CallCompositeMultitaskingOptions(true, true)) 93 | .build(); 94 | final AzureCalling calling = (AzureCalling) requireActivity().getApplicationContext(); 95 | calling.createCallingContext(); 96 | final CallingContext callingContext = calling.getCallingContext(); 97 | final CallCompositeRemoteOptions remoteOptions = callingContext 98 | .getCallCompositeRemoteOptions(displayName, teamsLink); 99 | composite.addOnErrorEventHandler(callCompositeEventHandler); 100 | composite.launch(requireActivity(), remoteOptions); 101 | } 102 | 103 | private Boolean isValidWebURL(final String url) { 104 | return URLUtil.isValidUrl(url) && Patterns.WEB_URL.matcher(url).matches(); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color-night/button_filled.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color-night/default_icon_tint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color-night/selector_button_filled_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color-night/selector_button_outlined.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color-night/selector_button_outlined_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color/button_filled.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color/default_icon_tint.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color/selector_button_filled_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color/selector_button_outlined.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color/selector_button_outlined_text.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color/selector_primary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/color/selector_secondary.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable-v24/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | 15 | 18 | 21 | 22 | 23 | 24 | 30 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/bordered_button_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/button_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/button_filled.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/ic_acs_vector.xml: -------------------------------------------------------------------------------- 1 | 7 | 9 | 10 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/ic_display_name.xml: -------------------------------------------------------------------------------- 1 | 6 | 9 | 10 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/ic_launcher_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 10 | 12 | 14 | 16 | 18 | 20 | 22 | 24 | 26 | 28 | 30 | 32 | 34 | 36 | 38 | 40 | 42 | 44 | 46 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 62 | 64 | 66 | 68 | 70 | 72 | 74 | 75 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/ic_shape.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/ic_teams_link.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/layout_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/rounded_corners.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 9 | 10 | 11 | 12 | 13 | 15 | 16 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/selected_tab_left.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/snackbar_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/tab_background_normal.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/tab_background_normal_blue.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/drawable/tabview_background.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/activity_intro_view.xml: -------------------------------------------------------------------------------- 1 | 5 | 12 | 13 | 20 | 21 | 26 | 27 | 29 | 30 | 31 | 32 | 38 | 39 | 49 | 50 | 61 | 62 | 63 | 64 | 65 | 66 | 72 | 85 | 86 | 87 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/activity_invitation.xml: -------------------------------------------------------------------------------- 1 | 5 | 14 | 15 | 21 | 22 | 27 | 28 | 37 | 38 | 46 | 47 | 48 | 49 | 50 | 55 | 62 | 63 | 64 | 70 | 71 | 82 | 83 | 84 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/activity_join_call.xml: -------------------------------------------------------------------------------- 1 | 5 | 12 | 13 | 17 | 18 | 22 | 23 | 35 | 36 | 41 | 42 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 57 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/activity_signin.xml: -------------------------------------------------------------------------------- 1 | 5 | 12 | 17 | 18 | 23 | 24 | 25 | 26 | 27 | 28 | 33 | 34 | 45 | 46 | 47 | 48 | 54 | 67 | 68 | 69 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/activity_start_call.xml: -------------------------------------------------------------------------------- 1 | 5 | 14 | 15 | 25 | 26 | 31 | 32 | 40 | 41 | 55 | 56 | 57 | 64 | 65 | 71 | 72 | 82 | 83 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/fragment_group_meeting.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | 16 | 17 | 26 | 27 | 40 | 41 | 47 | 48 | 57 | 58 | 62 | 63 | 71 | 72 | 85 | 86 | 87 | 94 | 95 | 99 | 100 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/fragment_teams_meeting.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | 16 | 17 | 26 | 27 | 31 | 32 | 41 | 42 | 55 | 56 | 57 | 63 | 64 | 73 | 74 | 78 | 79 | 87 | 88 | 101 | 102 | 103 | 109 | 110 | 114 | 115 | 125 | 126 | 127 | 128 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/landing_layout.xml: -------------------------------------------------------------------------------- 1 | 5 | 9 | 10 | 19 | 20 | 21 | 29 | 30 | 38 | 39 | 44 | 45 | 52 | 53 | 61 | 62 | 63 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/layout/view_intro_actionbar.xml: -------------------------------------------------------------------------------- 1 | 5 | 11 | 12 | 22 | 23 | 36 | 37 | 48 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-hdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-hdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-mdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-mdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/raw/auth_config_single_account.json: -------------------------------------------------------------------------------- 1 | { 2 | "client_id": "client-id", 3 | "authorization_user_agent": "DEFAULT", 4 | "redirect_uri": "msauth://package-name/sha1-hash", 5 | "account_mode" : "SINGLE", 6 | "authorities": [ 7 | { 8 | "type": "AAD", 9 | "audience": { 10 | "type": "AzureADMyOrg", 11 | "tenant_id": "tenant-id" 12 | } 13 | } 14 | ] 15 | } -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/values-night/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | 11 | @android:color/black 12 | #000000 13 | @color/grey700 14 | #3AA0F3 15 | #2B88D8 16 | @color/fluentui_white 17 | @color/fluentui_gray_25 18 | @color/fluentui_gray_25 19 | @color/grey300 20 | @android:color/white 21 | @color/grey900 22 | #F9D9D9 23 | #A52121 24 | #000000 25 | #141414 26 | 27 | @android:color/black 28 | @android:color/white 29 | #E1E1E1 30 | 31 | @android:color/white 32 | @android:color/black 33 | 34 | #FF141414 35 | #212121 36 | #FF303030 37 | #FF6E6E6E 38 | #FF919191 39 | #FFACACAC 40 | #D9303030 41 | #FFF1F1F1 42 | #605E5C 43 | 44 | @android:color/black 45 | @android:color/white 46 | 47 | #0063B1 48 | @color/grey700 49 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/values-night/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16 | 17 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 34 | 35 | 36 | 43 | 44 | 51 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/values/colors.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | #FFBB86FC 4 | #FF6200EE 5 | #FF3700B3 6 | #FF03DAC5 7 | #FF018786 8 | #FF000000 9 | #FFFFFFFF 10 | 11 | @android:color/white 12 | @android:color/white 13 | #F1F1F1 14 | #0078D4 15 | #C7E0F4 16 | #212121 17 | @color/grey700 18 | @color/grey500 19 | @color/grey300 20 | @android:color/white 21 | @color/grey900 22 | #F9D9D9 23 | #A52121 24 | #FFFFFF 25 | #F8F8F8 26 | 27 | @android:color/black 28 | @android:color/white 29 | #E1E1E1 30 | 31 | @android:color/white 32 | @android:color/black 33 | 34 | #FF141414 35 | #212121 36 | #FF303030 37 | #FF6E6E6E 38 | #FF919191 39 | #FFACACAC 40 | #D9303030 41 | #FFF1F1F1 42 | #605E5C 43 | 44 | @android:color/black 45 | @android:color/white 46 | 47 | #0063B1 48 | #F1F1F1 49 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | AzureCalling 3 | 4 | Hello blank fragment 5 | Sign out 6 | Sign in 7 | Signing in… 8 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/values/themes.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 17 | 18 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 35 | 36 | 37 | 44 | 45 | 52 | 53 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/xml/backup_rules.xml: -------------------------------------------------------------------------------- 1 | 8 | 9 | 13 | -------------------------------------------------------------------------------- /AzureCalling/app/src/main/res/xml/data_extraction_rules.xml: -------------------------------------------------------------------------------- 1 | 6 | 7 | 8 | 12 | 13 | 19 | -------------------------------------------------------------------------------- /AzureCalling/build.gradle: -------------------------------------------------------------------------------- 1 | // Top-level build file where you can add configuration options common to all sub-projects/modules. 2 | plugins { 3 | id 'com.android.application' version '7.2.1' apply false 4 | id 'com.android.library' version '7.2.1' apply false 5 | } 6 | 7 | task clean(type: Delete) { 8 | delete rootProject.buildDir 9 | } 10 | 11 | subprojects { 12 | apply from: "checkstyle.gradle" 13 | afterEvaluate { 14 | preBuild.dependsOn 'checkstyle' 15 | check.dependsOn 'checkstyle' 16 | } 17 | } -------------------------------------------------------------------------------- /AzureCalling/gradle.properties: -------------------------------------------------------------------------------- 1 | # Project-wide Gradle settings. 2 | # IDE (e.g. Android Studio) users: 3 | # Gradle settings configured through the IDE *will override* 4 | # any settings specified in this file. 5 | # For more details on how to configure your build environment visit 6 | # http://www.gradle.org/docs/current/userguide/build_environment.html 7 | # Specifies the JVM arguments used for the daemon process. 8 | # The setting is particularly useful for tweaking memory settings. 9 | org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8 10 | # When configured, Gradle will run in incubating parallel mode. 11 | # This option should only be used with decoupled projects. More details, visit 12 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects 13 | # org.gradle.parallel=true 14 | # AndroidX package structure to make it clearer which packages are bundled with the 15 | # Android operating system, and which are packaged with your app"s APK 16 | # https://developer.android.com/topic/libraries/support-library/androidx-rn 17 | android.useAndroidX=true 18 | # Enables namespacing of each library's R class so that its R class includes only the 19 | # resources declared in the library itself and none from the library's dependencies, 20 | # thereby reducing the size of the R class for that library 21 | android.nonTransitiveRClass=true -------------------------------------------------------------------------------- /AzureCalling/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/AzureCalling/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /AzureCalling/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Tue Jun 28 16:21:08 PDT 2022 2 | distributionBase=GRADLE_USER_HOME 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-bin.zip 4 | distributionPath=wrapper/dists 5 | zipStorePath=wrapper/dists 6 | zipStoreBase=GRADLE_USER_HOME 7 | -------------------------------------------------------------------------------- /AzureCalling/gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env sh 2 | 3 | # 4 | # Copyright 2015 the original author or authors. 5 | # 6 | # Licensed under the Apache License, Version 2.0 (the "License"); 7 | # you may not use this file except in compliance with the License. 8 | # You may obtain a copy of the License at 9 | # 10 | # https://www.apache.org/licenses/LICENSE-2.0 11 | # 12 | # Unless required by applicable law or agreed to in writing, software 13 | # distributed under the License is distributed on an "AS IS" BASIS, 14 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | # See the License for the specific language governing permissions and 16 | # limitations under the License. 17 | # 18 | 19 | ############################################################################## 20 | ## 21 | ## Gradle start up script for UN*X 22 | ## 23 | ############################################################################## 24 | 25 | # Attempt to set APP_HOME 26 | # Resolve links: $0 may be a link 27 | PRG="$0" 28 | # Need this for relative symlinks. 29 | while [ -h "$PRG" ] ; do 30 | ls=`ls -ld "$PRG"` 31 | link=`expr "$ls" : '.*-> \(.*\)$'` 32 | if expr "$link" : '/.*' > /dev/null; then 33 | PRG="$link" 34 | else 35 | PRG=`dirname "$PRG"`"/$link" 36 | fi 37 | done 38 | SAVED="`pwd`" 39 | cd "`dirname \"$PRG\"`/" >/dev/null 40 | APP_HOME="`pwd -P`" 41 | cd "$SAVED" >/dev/null 42 | 43 | APP_NAME="Gradle" 44 | APP_BASE_NAME=`basename "$0"` 45 | 46 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 47 | DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' 48 | 49 | # Use the maximum available, or set MAX_FD != -1 to use that value. 50 | MAX_FD="maximum" 51 | 52 | warn () { 53 | echo "$*" 54 | } 55 | 56 | die () { 57 | echo 58 | echo "$*" 59 | echo 60 | exit 1 61 | } 62 | 63 | # OS specific support (must be 'true' or 'false'). 64 | cygwin=false 65 | msys=false 66 | darwin=false 67 | nonstop=false 68 | case "`uname`" in 69 | CYGWIN* ) 70 | cygwin=true 71 | ;; 72 | Darwin* ) 73 | darwin=true 74 | ;; 75 | MINGW* ) 76 | msys=true 77 | ;; 78 | NONSTOP* ) 79 | nonstop=true 80 | ;; 81 | esac 82 | 83 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 84 | 85 | 86 | # Determine the Java command to use to start the JVM. 87 | if [ -n "$JAVA_HOME" ] ; then 88 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 89 | # IBM's JDK on AIX uses strange locations for the executables 90 | JAVACMD="$JAVA_HOME/jre/sh/java" 91 | else 92 | JAVACMD="$JAVA_HOME/bin/java" 93 | fi 94 | if [ ! -x "$JAVACMD" ] ; then 95 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 96 | 97 | Please set the JAVA_HOME variable in your environment to match the 98 | location of your Java installation." 99 | fi 100 | else 101 | JAVACMD="java" 102 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 103 | 104 | Please set the JAVA_HOME variable in your environment to match the 105 | location of your Java installation." 106 | fi 107 | 108 | # Increase the maximum file descriptors if we can. 109 | if [ "$cygwin" = "false" -a "$darwin" = "false" -a "$nonstop" = "false" ] ; then 110 | MAX_FD_LIMIT=`ulimit -H -n` 111 | if [ $? -eq 0 ] ; then 112 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 113 | MAX_FD="$MAX_FD_LIMIT" 114 | fi 115 | ulimit -n $MAX_FD 116 | if [ $? -ne 0 ] ; then 117 | warn "Could not set maximum file descriptor limit: $MAX_FD" 118 | fi 119 | else 120 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 121 | fi 122 | fi 123 | 124 | # For Darwin, add options to specify how the application appears in the dock 125 | if $darwin; then 126 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 127 | fi 128 | 129 | # For Cygwin or MSYS, switch paths to Windows format before running java 130 | if [ "$cygwin" = "true" -o "$msys" = "true" ] ; then 131 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 132 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 133 | 134 | JAVACMD=`cygpath --unix "$JAVACMD"` 135 | 136 | # We build the pattern for arguments to be converted via cygpath 137 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 138 | SEP="" 139 | for dir in $ROOTDIRSRAW ; do 140 | ROOTDIRS="$ROOTDIRS$SEP$dir" 141 | SEP="|" 142 | done 143 | OURCYGPATTERN="(^($ROOTDIRS))" 144 | # Add a user-defined pattern to the cygpath arguments 145 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 146 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 147 | fi 148 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 149 | i=0 150 | for arg in "$@" ; do 151 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 152 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 153 | 154 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 155 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 156 | else 157 | eval `echo args$i`="\"$arg\"" 158 | fi 159 | i=`expr $i + 1` 160 | done 161 | case $i in 162 | 0) set -- ;; 163 | 1) set -- "$args0" ;; 164 | 2) set -- "$args0" "$args1" ;; 165 | 3) set -- "$args0" "$args1" "$args2" ;; 166 | 4) set -- "$args0" "$args1" "$args2" "$args3" ;; 167 | 5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 168 | 6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 169 | 7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 170 | 8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 171 | 9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 172 | esac 173 | fi 174 | 175 | # Escape application args 176 | save () { 177 | for i do printf %s\\n "$i" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/' \\\\/" ; done 178 | echo " " 179 | } 180 | APP_ARGS=`save "$@"` 181 | 182 | # Collect all arguments for the java command, following the shell quoting and substitution rules 183 | eval set -- $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS "\"-Dorg.gradle.appname=$APP_BASE_NAME\"" -classpath "\"$CLASSPATH\"" org.gradle.wrapper.GradleWrapperMain "$APP_ARGS" 184 | 185 | exec "$JAVACMD" "$@" 186 | -------------------------------------------------------------------------------- /AzureCalling/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /AzureCalling/settings.gradle: -------------------------------------------------------------------------------- 1 | pluginManagement { 2 | repositories { 3 | gradlePluginPortal() 4 | google() 5 | mavenCentral() 6 | } 7 | } 8 | dependencyResolutionManagement { 9 | repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS) 10 | repositories { 11 | google() 12 | mavenCentral() 13 | maven { 14 | url "https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1" 15 | } 16 | } 17 | } 18 | rootProject.name = "AzureCalling" 19 | include ':app' 20 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Group Calling Sample 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to agree to a 4 | Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us 5 | the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. 6 | 7 | When you submit a pull request, a CLA bot will automatically determine whether you need to provide 8 | a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions 9 | provided by the bot. You will only need to do this once across all repos using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or 13 | contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | - [Code of Conduct](#coc) 16 | - [Issues and Bugs](#issue) 17 | - [Feature Requests](#feature) 18 | - [Submission Guidelines](#submit) 19 | 20 | ## Code of Conduct 21 | Help us keep this project open and inclusive. Please read and follow our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 22 | 23 | ## Found an Issue? 24 | If you find a bug in the source code or a mistake in the documentation, you can help us by 25 | [submitting an issue](#submit-issue) to the GitHub Repository. Even better, you can 26 | [submit a Pull Request](#submit-pr) with a fix. 27 | 28 | ## Want a Feature? 29 | You can *request* a new feature by [submitting an issue](#submit-issue) to the GitHub 30 | Repository. If you would like to *implement* a new feature, please submit an issue with 31 | a proposal for your work first, to be sure that we can use it. 32 | 33 | * **Small Features** can be crafted and directly [submitted as a Pull Request](#submit-pr). 34 | 35 | ## Submission Guidelines 36 | 37 | ### Submitting an Issue 38 | Before you submit an issue, search the archive, maybe your question was already answered. 39 | 40 | If your issue appears to be a bug, and hasn't been reported, open a new issue. 41 | Help us to maximize the effort we can spend fixing issues and adding new 42 | features, by not reporting duplicate issues. Providing the following information will increase the 43 | chances of your issue being dealt with quickly: 44 | 45 | * **Overview of the Issue** - if an error is being thrown a non-minified stack trace helps 46 | * **Version** - what version is affected (e.g. 0.1.2) 47 | * **Motivation for or Use Case** - explain what are you trying to do and why the current behavior is a bug for you 48 | * **Browsers and Operating System** - is this a problem with all browsers? 49 | * **Reproduce the Error** - provide a live example or a unambiguous set of steps 50 | * **Related Issues** - has a similar issue been reported before? 51 | * **Suggest a Fix** - if you can't fix the bug yourself, perhaps you can point to what might be 52 | causing the problem (line of code or commit) 53 | 54 | You can file new issues by providing the above information at the corresponding repository's issues link: https://github.com/[organization-name]/[repository-name]/issues/new]. 55 | 56 | ### Submitting a Pull Request (PR) 57 | Before you submit your Pull Request (PR) consider the following guidelines: 58 | 59 | * Search the repository (https://github.com/[organization-name]/[repository-name]/pulls) for an open or closed PR 60 | that relates to your submission. You don't want to duplicate effort. 61 | 62 | * Make your changes in a new git fork: 63 | 64 | * Commit your changes using a descriptive commit message 65 | * Push your fork to GitHub: 66 | * In GitHub, create a pull request 67 | * If we suggest changes then: 68 | * Make the required updates. 69 | * Rebase your fork and force push to your GitHub repository (this will update your Pull Request): 70 | 71 | ```shell 72 | git rebase master -i 73 | git push -f 74 | ``` 75 | 76 | That's it! Thank you for your contribution! 77 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | --- 2 | page_type: sample 3 | languages: 4 | - java 5 | products: 6 | - azure 7 | - azure-communication-services 8 | - azure-communication-ui-calling 9 | --- 10 | 11 | # Group Calling Sample 12 | 13 | The sample is a native Android application that uses the [Azure Communication Services Android UI client library](https://docs.microsoft.com/en-us/azure/communication-services/quickstarts/ui-library/get-started-composites?tabs=kotlin&pivots=platform-android) to build a calling experience that features both voice and video calling. The application uses a server-side component to provision access tokens that are then used to initialize the Azure Communication Services client library. To configure this server-side component, feel free to follow the [Trusted Service with Azure Functions](https://docs.microsoft.com/azure/communication-services/tutorials/trusted-service-tutorial) tutorial. 14 | 15 | Additional documentation for this sample can be found on [Microsoft Docs](https://docs.microsoft.com/en-us/azure/communication-services/samples/calling-hero-sample?pivots=platform-android). See this sample's wiki to see updated information on [known issues](https://github.com/Azure-Samples/communication-services-android-calling-hero/wiki/Known-Issues) 16 | 17 | ## Features 18 | 19 | - Start a new group call 20 | - Join an existing group call 21 | - Join an existing Teams Meeting (For instructions: [Teams Tenant Interoperability](https://docs.microsoft.com/azure/communication-services/concepts/teams-interop)) 22 | - Render remote participant video streams dynamically 23 | - Switch layout between different call cases: only-local video view, one-on-one call view and group call with multiple participants 24 | - Turning local video stream from camera on/off 25 | - Mute/unmute local microphone audio 26 | - call on hold 27 | 28 | 29 | 30 | 31 | ## Prerequisites 32 | 33 | - An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). 34 | - An OS running [Android Studio](https://developer.android.com/studio). 35 | - A deployed Communication Services resource. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). 36 | - An Authentication Endpoint that will return the Azure Communication Services Token. See [example](https://docs.microsoft.com/azure/communication-services/tutorials/trusted-service-tutorial) 37 | 38 | ## Before running the sample for the first time 39 | 40 | 1. Open Android Studio and select `Open an Existing Project`. 41 | 2. Select folder `AzureCalling`. 42 | 3. Expand app/assets to update `appSettings.properties`. Set the value for the key `communicationTokenFetchUrl` to be the URL for your Authentication Endpoint. 43 | 44 | ## Run Sample 45 | 46 | 1. Build/Run in Android Studio 47 | 48 | ## Securing Authentication Endpoint 49 | 50 | For simple demonstration purposes, this sample uses a publicly accessible endpoint by default to fetch an Azure Communication token. For production scenarios, it is recommended that the Azure Communication token is returned from a secured endpoint. 51 | 52 | With additional configuration, this sample also supports connecting to an **Azure Active Directory** (AAD) protected endpoint so that user login is required for the app to fetch an Azure Communication Services access token. See steps below: 53 | 54 | 1. Enable Azure Active Directory authentication in your app. 55 | - [Register your app under Azure Active Directory (using Android platform settings)](https://docs.microsoft.com/azure/active-directory/develop/tutorial-v2-android) 56 | - [Configure your App Service or Azure Functions app to use Azure AD login](https://docs.microsoft.com/azure/app-service/configure-authentication-provider-aad) 57 | 2. Go to your registered app overview page under Azure Active Directory App Registrations. Take note of the `Package name`, `Signature hash`, `MSAL Configutaion` 58 | ![Azure Active Directory Configuration](./docs/images/androidConfigurationImage.png) 59 | 3. Edit `AzureCalling/app/src/main/assets/appSettings.properties` and set `isAADAuthEnabled` to enable Azure Active Directory 60 | 4. Edit `AndroidManifest.xml` and set `android:path` to keystore signature hash. (Optional. The current value uses hash from bundled debug.keystore. If different keystore is used, this must be updated.) 61 | 62 | ```xml 63 | 64 | 65 | 66 | 67 | 68 | 71 | android:scheme="msauth" /> 72 | 73 | 74 | ``` 75 | 76 | 5. Copy MSAL Android configuration from Azure portal and paste to `AzureCalling/app/src/main/res/raw/auth_config_single_account.json`. Include "account_mode" : "SINGLE" 77 | 78 | ```json 79 | { 80 | "client_id": "", 81 | "authorization_user_agent": "DEFAULT", 82 | "redirect_uri": "", 83 | "account_mode" : "SINGLE", 84 | "authorities": [ 85 | { 86 | "type": "AAD", 87 | "audience": { 88 | "type": "AzureADMyOrg", 89 | "tenant_id": "" 90 | } 91 | } 92 | ] 93 | } 94 | ``` 95 | 96 | 6. Edit `AzureCalling/app/src/main/assets/appSettings.properties` and set the value for the key `communicationTokenFetchUrl` to be the URL for your secure Authentication Endpoint. 97 | 7. Edit `AzureCalling/app/src/main/assets/appSettings.properties` and set the value for the key `aadScopes` from `Azure Active Directory` `Expose an API` scopes. 98 | 8. Set value for `graphURL` in `AzureCalling/app/assets/appSettings.properties` as the Graph API endpoint to fetch user information. 99 | 9. Edit `AzureCalling/app/src/main/assets/appSettings.properties` and set the value for the key `tenant` to enable silent login so that the user does not have to be authenticate again and again while restarting the application. 100 | 101 | ## Additional Reading 102 | 103 | - [Azure Communication Calling Features](https://docs.microsoft.com/azure/communication-services/concepts/voice-video-calling/calling-sdk-features) - To learn more about the calling Android sdk 104 | -[Azure Communication Android Calling SDK](https://search.maven.org/artifact/com.azure.android/azure-communication-calling) 105 | 106 | ## Known Issues 107 | 108 | Please refer to the [wiki](https://github.com/Azure-Samples/communication-services-android-calling-hero/wiki/Known-Issues) for known issues related to this sample. 109 | -------------------------------------------------------------------------------- /SCC-SPOOL-CallingSample-Android-PR-YMLyml.yml: -------------------------------------------------------------------------------- 1 | 2 | pool: 3 | vmImage: 'macos-latest' 4 | 5 | steps: 6 | 7 | # By default, JAVA_HOME points to java1.8, but we need java11 for the current Android Gradle plugin. 8 | # macos-12 image comes iwth java11 preinstalled, so use that instead. 9 | - task: JavaToolInstaller@0 10 | displayName: 'Configure Java 11' 11 | inputs: 12 | versionSpec: '11' 13 | jdkArchitectureOption: 'x64' 14 | jdkSourceOption: 'PreInstalled' 15 | 16 | - task: Gradle@2 17 | displayName: 'build (compile, lint, test)' 18 | inputs: 19 | workingDirectory: 'AzureCalling' 20 | gradleWrapperFile: 'AzureCalling/gradlew' 21 | gradleOptions: '-Xmx3072m' 22 | publishJUnitResults: false 23 | tasks: 'build --stacktrace' -------------------------------------------------------------------------------- /docs/images/aadOverview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/docs/images/aadOverview.png -------------------------------------------------------------------------------- /docs/images/androidConfigurationImage.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/docs/images/androidConfigurationImage.png -------------------------------------------------------------------------------- /docs/images/intro-page-android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/docs/images/intro-page-android.png -------------------------------------------------------------------------------- /docs/images/landing-page-android.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Azure-Samples/communication-services-android-calling-hero/ac20a503121b573b4ddc2222ef6fd5d003208b0e/docs/images/landing-page-android.png --------------------------------------------------------------------------------