Skip to content
This repository was archived by the owner on Feb 20, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
4d3115d
ConnectionServices Kotlin sample
VZaphod Jun 27, 2025
3447aa0
Removed /build from .gitignore
VZaphod Jun 30, 2025
fec8c03
Removed local OpenTok and related dependencies
VZaphod Jun 30, 2025
97e9b3e
Removed proguard
VZaphod Jun 30, 2025
1293111
Removed sample instrumented & unit tests
VZaphod Jun 30, 2025
6f2e47b
Removed unused permission & tools
VZaphod Jun 30, 2025
8891b9a
Replaced `HangUp` with `End call`
VZaphod Jun 30, 2025
d016bef
Display OpenTok errors
VZaphod Jun 30, 2025
d3ee306
Updated permissions request
VZaphod Jul 1, 2025
49a2063
Added kotlin and java connection service workflows
VZaphod Jul 1, 2025
786503c
Removed Java version workflow
VZaphod Jul 1, 2025
b2912d8
Removed unused actions
VZaphod Jul 1, 2025
bf050b0
If current state is answering or dialing when session connects then u…
VZaphod Jul 1, 2025
6c912c9
isRinging was always false when posting the incoming call notificatio…
VZaphod Jul 1, 2025
a8c1f8b
Using non deprecated version of `stopForeground`
VZaphod Jul 1, 2025
1b5df9e
Display errors when is not possible to launch new calls
VZaphod Jul 1, 2025
d8f6833
Removed implicit unwrappings from `VonageManager`
VZaphod Jul 1, 2025
a21bb73
Credential checks before notifiying calls to the telecomManager
VZaphod Jul 1, 2025
d6368ba
Supress deprecations
VZaphod Jul 1, 2025
2521c72
Referencing the latest OpenTok version from the toml file
VZaphod Nov 3, 2025
26c4243
Upgraded OpenTok version to 2.31.1
VZaphod Nov 4, 2025
4455aef
Merge branch 'main' into VIDCS-3882
VZaphod Nov 4, 2025
115bec8
Updated app_name literal
VZaphod Nov 5, 2025
0f73bee
Changed package from com.vonage to com.tokbox
VZaphod Nov 5, 2025
6c06d0a
Changed package domain
VZaphod Nov 5, 2025
828ebd9
Added empty Readme
VZaphod Nov 5, 2025
1a491bb
Updated top level readme
VZaphod Nov 5, 2025
048912c
Added package declaration
VZaphod Nov 5, 2025
50fdeef
Migrated build.gradle files from kts to groovy
VZaphod Nov 5, 2025
e7568a4
Changed quotes of applicationId
VZaphod Nov 5, 2025
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: Build Basic-Video-Chat-ConnectionService-Kotlin

on:
push:
branches: [main] # Just in case main was not up to date while merging PR
pull_request:
types: [opened, synchronize]

jobs:
run:
continue-on-error: true
runs-on: ubuntu-latest
strategy:
fail-fast: false
steps:
- name: checkout
uses: actions/checkout@v2

- name: Set up JDK
uses: actions/setup-java@v1
with:
java-version: 17

- name: Build
run: cd Basic-Video-Chat-ConnectionService-Kotlin && ./gradlew app:assembleRelease && cd ..
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ jobs:
uses: actions/setup-java@v1
with:
java-version: 17

- name: Build
run: cd Basic-Video-Chat-With-ForegroundServices-Java && ./gradlew app:assembleRelease && cd ..

18 changes: 18 additions & 0 deletions Basic-Video-Chat-ConnectionService-Kotlin/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
*.iml
.gradle
/local.properties
/.idea/caches
/.idea/libraries
/.idea/modules.xml
/.idea/workspace.xml
/.idea/navEditor.xml
/.idea/assetWizardSettings.xml
.DS_Store
/captures
.externalNativeBuild
.cxx
local.properties
app/build

.settings/
app/jniLibs/
Empty file.
1 change: 1 addition & 0 deletions Basic-Video-Chat-ConnectionService-Kotlin/app/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
Comment thread
VZaphod marked this conversation as resolved.
Resolved
67 changes: 67 additions & 0 deletions Basic-Video-Chat-ConnectionService-Kotlin/app/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
plugins {
alias(libs.plugins.android.application)
alias(libs.plugins.kotlin.android)
alias(libs.plugins.kotlin.compose)
alias(libs.plugins.google.dagger.hilt)
alias(libs.plugins.ksp)
}

apply from: '../../commons.gradle'

android {
namespace 'com.tokbox.sample.basicvideochatconnectionservice'
compileSdk 36

defaultConfig {
applicationId "com.tokbox.sample.basicvideochatconnectionservice"
minSdk 24
targetSdk 36
versionCode 1
versionName '1.0'
testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner'
}

buildTypes {
release {
minifyEnabled false
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_11
targetCompatibility JavaVersion.VERSION_11
}

kotlinOptions {
jvmTarget = '11'
}

buildFeatures {
compose true
}
}

def extOpentokSdkVersion = ext.extOpentokSdkVersion

dependencies {
implementation "com.opentok.android:opentok-android-sdk:${extOpentokSdkVersion}"

implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.ui)
implementation(libs.androidx.ui.graphics)
implementation(libs.androidx.ui.tooling.preview)
implementation(libs.androidx.material3)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
androidTestImplementation(platform(libs.androidx.compose.bom))
androidTestImplementation(libs.androidx.ui.test.junit4)
debugImplementation(libs.androidx.ui.tooling)
debugImplementation(libs.androidx.ui.test.manifest)

implementation(libs.hilt.android)
ksp(libs.hilt.compiler)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.tokbox.sample.basicvideochatconnectionservice">

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.USE_FULL_SCREEN_INTENT" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS"/>
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.CALL_PHONE" />
<uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />
<uses-permission android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_PHONE_CALL" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
Comment thread
VZaphod marked this conversation as resolved.
Resolved

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" android:required="false" />
<uses-feature android:name="android.hardware.telephony" android:required="false" />

<application
android:name=".MyApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.BasicVideoChatConnectionServiceKotlin">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.BasicVideoChatConnectionServiceKotlin">
<intent-filter>
<action android:name="android.intent.action.MAIN" />

<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

<meta-data
android:name="android.telecom.connectionService"
android:value="com.tokbox.sample.basicvideochatconnectionservice.connectionservice.VonageConnectionService">
</meta-data>

<service
android:name=".connectionservice.VonageConnectionService"
android:exported="true"
android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"
android:foregroundServiceType="microphone|camera">
<intent-filter>
<action android:name="android.telecom.ConnectionService" />
</intent-filter>
</service>

<receiver android:name=".CallActionReceiver"
android:exported="true">
<intent-filter>
<action android:name="com.tokbox.sample.basicvideochatconnectionservice.ACTION_ANSWER_CALL" />
<action android:name="com.tokbox.sample.basicvideochatconnectionservice.ACTION_REJECT_CALL" />
<action android:name="com.tokbox.sample.basicvideochatconnectionservice.ACTION_END_CALL" />
</intent-filter>
</receiver>
</application>

</manifest>
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.tokbox.sample.basicvideochatconnectionservice

data class Call(
val callID: Int,
val name: String,
val state: CallState
)

enum class CallState {
IDLE,
CONNECTING,
DIALING,
ANSWERING,
CONNECTED,
HOLDING,
DISCONNECTED
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.tokbox.sample.basicvideochatconnectionservice

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.util.Log
import com.tokbox.sample.basicvideochatconnectionservice.connectionservice.VonageConnectionHolder

class CallActionReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent?) {
if (intent != null && intent.action != null) {
val action = intent.action
when (action) {
ACTION_ANSWER_CALL -> {
Log.d(TAG, "Action: Answer call")
answerCall()
}

ACTION_REJECT_CALL -> {
Log.d(TAG, "Action: Reject call")
rejectCall()
}

ACTION_END_CALL -> {
Log.d(TAG, "Action: End call")
endCall()
}

else -> Log.w(
TAG,
"Unknown action: $action"
)
}
}
}

private fun answerCall() {
VonageConnectionHolder.connection?.onAnswer()
}

private fun rejectCall() {
VonageConnectionHolder.connection?.onReject()
}

private fun endCall() {
VonageConnectionHolder.connection?.onDisconnect()
}

companion object {
private const val TAG = "CallActionReceiver"

const val ACTION_ANSWER_CALL: String =
"com.tokbox.sample.basicvideochatconnectionservice.ACTION_ANSWER_CALL"
const val ACTION_REJECT_CALL: String =
"com.tokbox.sample.basicvideochatconnectionservice.ACTION_REJECT_CALL"
const val ACTION_END_CALL: String =
"com.tokbox.sample.basicvideochatconnectionservice.ACTION_END_CALL"

const val ACTION_ANSWER_CALL_ID: Int = 2
const val ACTION_REJECT_CALL_ID: Int = 3
const val ACTION_END_CALL_ID: Int = 4
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package com.tokbox.sample.basicvideochatconnectionservice

class CallException(message: String, val code: Int = 0) : Exception(message)
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.tokbox.sample.basicvideochatconnectionservice

import android.view.View
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

class CallHolder {
private val _callFlow = MutableStateFlow<Call?>(null)
val callFlow: Flow<Call?> = _callFlow.asStateFlow()

private val _callStateFlow = MutableStateFlow(CallState.IDLE)
val callStateFlow: Flow<CallState> = _callStateFlow.asStateFlow()

private val subscriberMap = mutableMapOf<String, View>()

private val _publisherViewFlow = MutableStateFlow<View?>(null)
val publisherViewFlow: Flow<View?> = _publisherViewFlow.asStateFlow()

private val _subscriberViewFlow = MutableStateFlow<List<View>>(emptyList())
val subscriberViewFlow: Flow<List<View>> = _subscriberViewFlow.asStateFlow()

fun setCall(call: Call?) {
_callFlow.value = call
}

fun updateCallState(state: CallState) {
_callStateFlow.value = state
}

fun setPublisherView(view: View?) {
_publisherViewFlow.value = view
}

fun addSubscriberView(streamId: String, view: View) {
subscriberMap[streamId] = view

_subscriberViewFlow.value = subscriberMap.values.toList()
}

fun removeSubscriberView(streamId: String) {
subscriberMap.remove(streamId)

_subscriberViewFlow.value = subscriberMap.values.toList()
}

fun clear() {
_callFlow.value = null
_callStateFlow.value = CallState.IDLE
subscriberMap.clear()
_publisherViewFlow.value = null
_subscriberViewFlow.value = emptyList()
}
}
Loading