diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000000..ddf90dd039 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,3 @@ +# These owners will be the default owners for everything in +# the repo. Unless a later match takes precedence +* @Azure-Samples/acs-ui-android-reviewer-1 @Azure-Samples/acs-ui-android-reviewer-2 @Azure-Samples/acs-ui-android-reviewer-3 diff --git a/.github/CODE_OF_CONDUCT.md b/.github/CODE_OF_CONDUCT.md new file mode 100644 index 0000000000..f9ba8cf65f --- /dev/null +++ b/.github/CODE_OF_CONDUCT.md @@ -0,0 +1,9 @@ +# Microsoft Open Source Code of Conduct + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). + +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md new file mode 100644 index 0000000000..c8eae5727d --- /dev/null +++ b/.github/ISSUE_TEMPLATE.md @@ -0,0 +1,33 @@ + +> Please provide us with the following information: +> --------------------------------------------------------------- + +### This issue is for a: (mark with an `x`) +``` +- [ ] bug report -> please search issues before submitting +- [ ] feature request +- [ ] documentation issue or request +- [ ] regression (a behavior that used to work and stopped in a new release) +``` + +### Minimal steps to reproduce +> + +### Any log messages given by the failure +> + +### Expected/desired behavior +> + +### OS and Version? +> Windows 7, 8 or 10. Linux (which distribution). macOS (Yosemite? El Capitan? Sierra?) + +### Versions +> + +### Mention any other details that might be useful + +> --------------------------------------------------------------- +> Thanks! We'll be in touch soon. \ No newline at end of file diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md new file mode 100644 index 0000000000..5b8b138c4e --- /dev/null +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -0,0 +1,37 @@ +## Purpose + +* ... + +## Does this introduce a breaking change? + +``` +[ ] Yes +[ ] No +``` + +## Pull Request Type +What kind of change does this Pull Request introduce? + + +``` +[ ] Bugfix +[ ] Feature +[ ] Code style update (formatting, local variables) +[ ] Refactoring (no functional changes, no api changes) +[ ] Documentation content changes +[ ] Other... Please describe: +``` + +## How to Test + +* Test the code + +``` +``` + +## What to Check +Verify that the following are valid +* ... + +## Other Information + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000..bc1a65c2d4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,82 @@ +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# exec files +*.exec + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +**/release/* + +# Gradle files +**/.gradle/* +build/ + +# DS_Store files +*.DS_Store + +# Local configuration file (sdk path, etc) +local.properties +appSettings.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ +*.iml +**/.idea/* + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000000..6f3bc5d10e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,10 @@ +# Release History + +## 1.0.0-beta.1 (2021-12-08) +This is the initial release of Azure Communication UI Library. For more information, please see the [README][read_me] and [QuickStart][documentation]. + +This is a Public Preview version, so breaking changes are possible in subsequent releases as we improve the product. To provide feedback, please submit an issue in our [Issues](https://github.com/Azure/communication-ui-library-android/issues). + + +[read_me]: https://github.com/Azure/communication-ui-library-android/blob/main/README.md +[documentation]: https://docs.microsoft.com/en-us/azure/communication-services/quickstarts/ui-library/get-started-call?tabs=kotlin&pivots=platform-android diff --git a/LICENSE.md b/LICENSE.md new file mode 100644 index 0000000000..9e841e7a26 --- /dev/null +++ b/LICENSE.md @@ -0,0 +1,21 @@ + MIT License + + Copyright (c) Microsoft Corporation. + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in all + copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + SOFTWARE diff --git a/README.md b/README.md index 5cd7cecfc8..0575aab59d 100644 --- a/README.md +++ b/README.md @@ -1,33 +1,121 @@ -# Project +![Hero Image](docs/media/mobile-ui-library-hero-image.png) -> This repo has been populated by an initial template to help get you started. Please -> make sure to update the content to build a great experience for community-building. +# Azure Communication UI Mobile Library for Android -As the maintainer of this project, please make a few updates: +Azure Communication [UI Mobile Library](https://docs.microsoft.com/en-us/azure/communication-services/concepts/ui-library/ui-library-overview) is an Azure Communication Services capability focused on providing UI components for common business-to-consumer and business-to-business calling interactions. -- Improving this README.MD file to provide a great experience -- Updating SUPPORT.MD with content about this project's support experience -- Understanding the security reporting process in SECURITY.MD -- Remove this section from the README -## Contributing +## Getting Started -This project welcomes contributions and suggestions. Most contributions require you to agree to a -Contributor License Agreement (CLA) declaring that you have the right to, and actually do, grant us -the rights to use your contribution. For details, visit https://cla.opensource.microsoft.com. +Get started with Azure Communication Services by using the UI Library to integrate communication experiences into your applications. For detailed instructions to quickly integrate the UI Library functionalities visit the [Quick-start Documentation](https://docs.microsoft.com/en-us/azure/communication-services/quickstarts/ui-library/get-started-call?tabs=kotlin&pivots=platform-android). -When you submit a pull request, a CLA bot will automatically determine whether you need to provide -a CLA and decorate the PR appropriately (e.g., status check, comment). Simply follow the instructions -provided by the bot. You will only need to do this once across all repos using our CLA. -This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or -contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. +### Prerequisites -## Trademarks +- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). +- An OS running [Android Studio](https://developer.android.com/studio). +- A deployed Communication Services resource. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). +- Azure Communication Services Token. [See example](https://docs.microsoft.com/azure/communication-services/tutorials/trusted-service-tutorial) -This project may contain trademarks or logos for projects, products, or services. Authorized use of Microsoft -trademarks or logos is subject to and must follow -[Microsoft's Trademark & Brand Guidelines](https://www.microsoft.com/en-us/legal/intellectualproperty/trademarks/usage/general). -Use of Microsoft trademarks or logos in modified versions of this project must not cause confusion or imply Microsoft sponsorship. -Any use of third-party trademarks or logos are subject to those third-party's policies. +### Install the packages + +In your app level (**app folder**) `build.gradle`, add the following lines to the dependencies and android sections. + +```groovy +android { + ... + packagingOptions { + pickFirst 'META-INF/*' + } + ... +} +``` + +```groovy +dependencies { + ... + implementation 'com.azure.android:azure-communication-ui:+' + ... +} +``` + +In your project gradle scripts add following lines to `repositories`. For `Android Studio (2020.*)` the `repositories` are in `settings.gradle` `dependencyResolutionManagement(Gradle version 6.8 or greater)`. If you are using old versions of `Android Studio (4.*)` then the `repositories` will be in project level `build.gradle` `allprojects{}`. + +```groovy +repositories { + ... + mavenCentral() + maven { + url "https://pkgs.dev.azure.com/MicrosoftDeviceSDK/DuoSDK-Public/_packaging/Duo-SDK-Feed/maven/v1" + } + ... +} +``` +Sync project with gradle files. (Android Studio -> File -> Sync Project With Gradle Files) + + +### Quick Sample + +Create `CallComposite` and launch it. Replace `` with your group ID for your call, `` with your name, and `` with your token. + +#### [Kotlin](#tab/kotlin) + +```kotlin +val communicationTokenRefreshOptions = CommunicationTokenRefreshOptions({ "" }, true) +val communicationTokenCredential = CommunicationTokenCredential(communicationTokenRefreshOptions) + +val options = GroupCallOptions( + context, + communicationTokenCredential, + UUID.fromString(""), + "", +) + +val callComposite: CallComposite = CallCompositeBuilder().build() +callComposite.launch(options) +``` + +#### [Java](#tab/java) + +```java +CommunicationTokenRefreshOptions communicationTokenRefreshOptions = + new CommunicationTokenRefreshOptions(() -> "", true); + +CommunicationTokenCredential communicationTokenCredential = + new CommunicationTokenCredential(communicationTokenRefreshOptions); + +GroupCallOptions options = new GroupCallOptions( + context, + communicationTokenCredential, + UUID.fromString(""), + "" + ); + +CallComposite callComposite = new CallCompositeBuilder().build(); +callComposite.launch(options); +``` + +For more details on Mobile UI Library functionalities visit the [API Reference Documentation](docs/api/CallComposite/Reference.md). + +## Contributing to the Library + +Before developing and contributing to Communication Mobile UI Library, check out our [making a contribution guide](docs/contributing-guide.md). +Included in this repository is a demo of using Mobile UI Library to start a call. You can find the detail of using and developing the UI Library in the [Demo Guide](azure-communication-ui/azure-communication-ui-demo-app). + +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. Also, please check our [Contribution Policy](CONTRIBUTING.md). + +## Community Help and Support + +If you find a bug or have a feature request, please raise the issue on [GitHub Issues](https://github.com/Azure/communication-ui-library-android/issues). + +## Known Issues + +Please refer to the [wiki](https://github.com/Azure/communication-ui-library-android/wiki/Known-Issues) for known issues related to the library. + +## Further Reading + +* [Azure Communication UI Library Conceptual Documentation](https://docs.microsoft.com/azure/communication-services/concepts/ui-framework/ui-sdk-overview) +* [Azure Communication Service](https://docs.microsoft.com/en-us/azure/communication-services/overview) +* [Azure Communication Client and Server Architecture](https://docs.microsoft.com/en-us/azure/communication-services/concepts/client-and-server-architecture) +* [Azure Communication Authentication](https://docs.microsoft.com/en-us/azure/communication-services/concepts/authentication) +* [Azure Communication Service Troubleshooting](https://docs.microsoft.com/en-us/azure/communication-services/concepts/troubleshooting-info) \ No newline at end of file diff --git a/azure-communication-ui/.gitignore b/azure-communication-ui/.gitignore new file mode 100644 index 0000000000..aa724b7707 --- /dev/null +++ b/azure-communication-ui/.gitignore @@ -0,0 +1,15 @@ +*.iml +.gradle +/local.properties +/.idea/caches +/.idea/libraries +/.idea/modules.xml +/.idea/workspace.xml +/.idea/navEditor.xml +/.idea/assetWizardSettings.xml +.DS_Store +/build +/captures +.externalNativeBuild +.cxx +local.properties diff --git a/azure-communication-ui/azure-communication-ui-demo-app/.gitignore b/azure-communication-ui/azure-communication-ui-demo-app/.gitignore new file mode 100644 index 0000000000..42afabfd2a --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/.gitignore @@ -0,0 +1 @@ +/build \ No newline at end of file diff --git a/azure-communication-ui/azure-communication-ui-demo-app/README.md b/azure-communication-ui/azure-communication-ui-demo-app/README.md new file mode 100644 index 0000000000..50bce57444 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/README.md @@ -0,0 +1,26 @@ +# Azure Communication UI Mobile Library for Android Demo App + +The sample app is a native Android application developed to demonstrate Azure Communication UI library. Showcases use of both Java and Kotlin to run library. + + +## Getting Started + +### Prerequisites + +- An Azure account with an active subscription. [Create an account for free](https://azure.microsoft.com/free/?WT.mc_id=A261C142F). +- An OS running [Android Studio](https://developer.android.com/studio). +- A deployed Communication Services resource. [Create a Communication Services resource](https://docs.microsoft.com/azure/communication-services/quickstarts/create-communication-resource). +- Azure Communication Services Token. [See example](https://docs.microsoft.com/azure/communication-services/tutorials/trusted-service-tutorial) +- (Optional) Create Azure Communication Services Token service URL. [See example](https://docs.microsoft.com/azure/communication-services/tutorials/trusted-service-tutorial). + +### Run Sample + +1. Open azure-communication-ui folder in Android Studio +2. Select azure-communication-ui-demo-app as a build configuration +3. (Optional) for your convinience you may configure default values for the app. Create `local.properties` file in the `/azure-communication-ui` directory: + - `TOKEN_FUNCTION_URL`="..." # the URL to request Azure Communication Services token + - `ACS_TOKEN`="..." # Azure Communication Services token + - `USER_NAME`="..." # your preferred display name + - `GROUP_CALL_ID`="..." # this a type of UUID used to start and join a meeting + - `TEAMS_MEETING_LINK`="..." # the URL to a Teams meeting +4. Build and Run diff --git a/azure-communication-ui/azure-communication-ui-demo-app/build.gradle b/azure-communication-ui/azure-communication-ui-demo-app/build.gradle new file mode 100644 index 0000000000..769d9da617 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/build.gradle @@ -0,0 +1,81 @@ +plugins { + id 'com.android.application' + id 'kotlin-android' +} + +Properties properties = new Properties() +if (project.rootProject.file('local.properties').canRead()) { + properties.load(project.rootProject.file('local.properties').newDataInputStream()) +} + +android { + compileSdkVersion 30 + buildToolsVersion "30.0.3" + + defaultConfig { + applicationId "com.azure.android.communication.ui.callingCompositeDemoApp" + minSdkVersion 23 + targetSdkVersion 30 + versionCode 1 + versionName "0.0.0" + + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + + buildConfigField 'String', 'TOKEN_FUNCTION_URL', properties.getProperty('TOKEN_FUNCTION_URL', '""') + buildConfigField 'String', 'USER_NAME', properties.getProperty('USER_NAME', '""') + buildConfigField 'String', 'GROUP_CALL_ID', properties.getProperty('GROUP_CALL_ID', '""') + buildConfigField 'String', 'TEAMS_MEETING_LINK', properties.getProperty('TEAMS_MEETING_LINK', '""') + buildConfigField 'String', 'ACS_TOKEN', properties.getProperty('ACS_TOKEN', '""') + } + + signingConfigs { + debug { + storeFile file('debug.keystore') + storePassword "android" + keyAlias "androiddebugkey" + keyPassword "android" + } + release { + storeFile file(String.valueOf(System.getenv("KEYSTORE_FILEPATH"))) + storePassword System.getenv("KEYSTORE_PASSWORD") + keyAlias System.getenv("KEY_ALIAS") + keyPassword System.getenv("KEY_PASSWORD") + } + } + + buildFeatures { + viewBinding true + } + + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + signingConfig signingConfigs.release + } + } + + compileOptions { + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + + kotlinOptions { + jvmTarget = '1.8' + } +} + +dependencies { + implementation project(path: ':azure-communication-ui') + implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version" + implementation "androidx.core:core-ktx:$androidx_core_version" + implementation "androidx.appcompat:appcompat:$appcompat_version" + implementation 'androidx.activity:activity-ktx:1.3.1' + implementation 'com.google.android.material:material:1.4.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.0' + implementation 'com.github.kittinunf.fuel:fuel:2.3.1' + implementation "com.azure.android:azure-communication-calling:$calling_sdk_version" + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' + androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0' +} diff --git a/azure-communication-ui/azure-communication-ui-demo-app/checkstyle.gradle b/azure-communication-ui/azure-communication-ui-demo-app/checkstyle.gradle new file mode 100644 index 0000000000..033f3826dd --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/checkstyle.gradle @@ -0,0 +1,15 @@ +apply plugin: 'checkstyle' + +checkstyle { + description 'Check style for the Java code files' + configFile file('../azure-communication-ui-demo-app/checkstyle/checkstyle.xml') +} + +task checkstyle(type: Checkstyle) { + source 'src' + include '**/*.java' + exclude '**/gen/**' + exclude '**/R.java' + exclude '**/BuildConfig.java' + classpath = files() +} \ No newline at end of file diff --git a/azure-communication-ui/azure-communication-ui-demo-app/checkstyle/checkstyle.xml b/azure-communication-ui/azure-communication-ui-demo-app/checkstyle/checkstyle.xml new file mode 100644 index 0000000000..e542db0005 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/checkstyle/checkstyle.xml @@ -0,0 +1,285 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/azure-communication-ui/azure-communication-ui-demo-app/debug.keystore b/azure-communication-ui/azure-communication-ui-demo-app/debug.keystore new file mode 100644 index 0000000000..e35d2e12b9 Binary files /dev/null and b/azure-communication-ui/azure-communication-ui-demo-app/debug.keystore differ diff --git a/azure-communication-ui/azure-communication-ui-demo-app/proguard-rules.pro b/azure-communication-ui/azure-communication-ui-demo-app/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/androidTest/java/com/azure/android/communication/ui/ExampleInstrumentedTest.kt b/azure-communication-ui/azure-communication-ui-demo-app/src/androidTest/java/com/azure/android/communication/ui/ExampleInstrumentedTest.kt new file mode 100644 index 0000000000..87f1b9d581 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/androidTest/java/com/azure/android/communication/ui/ExampleInstrumentedTest.kt @@ -0,0 +1,25 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.android.communication.ui + +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.platform.app.InstrumentationRegistry +import org.junit.Assert.assertEquals +import org.junit.Test +import org.junit.runner.RunWith + +/** + * Instrumented test, which will execute on an Android device. + * + * See [testing documentation](http://d.android.com/tools/testing). + */ +@RunWith(AndroidJUnit4::class) +class ExampleInstrumentedTest { + @Test + fun useAppContext() { + // Context of the app under test. + val appContext = InstrumentationRegistry.getInstrumentation().targetContext + assertEquals("com.azure.android.communication.ui", appContext.packageName) + } +} diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/AndroidManifest.xml b/azure-communication-ui/azure-communication-ui-demo-app/src/main/AndroidManifest.xml new file mode 100644 index 0000000000..06afd44118 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/MainActivity.kt b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/MainActivity.kt new file mode 100644 index 0000000000..33bc6478f8 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/MainActivity.kt @@ -0,0 +1,173 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.android.communication.ui.callingcompositedemoapp + +import android.os.Bundle +import androidx.activity.viewModels +import androidx.appcompat.app.AlertDialog +import androidx.appcompat.app.AppCompatActivity +import com.azure.android.communication.ui.callingcompositedemoapp.databinding.ActivityMainBinding +import com.azure.android.communication.ui.callingcompositedemoapp.launcher.CallingCompositeLauncher +import java.util.UUID + +class MainActivity : AppCompatActivity() { + private lateinit var binding: ActivityMainBinding + private val mainViewModel: MainViewModel by viewModels() + private val isTokenFunctionOptionSelected: String = "isTokenFunctionOptionSelected" + private val isKotlinLauncherOptionSelected: String = "isKotlinLauncherOptionSelected" + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + binding = ActivityMainBinding.inflate(layoutInflater) + setContentView(binding.root) + + if (savedInstanceState != null) { + if (savedInstanceState.getBoolean(isTokenFunctionOptionSelected)) { + mainViewModel.useTokenFunction() + } else { + mainViewModel.useAcsToken() + } + + if (savedInstanceState.getBoolean(isKotlinLauncherOptionSelected)) { + mainViewModel.setKotlinLauncher() + } else { + mainViewModel.setJavaLauncher() + } + } + + binding.run { + tokenFunctionUrlText.setText(BuildConfig.TOKEN_FUNCTION_URL) + userNameText.setText(BuildConfig.USER_NAME) + groupIdOrTeamsMeetingLinkText.setText(BuildConfig.GROUP_CALL_ID) + acsTokenText.setText(BuildConfig.ACS_TOKEN) + + launchButton.setOnClickListener { + launchButton.isEnabled = false + mainViewModel.doLaunch( + tokenFunctionUrlText.text.toString(), + acsTokenText.text.toString() + ) + } + + tokenFunctionRadioButton.setOnClickListener { + if (tokenFunctionRadioButton.isChecked) { + tokenFunctionUrlText.requestFocus() + tokenFunctionUrlText.isEnabled = true + acsTokenText.isEnabled = false + acsTokenRadioButton.isChecked = false + mainViewModel.useTokenFunction() + } + } + acsTokenRadioButton.setOnClickListener { + if (acsTokenRadioButton.isChecked) { + acsTokenText.requestFocus() + acsTokenText.isEnabled = true + tokenFunctionUrlText.isEnabled = false + tokenFunctionRadioButton.isChecked = false + mainViewModel.useAcsToken() + } + } + groupCallRadioButton.setOnClickListener { + if (groupCallRadioButton.isChecked) { + groupIdOrTeamsMeetingLinkText.setText(BuildConfig.GROUP_CALL_ID) + teamsMeetingRadioButton.isChecked = false + } + } + teamsMeetingRadioButton.setOnClickListener { + if (teamsMeetingRadioButton.isChecked) { + groupIdOrTeamsMeetingLinkText.setText(BuildConfig.TEAMS_MEETING_LINK) + groupCallRadioButton.isChecked = false + } + } + javaButton.setOnClickListener { + mainViewModel.setJavaLauncher() + } + kotlinButton.setOnClickListener { + mainViewModel.setKotlinLauncher() + } + } + + mainViewModel.fetchResult.observe(this) { + processResult(it) + } + } + + override fun onDestroy() { + mainViewModel.destroy() + super.onDestroy() + } + + override fun onSaveInstanceState(outState: Bundle) { + saveState(outState) + super.onSaveInstanceState(outState) + } + + private fun processResult(result: Result) { + if (result.isFailure) { + result.exceptionOrNull()?.let { + if (it.message != null) { + val causeMessage = it.cause?.message ?: "" + showAlert(it.toString() + causeMessage) + binding.launchButton.isEnabled = true + } else { + showAlert("Unknown error") + } + } + } + if (result.isSuccess) { + result.getOrNull()?.let { launcherObject -> + launch(launcherObject) + binding.launchButton.isEnabled = true + } + } + } + + private fun launch(launcher: CallingCompositeLauncher) { + val userName = binding.userNameText.text.toString() + + if (binding.groupCallRadioButton.isChecked) { + val groupId: UUID + + try { + groupId = + UUID.fromString(binding.groupIdOrTeamsMeetingLinkText.text.toString().trim()) + } catch (e: IllegalArgumentException) { + val message = "Group ID is invalid or empty." + showAlert(message) + return + } + + launcher.launch(this@MainActivity, userName, groupId, null, ::showAlert) + } + + if (binding.teamsMeetingRadioButton.isChecked) { + val meetingLink = binding.groupIdOrTeamsMeetingLinkText.text.toString() + + if (meetingLink.isBlank()) { + val message = "Teams meeting link is invalid or empty." + showAlert(message) + return + } + + launcher.launch(this@MainActivity, userName, null, meetingLink, ::showAlert) + } + } + + private fun showAlert(message: String) { + runOnUiThread { + val builder = AlertDialog.Builder(this).apply { + setMessage(message) + setTitle("Alert") + setPositiveButton("OK") { _, _ -> + } + } + builder.show() + } + } + + private fun saveState(outstate: Bundle?) { + outstate?.putBoolean(isTokenFunctionOptionSelected, mainViewModel.isTokenFunctionOptionSelected) + outstate?.putBoolean(isKotlinLauncherOptionSelected, mainViewModel.isKotlinLauncher) + } +} diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/MainViewModel.kt b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/MainViewModel.kt new file mode 100644 index 0000000000..7670003418 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/MainViewModel.kt @@ -0,0 +1,87 @@ +package com.azure.android.communication.ui.callingcompositedemoapp + +import android.webkit.URLUtil +import androidx.lifecycle.LiveData +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import com.azure.android.communication.ui.callingcompositedemoapp.launcher.CallingCompositeJavaLauncher +import com.azure.android.communication.ui.callingcompositedemoapp.launcher.CallingCompositeKotlinLauncher +import com.azure.android.communication.ui.callingcompositedemoapp.launcher.CallingCompositeLauncher +import com.github.kittinunf.fuel.httpGet +import org.json.JSONObject +import java.io.IOException + +class MainViewModel : ViewModel() { + private var token: String? = null + + var isKotlinLauncher = true; private set + var isTokenFunctionOptionSelected = false; private set + + private val launcher = hashMapOf( + true to CallingCompositeKotlinLauncher(::getToken), + false to CallingCompositeJavaLauncher(::getToken) + ) + + private val fetchResultInternal = MutableLiveData>() + val fetchResult: LiveData> = fetchResultInternal + + fun destroy() { + fetchResultInternal.value = Result.success(null) + } + + fun setJavaLauncher() { + isKotlinLauncher = false + } + + fun setKotlinLauncher() { + isKotlinLauncher = true + } + + fun useTokenFunction() { + isTokenFunctionOptionSelected = true + } + + fun useAcsToken() { + isTokenFunctionOptionSelected = false + } + + fun doLaunch(tokenFunctionURL: String, acsToken: String) { + when { + isTokenFunctionOptionSelected -> { + token = null + fetchToken(tokenFunctionURL) + } + acsToken.isNotBlank() -> { + token = acsToken + fetchResultInternal.value = Result.success(launcher[isKotlinLauncher]) + } + else -> fetchResultInternal.value = Result.failure( + IllegalStateException("Invalid Token function or acs Token") + ) + } + } + + private fun urlIsValid(url: String) = url.isNotBlank() && URLUtil.isValidUrl(url.trim()) + + private fun fetchToken(tokenFunctionURL: String) { + + if (urlIsValid(tokenFunctionURL)) { + tokenFunctionURL + .httpGet() + .responseString { result -> + val response = result.component1() + val cause = result.component2() + if (cause != null || response == null) { + fetchResultInternal.postValue(Result.failure(IOException("Unable to fetch token: ", cause))) + } else { + token = JSONObject(response).getString("token") + fetchResultInternal.postValue(Result.success(launcher[isKotlinLauncher])) + } + } + } else { + fetchResultInternal.value = Result.failure(IllegalStateException("Token function URL error")) + } + } + + private fun getToken() = token!! +} diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeJavaLauncher.java b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeJavaLauncher.java new file mode 100644 index 0000000000..8e2cd188ea --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeJavaLauncher.java @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.android.communication.ui.callingcompositedemoapp.launcher; + +import android.content.Context; +import android.text.TextUtils; + +import com.azure.android.communication.common.CommunicationTokenCredential; +import com.azure.android.communication.common.CommunicationTokenRefreshOptions; +import com.azure.android.communication.ui.CallComposite; +import com.azure.android.communication.ui.CallCompositeBuilder; +import com.azure.android.communication.ui.GroupCallOptions; +import com.azure.android.communication.ui.TeamsMeetingOptions; + +import java.util.UUID; +import java.util.concurrent.Callable; + +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +public class CallingCompositeJavaLauncher implements CallingCompositeLauncher { + private final Callable tokenRefresher; + + public CallingCompositeJavaLauncher(final Callable tokenRefresher) { + this.tokenRefresher = tokenRefresher; + } + + @Override + public void launch(final Context context, + final String displayName, + final UUID groupId, + final String meetingLink, + final Function1 showAlert) { + final CallComposite callComposite = + new CallCompositeBuilder() +// .theme(new ThemeConfiguration(R.style.MyCompany_Theme)) + .build(); + + callComposite.setOnErrorHandler(eventHandler -> { + System.out.println("================= application is logging exception ================="); + System.out.println(eventHandler.getCause()); + System.out.println(eventHandler.getErrorCode()); + if (eventHandler.getCause() != null) { + showAlert.invoke(eventHandler.getErrorCode().toString() + " " + + eventHandler.getCause().getMessage()); + } else { + showAlert.invoke(eventHandler.getErrorCode().toString()); + } + System.out.println("===================================================================="); + }); + + final CommunicationTokenRefreshOptions communicationTokenRefreshOptions = + new CommunicationTokenRefreshOptions(tokenRefresher, true); + final CommunicationTokenCredential communicationTokenCredential = + new CommunicationTokenCredential(communicationTokenRefreshOptions); + + if (groupId != null) { + final GroupCallOptions groupCallOptions = + new GroupCallOptions(context, communicationTokenCredential, groupId, displayName); + + callComposite.launch(groupCallOptions); + + } else if (!TextUtils.isEmpty(meetingLink)) { + final TeamsMeetingOptions teamsMeetingOptions = + new TeamsMeetingOptions(context, communicationTokenCredential, meetingLink, displayName); + + callComposite.launch(teamsMeetingOptions); + } + } +} diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeKotlinLauncher.kt b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeKotlinLauncher.kt new file mode 100644 index 0000000000..011f52254d --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeKotlinLauncher.kt @@ -0,0 +1,63 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.azure.android.communication.ui.callingcompositedemoapp.launcher + +import android.content.Context +import com.azure.android.communication.common.CommunicationTokenCredential +import com.azure.android.communication.common.CommunicationTokenRefreshOptions +import com.azure.android.communication.ui.CallComposite +import com.azure.android.communication.ui.CallCompositeBuilder +import com.azure.android.communication.ui.GroupCallOptions +import com.azure.android.communication.ui.TeamsMeetingOptions +import java.util.UUID +import java.util.concurrent.Callable + +class CallingCompositeKotlinLauncher(private val tokenRefresher: Callable) : + CallingCompositeLauncher { + + override fun launch( + context: Context, + displayName: String, + groupId: UUID?, + meetingLink: String?, + showAlert: ((String) -> Unit)?, + ) { + val callComposite: CallComposite = CallCompositeBuilder().build() + + callComposite.setOnErrorHandler { + println("================= application is logging exception =================") + println(it.cause) + println(it.errorCode) + if (it.cause != null) { + showAlert?.invoke(it.errorCode.toString() + " " + it.cause?.message) + } else { + showAlert?.invoke(it.errorCode.toString()) + } + println("====================================================================") + } + + val communicationTokenRefreshOptions = + CommunicationTokenRefreshOptions(tokenRefresher, true) + val communicationTokenCredential = + CommunicationTokenCredential(communicationTokenRefreshOptions) + + if (groupId != null) { + val groupCallOptions = GroupCallOptions( + context, + communicationTokenCredential, + groupId, + displayName, + ) + callComposite.launch(groupCallOptions) + } else if (!meetingLink.isNullOrBlank()) { + val teamsMeetingOptions = TeamsMeetingOptions( + context, + communicationTokenCredential, + meetingLink, + displayName, + ) + callComposite.launch(teamsMeetingOptions) + } + } +} diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeLauncher.java b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeLauncher.java new file mode 100644 index 0000000000..ac461df584 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/java/com/azure/android/communication/ui/callingcompositedemoapp/launcher/CallingCompositeLauncher.java @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. +package com.azure.android.communication.ui.callingcompositedemoapp.launcher; + +import android.content.Context; + +import java.util.UUID; + +import kotlin.Unit; +import kotlin.jvm.functions.Function1; + +public interface CallingCompositeLauncher { + void launch(Context context, + String userName, + UUID groupId, + String meetingLink, + Function1 showAlert); +} diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000000..7b55b3fa09 --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,35 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/drawable/ic_launcher_background.xml b/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000000..f40a1ef53c --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,204 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/layout/activity_main.xml b/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000000..953d59e00b --- /dev/null +++ b/azure-communication-ui/azure-communication-ui-demo-app/src/main/res/layout/activity_main.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + +