Skip to content

Update Android sample to Android 15 #13

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 75 additions & 36 deletions Android/AmazonConnectInAppCallingAndroidSample/README.md
Original file line number Diff line number Diff line change
@@ -1,36 +1,75 @@
# Amazon Connect In-App Calling Sample - Android

This sample Android app demonstrates how to interact with Amazon Connect APIs to start an in-app call
with Amazon Connect and send DTMF.

This sample app works together with the serverless solution deployed
by the `Amazon Connect In-App Calling API Sample`
to deliver an in-app calling experience. Please make sure you follow the `README` and deploy the serverless demo first, and note down the output.

> NOTE: this sample is for demo purposes.

## Setup
1. `Amazon Connect In-App Calling API Sample` has been deployed.
2. Git clone the repo
3. Open the project from [Android Studio](https://developer.android.com/studio)
4. Make sure Android SDK location has been properly configured by clicking *File* -> *Project Structure* -> *SDK Location*, or go to `<Project root path>/local.properties`, there should be `sdk.dir` property
5. Connect a physical Android device, make sure developer mode is on by following [this guide](https://developer.android.com/studio/debug/dev-options)
1. Fill in required configurations in `CallConfiguration.kt`
```kotlin
data class CallConfiguration(
// ...
val connectInstanceId: String = "", // your Amazon Connect instance Id
val contactFlowId: String = "", // your contact flow Id that you want to associated with the calls
val startWebrtcEndpoint: String = "", // the endpoint URL of startWebrtcContact API deployed by the serverless demo
val createParticipantConnectionEndpoint: String = "", // the endpoint URL of createParticipantConnection API deployed by the serverless demo
val sendMessageEndpoint: String = "", // the endpoint URL of sendMessage API deployed by the serverless demo
// ...
)
```
6. In Android Studio, choose your connected device, and target `app`, click run

### Key files and methods in the sample code

**CallManager**: Contains methods to: 1) manage the call session, 2) send DTMF 3) audio / video controls (e.g., mute, unmute, start, stop video) 4) handle SDK meeting events (e.g., meeting started / ende, attendee joined, dropped)

**Call Sheet**: Main UI for hosting the call session
# Amazon Connect In-App Calling Sample – Android

This sample Android app demonstrates how to interact with Amazon Connect APIs to start an **in-app voice/video call** and send **DTMF**. It also supports **screen sharing** via Amazon Chime SDK.

This app works together with the serverless solution provided by the [`Amazon Connect In-App Calling API Sample`](https://github.com/amazon-connect/amazon-connect-in-app-calling-examples/tree/main/Backend/AmazonConnectNetraApiSample).
➡️ **Please deploy the backend first and take note of the endpoint outputs.**


---

## 📱 Preview

<img src="docs/screenshot.png" alt="App Screenshot" width="300" />

---

## 🚀 Setup

1. **Deploy the Backend**
Follow the [Amazon Connect In-App Calling API Sample](https://github.com/amazon-connect/amazon-connect-in-app-calling-examples/tree/main/Backend/AmazonConnectNetraApiSample) README to deploy the Lambda-based APIs via AWS CDK.

2. **Clone this repo**

```bash
git clone https://github.com/amazon-connect/amazon-connect-in-app-calling-examples.git
cd Android/AmazonConnectInAppCallingAndroidSample
```

3. **Configure Android SDK**
- Open in [Android Studio](https://developer.android.com/studio)
- Ensure Android SDK path is set:
- File → Project Structure → SDK Location
- Or edit local.properties:
```properties
sdk.dir=/your/android/sdk/path
```

4. **Provide Required Configuration**
Add the following entries in your local.properties file:
```properties
connect.instanceId=your-connect-instance-id
connect.contactFlowId=your-contact-flow-id
endpoints.startWebRTC=https://your-start-webrtc-endpoint/
endpoints.createParticipant=https://your-create-participant-endpoint/
endpoints.sendMessage=https://your-send-message-endpoint/
```
These values are used as default and can be changed in the app’s Configuration dialog at runtime.

5. **Run the App**
- Connect a physical Android device
- Hit ▶️ Run in Android Studio and target the app

6. **Start a Call**
- Enter your Display Name and City
- Tap Start Call to initiate
- Click **Config** button to update endpoints if needed

## 🧩 Key Components

| Component | File / Class Name | Responsibility |
|------------------------|----------------------------------------|-------------------------------------------------------------------------------|
| Call Sheet | `CallSheet.kt` | Main UI sheet that shows during an active call |
| Call Manager | `CallManager.kt` | Manages the call session, audio/video state, DTMF, and participants events |
| Call Connection | `CallConnection.kt` | Bridges the in-app call with Android Telecom. Manages call audio routing changes (e.g. Bluetooth, speaker), and handles system-level disconnects or conflicts with other ongoing calls. |
| Screen Share Manager | `ScreenShareManager.kt` | Manages screen capture and sharing |
| Configuration Repository | `ConfigRepository.kt` | Central source for retrieving call-related configuration used to start calls |

## ✅ Features
- In-app audio/video calling with Amazon Connect
- Dynamic contact attributes (e.g., name and city)
- DTMF (dual-tone signaling)
- Screen sharing
- Video background blur
- Voice focus

36 changes: 22 additions & 14 deletions Android/AmazonConnectInAppCallingAndroidSample/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,30 +8,38 @@ localProperties.load(new FileInputStream((rootProject.file("local.properties")))

android {
namespace 'com.amazonaws.services.connect.inappcalling.sample'
compileSdk 33
compileSdk 35

defaultConfig {
applicationId "com.amazonaws.services.connect.inappcalling.sample"
minSdk 24
targetSdk 33
targetSdk 35
versionCode 1
versionName "1.0"

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

buildTypes {
debug {
resValue("string", "connect_instance_id", localProperties['connect.instanceId'] ?: "")
resValue("string", "contact_flow_id", localProperties['connect.contactFlowId'] ?: "")
resValue("string", "start_webrtc_endpoint", localProperties['endpoints.startWebRTC'] ?: "")
resValue("string", "create_participant_connection_endpoint", localProperties['endpoints.createParticipant'] ?: "")
resValue("string", "send_message_endpoint", localProperties['endpoints.sendMessage'] ?: "")
}

release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = '1.8'
jvmTarget = '17'
}
buildFeatures {
viewBinding true
Expand All @@ -40,15 +48,15 @@ android {
}

dependencies {
implementation 'androidx.core:core-ktx:1.7.0'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.9.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.navigation:navigation-fragment-ktx:2.6.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.6.0'
implementation 'software.aws.chimesdk:amazon-chime-sdk:0.21.0'
implementation 'androidx.core:core-ktx:1.16.0'
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.navigation:navigation-fragment-ktx:2.9.0'
implementation 'androidx.navigation:navigation-ui-ktx:2.9.0'
implementation 'software.aws.chimesdk:amazon-chime-sdk:0.25.0'
implementation 'com.google.code.gson:gson:2.10.1'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />

<application
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,11 @@ import android.content.Context

data class CallConfiguration(
val applicationContext: Context,
val connectInstanceId: String = "",
val contactFlowId: String = "",
val startWebrtcEndpoint: String = "",
val createParticipantConnectionEndpoint: String = "",
val sendMessageEndpoint: String = "",
val connectInstanceId: String,
val contactFlowId: String,
val startWebrtcEndpoint: String,
val createParticipantConnectionEndpoint: String,
val sendMessageEndpoint: String,
val displayName: String,
val attributes: Map<String, String>
)
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,35 @@ package com.amazonaws.services.connect.inappcalling.sample

import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
import com.amazonaws.services.connect.inappcalling.sample.common.showGeneralErrorAlert
import com.amazonaws.services.connect.inappcalling.sample.data.ConfigRepository
import com.amazonaws.services.connect.inappcalling.sample.databinding.ActivityMainBinding
import com.amazonaws.services.connect.inappcalling.sample.ui.CallSheet
import com.amazonaws.services.connect.inappcalling.sample.ui.utils.ConfigDialog

const val displayNameKey = "DisplayName"
const val cityKey = "City"
class MainActivity : AppCompatActivity() {

private lateinit var binding: ActivityMainBinding
private lateinit var configRepo: ConfigRepository

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// set system bar's text color suits for a light bg
WindowCompat.getInsetsController(window, window.decorView)
.isAppearanceLightStatusBars = true

binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)

configRepo = ConfigRepository(applicationContext)

binding.btnConfig.setOnClickListener {
ConfigDialog(this, configRepo).show()
}

binding.startCallButton.setOnClickListener {
startCall()
}
Expand All @@ -36,9 +50,21 @@ class MainActivity : AppCompatActivity() {
showGeneralErrorAlert(getString(R.string.error_input_are_required))
return
}

val connectInstanceId = configRepo.getConnectInstanceId()
val contactFlowId = configRepo.getContactFlowId()
val startWebrtcEndpoint = configRepo.getStartWebrtcEndpoint()
val createParticipantConnectionEndpoint = configRepo.getCreateParticipantConnectionEndpoint()
val sendMessageEndpoint = configRepo.getSendMessageEndpoint()

// Create service locator for data access
val config = CallConfiguration(
applicationContext = applicationContext,
connectInstanceId = connectInstanceId,
contactFlowId = contactFlowId,
startWebrtcEndpoint = startWebrtcEndpoint,
createParticipantConnectionEndpoint = createParticipantConnectionEndpoint,
sendMessageEndpoint = sendMessageEndpoint,
displayName = displayName,
attributes = mapOf(displayNameKey to displayName, cityKey to city)
)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.amazonaws.services.connect.inappcalling.sample.data

import android.content.Context
import androidx.core.content.edit
import com.amazonaws.services.connect.inappcalling.sample.R

class ConfigRepository(private val context: Context) {

private val prefs = context.getSharedPreferences("config_prefs", Context.MODE_PRIVATE)

fun getConnectInstanceId(): String =
prefs.getString(KEY_CONNECT_INSTANCE_ID, null)
?: context.getString(R.string.connect_instance_id)

fun getContactFlowId(): String =
prefs.getString(KEY_CONTACT_FLOW_ID, null)
?: context.getString(R.string.contact_flow_id)

fun getStartWebrtcEndpoint(): String =
prefs.getString(KEY_START_WEBRTC_ENDPOINT, null)
?: context.getString(R.string.start_webrtc_endpoint)

fun getCreateParticipantConnectionEndpoint(): String =
prefs.getString(KEY_CREATE_PARTICIPANT_ENDPOINT, null)
?: context.getString(R.string.create_participant_connection_endpoint)

fun getSendMessageEndpoint(): String =
prefs.getString(KEY_SEND_MESSAGE_ENDPOINT, null)
?: context.getString(R.string.send_message_endpoint)

fun saveConnectInstanceId(value: String) {
prefs.edit { putString(KEY_CONNECT_INSTANCE_ID, value) }
}

fun saveContactFlowId(value: String) {
prefs.edit { putString(KEY_CONTACT_FLOW_ID, value) }
}

fun saveStartWebrtcEndpoint(value: String) {
prefs.edit { putString(KEY_START_WEBRTC_ENDPOINT, value) }
}

fun saveCreateParticipantConnectionEndpoint(value: String) {
prefs.edit { putString(KEY_CREATE_PARTICIPANT_ENDPOINT, value) }
}

fun saveSendMessageEndpoint(value: String) {
prefs.edit { putString(KEY_SEND_MESSAGE_ENDPOINT, value) }
}

companion object {
private const val KEY_CONNECT_INSTANCE_ID = "connect_instance_id"
private const val KEY_CONTACT_FLOW_ID = "contact_flow_id"
private const val KEY_START_WEBRTC_ENDPOINT = "start_webrtc_endpoint"
private const val KEY_CREATE_PARTICIPANT_ENDPOINT = "create_participant_connection_endpoint"
private const val KEY_SEND_MESSAGE_ENDPOINT = "send_message_endpoint"
}
}
Loading