ECV Video Görüşme Android SDK [TR]
Giriş
ECV Görüntülü Görüşme Android SDK'sına hoş geldiniz! Bu SDK, Android uygulamalarınıza real-time görüntülü görüşme işlevselliğini kolayca entegre etmenizi sağlar. Signaling, WebRTC peer-to-peer bağlantıları, media streams ve çağrı state yönetiminin karmaşıklığını ele alarak uygulamanızın user interface (UI) ve iş mantığına odaklanmanıza olanak tanır.
Bu repository şunları içerir:
-
SDK: Önceden compile edilmiş bir Android Library (
.aardosyası) olarak sağlanır. Bunu projenize bir dependency olarak ekleyeceksiniz. -
Demo Uygulama: SDK feature'larının nasıl kullanılacağını gösteren bir tanıtım uygulamasının (
ecv175_android) tam kaynak kodu. Bu demo, production'a hazır bir UI kodu değil, bir referans ve örnek olarak tasarlanmıştır.
Temel Özellikler
-
ECV backend services (v1.75+) bağlantısı kurar.
-
WebRTC kurulumunu ve peer-to-peer bağlantıyı yönetir.
-
Yerel ve uzak video/ses media streams yönetir.
-
Ses ve videoyu sessize alma/açma için kontroller sağlar.
-
Kamera değiştirmeyi destekler.
-
Çağrı lifecycle Events (dialing, queuing, connected, hold, ended) yönetir.
-
Yapılandırılabilir environment'lar ve çağrı parameters.
Başlarken
Ön Koşullar
-
Android Studio (En Son Kararlı Sürüm Önerilir)
- Temel Android geliştirme bilgisi (Kotlin veya Java).
-
Minimum Android SDK Sürümü: 21 (SDK'nızın
minSdkdeğeriyle eşleştiğini onaylayın) -
Hedef Android SDK Sürümü: 35 (SDK'nızın
targetSdkdeğeriyle eşleştiğini onaylayın)
1. Gerekli Library'leri Ekleme
Uygulamanızın ECV SDK library'sini ve gerekli dependency'lerini içermesi gerekir: WebRTC ve Autobahn. Bunları .aar dosyaları olarak alacaksınız.
-
Gerekli AAR Dosyaları:
-
ECV SDK: (örn.
ECV-175-SDK-vX.Y.Z.aar) - Gerçek SDK dosya adınızla değiştirin. -
WebRTC:
M134-libwebrtc.aar -
Autobahn:
autobahn-android-legacy-20.2.1.aar
-
-
Üç
.aardosyasını da projenizinlibsdizinine kopyalayın (appmodülü altında yoksa oluşturun). -
Uygulama düzeyindeki
build.gradle(veyabuild.gradle.kts) dosyanızı açın. -
libsdizinini, eğer zaten ekli değilse, repositories kısmına ekleyin:
-
// build.gradle (Groovy) repositories { google() mavenCentral() flatDir { dirs 'libs' // Bu satırı ekleyin } // Gerekirse diğer özel repoları ekleyin } // build.gradle.kts (Kotlin) repositories { google() mavenCentral() flatDir { dirs("libs") // Bu satırı ekleyin } // Gerekirse diğer özel repoları ekleyin } -
AAR dosyalarını dependency olarak ekleyin:
// build.gradle (Groovy) dependencies { // ... diğer dependency'ler // --- ECV SDK ve Dependency'leri --- implementation(name: 'ECV-175-SDK-vX.Y.Z', ext: 'aar') // SDK dosya adınızla değiştirin implementation(name: 'M134-libwebrtc', ext: 'aar') implementation(name: 'autobahn-android-legacy-20.2.1', ext: 'aar') // android 21 uyumluluk için implementation 'net.sourceforge.streamsupport:streamsupport-cfuture:1.7.4' // eğer streamsupport-cfuture kullanılamaz ise onun yerine // compileOptions içerisinde `coreLibraryDesugaringEnabled true` yapılıp // aşağıdaki satır açılmalıdır // coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.4' // --- ECV SDK Dependency'leri Sonu --- // Ayrıca lifecycle-runtime-ktx'e de ihtiyacınız olabilir (aşağıya bakın) } // build.gradle.kts (Kotlin) dependencies { // ... diğer dependency'ler // --- ECV SDK ve Dependency'leri --- implementation(name = "ECV-175-SDK-vX.Y.Z", ext = "aar") // SDK dosya adınızla değiştirin implementation(name = "M134-libwebrtc", ext = "aar") implementation(name = "autobahn-android-legacy-20.2.1", ext = "aar") // android 21 uyumluluk için implementation("net.sourceforge.streamsupport:streamsupport-cfuture:1.7.4") // eğer streamsupport-cfuture kullanılamaz ise onun yerine // compileOptions içerisinde `coreLibraryDesugaringEnabled(true)` eklenip // aşağıdaki satır açılmalıdır // coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.0.4") // --- ECV SDK Dependency'leri Sonu --- // Ayrıca lifecycle-runtime-ktx'e de ihtiyacınız olabilir (aşağıya bakın) } -
Lifecycle Runtime Dependency Ekleme (Gerekirse):
ECV SDK, dahili olarakandroidx.lifecycle:lifecycle-runtime-ktxlibrary'sini (implementationdependency) kullanır. Bu birapidependency olmadığından, kullandığınız diğer library'ler (Compose, Navigation vb.) tarafından zaten dolaylı olarak dahil edilmiyorsa uygulamanızın bunu sağlaması gerekir.Bu dependency'nin uygulama düzeyindeki
build.gradle/build.gradle.ktsdosyanızda bulunduğundan emin olun:// build.gradle (Groovy) dependencies { // ... diğer dependency'ler implementation libs.androidx.lifecycle.runtime.ktx // Veya belirli version string'i, örn. "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0" // ECV SDK AAR'ları... } // build.gradle.kts (Kotlin) dependencies { // ... diğer dependency'ler implementation(libs.androidx.lifecycle.runtime.ktx) // Veya belirli version string'i, örn. "androidx.lifecycle:lifecycle-runtime-ktx:2.7.0" // ECV SDK AAR'ları... }Not: Version catalog (
libs.) kullanmıyorsanız,libs.androidx.lifecycle.runtime.ktxifadesini"androidx.lifecycle:lifecycle-runtime-ktx:2.7.0"gibi açık coordinate string'iyle değiştirin (projenizle uyumlu en son kararlı version'u kullanın). -
Gradle projenizi senkronize edin (
File > Sync Project with Gradle Files).
2. Demo Uygulamayı Keşfetme
Dahil edilen demo uygulama (ecv175_android), SDK entegrasyonunun pratik bir örneğini sunar.
-
ecv175_androidprojesini Android Studio'da açın. -
Demo uygulamayı bir emulator'de veya fiziksel cihazda build edip çalıştırın.
-
SDK component'lerinin nasıl initialize edildiğini ve kullanıldığını anlamak için kaynak kodunu, özellikle
uipackage'ındaki dosyaları (EnvironmentSelectorScreen.kt,MainScreen.kt,QueueScreen.kt,CallScreen.kt) veAppState.kt(veya eşdeğeri) object'ini inceleyin.
3. Temel SDK Başlatma
// Application class'ınızda veya merkezi bir yönetim object'inde bir yerde
import group.ccr.ecv.sdk.ECVSdk
import group.ccr.ecv.sdk.ECVSdkConfiguration
// 1. SDK Configuration Tanımlayın (Detaylar için Yapilandirma.md'ye bakın)
val sdkConfig = ECVSdkConfiguration(
serviceUrl = "SİZİN_SERVİS_URL_ADRESİNİZ", // Gerçek service URL'nizle değiştirin
// İsteğe bağlı olarak webrtcUrl, signalingUrl vb. override edin.
)
// 2. SDK Instance'ını Başlatın
// Merkezi olarak yönetilen tek bir instance'a sahip olmanız önerilir
val ecvSdk = ECVSdk(instanceName = "MyAppSdkInstance", config = sdkConfig)
// Bu instance'ı uygulamanız boyunca erişim için saklayın (örn. Application class, DI, Singleton)
// Örnek: MyApp.sdkInstance = ecvSdk
// Application class'ınızda veya merkezi bir yönetim object'inde bir yerde
import group.ccr.ecv.sdk.ECVSdk;
import group.ccr.ecv.sdk.ECVSdkConfiguration;
// 1. SDK Configuration Tanımlayın (Detaylar için Yapilandirma.md'ye bakın)
ECVSdkConfiguration sdkConfig = new ECVSdkConfiguration(
/* serviceUrl */ "SİZİN_SERVİS_URL_ADRESİNİZ", // Gerçek service URL'nizle değiştirin
);
// 2. SDK Instance'ını Başlatın
// Merkezi olarak yönetilen tek bir instance'a sahip olmanız önerilir
ECVSdk ecvSdk = new ECVSdk("MyAppSdkInstance", sdkConfig);
// Bu instance'ı uygulamanız boyunca erişim için saklayın (örn. Application class, DI, Singleton)
// Örnek: MyApp.sdkInstance = ecvSdk;
Temel Kavramlar
-
ECVSdk: SDK ile etkileşim için ana giriş noktası. Çağrı oluşturmak ve genel ayarlara/modules erişmek için kullanılır. -
ECVSdkConfiguration: SDK için backend URL'lerini ve bağlantı parameters tanımlar. -
VideoCall: Tek bir görüntülü görüşme session'ını temsil eder. Çağrı state'ini, Events'i ve bağlantı ayrıntılarını yönetir. -
WebRtcConnection: Temeldeki WebRTC peer connection'ını, media streams ve Renderers yönetir.VideoCall.connect()aracılığıyla erişilir. -
Events: SDK, çağrı state değişikliklerini, hataları ve medya olaylarını (
CallEventTypes) uygulamanıza bildirmek için bir flow/callback sistemi (VideoCall.eventFlow/VideoCall.subscribe) kullanır. -
Renderers (
SurfaceViewRenderer): Yerel ve uzak videoyu görüntülemek için kullanılan standart WebRTC view'ları. Lifecycle'larını yönetmeniz veWebRtcConnectionkullanarak attach/detach etmeniz gerekir.
Demo Uygulama Akışı
Demo uygulama şu temel flow'u izler:
-
EnvironmentSelectorScreen: Backend bağlantı configurations (Service URL'si vb.) seçmeye veya tanımlamaya olanak tanır. Bu öncelikle tanıtım amaçlıdır; uygulamanız muhtemelen sabit bir configuration sahip olacak veya dinamik olarak alacaktır. -
MainScreen: Gerekli çağrı bilgilerini (örn. arayan adı, attributes) toplar, permissions (Kamera, Mikrofon) ister vesdk.createCall()vecall.start()kullanarak çağrıyı başlatır. -
QueueScreen: Çağrı bir agent'a bağlanırken bekleme durumunu gösterir.AgentConnectedveyaError/HangupEvents dinler. -
CallScreen: Ana görüntülü görüşme interface'i. Yerel ve uzak video içinSurfaceViewRenderer'ları yönetir, çağrı kontrollerini (sessize alma, kamera değiştirme, kapatma) gösterir ve UI'yi güncellemek için SDK Events tepki verir.
Kullanım Kılavuzu
Bu kılavuz, ECV Android SDK'sının temel component'lerinin kullanımı hakkında ayrıntılı bilgi sağlar.
1. Initialization (ECVSdk)
ECVSdk class'ı, SDK için birincil giriş noktasıdır. Genellikle uygulamanızın lifecycle'ı için tek bir instance oluşturursunuz.
Instance Oluşturma:
import group.ccr.ecv.sdk.ECVSdk
import group.ccr.ecv.sdk.ECVSdkConfiguration
// Configuration (Detaylar için Yapilandirma.md'ye bakın)
val sdkConfig = ECVSdkConfiguration(
serviceUrl = "SİZİN_SERVİS_URL_ADRESİNİZ",
)
// SDK Instance'ı Oluşturma
// Loglama/debug amacıyla açıklayıcı bir ad kullanın
val sdkInstance = ECVSdk(instanceName = "MyAppSdkInstance", config = sdkConfig)
// --- SDK Instance'ına Erişme ---
// Bu instance'ı uygun şekilde yönetmek çok önemlidir. Yaygın patterns:
// 1. Application alt class'ınızda saklayın:
// class MyApp : Application() {
// lateinit var sdkInstance: ECVSdk
// override fun onCreate() {
// super.onCreate()
// sdkInstance = ECVSdk(...)
// }
// }
// Erişim: (applicationContext as MyApp).sdkInstance
// 2. Dependency Injection (Hilt, Koin, vb.): ECVSdk'yı singleton olarak sağlayın.
// 3. Basit Singleton Object (Kotlin):
// object SdkManager {
// lateinit var instance: ECVSdk
// fun initialize(config: ECVSdkConfiguration) {
// instance = ECVSdk("MyAppSdkInstance", config)
// }
// }
// Erişim: SdkManager.instance
import group.ccr.ecv.sdk.ECVSdk;
import group.ccr.ecv.sdk.ECVSdkConfiguration;
import android.app.Application;
import android.content.Context;
import android.util.Log;
// Configuration (Detaylar için Yapilandirma.md'ye bakın)
ECVSdkConfiguration sdkConfig = new ECVSdkConfiguration(
/* serviceUrl */ "SİZİN_SERVİS_URL_ADRESİNİZ",
// ... webrtcUrl, signalingUrl gibi diğer parameters ...
);
// SDK Instance'ı Oluşturma
// Loglama/debug amacıyla açıklayıcı bir ad kullanın
ECVSdk sdkInstance = new ECVSdk("MyAppSdkInstance", sdkConfig);
// --- SDK Instance'ına Erişme ---
// Bu instance'ı uygun şekilde yönetmek çok önemlidir. Yaygın patterns:
// 1. Application alt class'ınızda saklayın:
// public class MyApp extends Application {
// private ECVSdk sdkInstance;
// @Override
// public void onCreate() {
// super.onCreate();
// ECVSdkConfiguration config = new ECVSdkConfiguration(...);
// sdkInstance = new ECVSdk("MyAppSdkInstance", config);
// }
// public ECVSdk getSdkInstance() {
// return sdkInstance;
// }
// }
// Erişim: ((MyApp) getApplicationContext()).getSdkInstance();
// 2. Dependency Injection (Dagger, Hilt): ECVSdk'yı singleton olarak sağlayın.
// 3. Basit Singleton Sınıfı (Java):
// public class SdkManager {
// private static ECVSdk instance;
// public static synchronized void initialize(Context context, ECVSdkConfiguration config) { // Context eklendi
// if (instance == null) {
// try {
// instance = new ECVSdk("MyAppSdkInstance", config);
// Log.i("SdkManager", "SDK Initialized");
// } catch (Exception e) {
// Log.e("SdkManager", "SDK Initialization failed", e);
// }
// }
// }
// public static ECVSdk getInstance() {
// if (instance == null) {
// throw new IllegalStateException("SdkManager not initialized. Call initialize first.");
// }
// return instance;
// }
// }
// Erişim: SdkManager.getInstance();
Temel Components:
-
config: Initialization sırasında iletilenECVSdkConfiguration. -
logger: Dahili bir logger instance'ı. Log'lar genellikle Android'in Logcat'inde görüntülenebilir. -
modules: SDK modules erişim sağlar (şu anda event subscription'a odaklanmıştır).
2. Çağrıları Yönetme (VideoCall)
Bir VideoCall object'i, başlatmadan sonlandırmaya kadar tek bir çağrı session'ını temsil eder.
Çağrı Oluşturma:
// sdkInstance'ın kullanılabilir olduğu varsayılır
val videoCall: VideoCall = sdkInstance.createCall() // API call yoluyla rtcId oluşturur
// VEYA test/bilinen bir rtcId ile katılma için:
// val testVideoCall: VideoCall = sdkInstance.createTestCall() // rtcId'yi otomatik olarak oluşturmaz
// testVideoCall.setRtcId("MEVCUT_RTC_ID")
// sdkInstance'ın kullanılabilir olduğu varsayılır
VideoCall videoCall = sdkInstance.createCall(); // API call yoluyla rtcId oluşturur
// VEYA test/bilinen bir rtcId ile katılma için:
// VideoCall testVideoCall = sdkInstance.createTestCall(); // rtcId'yi otomatik olarak oluşturmaz
// testVideoCall.setRtcId("MEVCUT_RTC_ID");
Çağrı Lifecycle & Anahtar Method'lar:
-
Initialization:
createCall()çağrıldığında, SDK backend'den eşzamansız olarak birrtcIdalır.GotRtcIdevent'ini dinlemelisiniz.rtcId, çağrıyı tanımlamak için gereklidir.-
videoCall.rtcId: Benzersiz ID'ye erişin (GotRtcIdevent'inden sonra kullanılabilir).
-
-
Dialing (
start): Çağrı parameters ile backend'le iletişim kurarak çağrı process'ini başlatır. Bu genellikle arayanı bir queue'ya koyar.-
videoCall.start(dialParameters: DialCallParameters):DialCallParametersiçinYapilandirma.mdye bakın. -
DialSuccessveyaErrorevents dinleyin. -
OnQueueevent'i, çağrının bir agent beklediğini gösterir.
-
-
Agent Bağlantısı:
-
AgentConnectedevent'i, bir agent çağrıyı kabul ettiğinde tetiklenir. -
videoCall.agentConnectedState: Agent'ın bağlı olup olmadığını gösteren Boolean flag'i.
-
-
WebRTC Bağlantısı (
connect):AgentConnectedsonrasında, peer-to-peer media bağlantısını kurarsınız.-
videoCall.connect(parameters: ConnectParameters? = null): WebRtcConnection:WebRtcConnectionobject'ini oluşturur veya alır.ConnectParametersiçinYapilandirma.mdye bakın.
-
-
Medyayı Başlatma (
startRoom):WebRtcConnection'a sahip olduğunuzda, WebRTC setup process'ini başlatmanız gerekir.-
webRtcConnection.startRoom(context: Context): PeerConnection'ı başlatır, media tracks ayarlar ve signaling process'ini başlatır.
-
-
Medya Kontrolü:
WebRtcConnectioninstance'ındaki method'ları kullanın (sonraki bölüme bakın). -
Çağrıyı Sonlandırma (
hangup): Çağrı session'ını sonlandırır ve resources temizler.-
videoCall.hangup(): Kapatma process'ini başlatır. Bu, user kapattığında veya çağrı başka nedenlerle (örn. uzaktan kapatma, error) sona erdiğinde çağrılmalıdır. -
Hangupevent'ini (hangup()çağrıldığında tetiklenir) veyaCallEndedevent'ini (genellikle uzak taraf veya errors tarafından tetiklenir) dinleyin.
-
Mevcut Bir Çağrıyı Bulma:
Çağrı ekranından uzaklaşırsanız ve daha sonra VideoCall instance'ını almanız gerekirse (örn. QueueScreen veya CallScreen'de), rtcId'yi kullanın:
val rtcId: String = // mevcut çağrı için rtcId'yi alın ...
val existingCall = sdkInstance.getVideoCall(rtcId)
if (existingCall != null) {
// Çağrı instance'ını kullanın
} else {
// Çağrı bulunamadı, uygun şekilde ele alın (örn. geri gidin)
}
String rtcId = // mevcut çağrı için rtcId'yi alın ...
VideoCall existingCall = sdkInstance.getVideoCall(rtcId);
if (existingCall != null) {
// Çağrı instance'ını kullanın
} else {
// Çağrı bulunamadı, uygun şekilde ele alın (örn. geri gidin)
}
Önemli: SDK, dahili olarak rtcId kullanarak VideoCall instance'larına bir reference tutar. hangup() çağrılması, sonunda bu reference'ın temizlenmesine ve potansiyel olarak kaldırılmasına yol açacaktır.
3. WebRTC Connection (WebRtcConnection)
Bu class, PeerConnection, media tracks ve Renderers dahil olmak üzere düşük seviyeli WebRTC ayrıntılarını yönetir. videoCall.connect() aracılığıyla elde edersiniz.
Anahtar Method'lar:
-
startRoom(context: Context): (Yukarıda belirtildiği gibi)AgentConnectedsonrasında WebRTC bağlantı setup'ını başlatır.Contextgerektirir. -
eglBase: EglBase:SurfaceViewRenderer'ları initialize etmek için gereken EGL context'ini sağlar. -
attachLocalView(renderer: SurfaceViewRenderer): Yerel kamera feed'ini görüntülemek için bir renderer ekler. -
attachRemoteView(renderer: SurfaceViewRenderer): Uzak katılımcının video feed'ini görüntülemek için bir renderer ekler. -
detachLocalView(renderer: SurfaceViewRenderer): Yerel feed'den bir renderer çıkarır. -
detachRemoteView(renderer: SurfaceViewRenderer): Uzak feed'den bir renderer çıkarır. -
setAudioEnabled(state: Boolean): Yerel ses çıkış stream'ini sessize alır veya açar.AudioMuted/AudioUnmutedevents tetikler. -
setVideoEnabled(state: Boolean): Yerel video stream'ini göndermeyi durdurur veya başlatır.VideoMuted/VideoUnmutedevents tetikler. -
switchCamera(targetCamera: String? = null): Ön ("f") ve arka ("b") kameralar arasında geçiş yapar.targetCameranull ise, geçiş yapar. changeFlashState(state: Boolean) v1.0.18.45+: True gönderildiğinde flash aktifleştirir, false gönderildiğinde kapatır. Sadece arka kamerada çalışır.toggleFlashState() v1.0.18.45+: Flash statüsü açık/kapalı arasında geçiş yapar. Sadece arka kamerada çalışır.
Renderer Lifecycle Management (Kritik):
SurfaceViewRenderer, özellikle AndroidView kullanan Jetpack Compose'da dikkatli lifecycle management gerektirir.
-
Initialization:
AndroidView'ınfactorylambda'sı çalıştığında,SurfaceViewRenderer'ı oluşturun vewebRtcConnection.eglBase.eglBaseContextkullanarak initialize edin. -
Attachment: Renderer'ı
factoryveyaupdatelambda'sı içindewebRtcConnection.attachLocalView()veyawebRtcConnection.attachRemoteView()kullanarak yerel veya uzak stream'e attach edin. -
Detachment: Hedef stream değişirse (örn. PiP'de yerel/uzak arasında geçiş yaparken)
updatelambda'sında renderer'ı detach edin. -
Release: Kritik olarak, resources serbest bırakmak ve leak'leri önlemek için
AndroidView'ınonReleaselambda'sında renderer'ı detach edin (detachLocalView/detachRemoteView) vesurfaceViewRenderer.release()çağırın.
Somut bir örnek için demo uygulamadaki CallScreen.kt -> VideoRenderer composable'ına bakın.
// AndroidView bloğu içinde basitleştirilmiş örnek
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.viewinterop.AndroidView
import androidx.compose.ui.graphics.Color
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.background
import androidx.compose.material3.Text // Veya androidx.compose.material.Text
import android.util.Log
import group.ccr.ecv.sdk.webRtc.WebRtcConnection // Import path'i doğrulayın
import org.webrtc.RendererCommon
import org.webrtc.SurfaceViewRenderer
// webRtcConnection ve isLocalView'in composable scope'unda mevcut olduğu varsayılır
@Composable
fun VideoDisplay(webRtcConnection: WebRtcConnection?, isLocalView: Boolean, modifier: Modifier = Modifier) {
if (webRtcConnection == null) {
// Connection hazır olmadığında durumu ele alın
Box(modifier.background(Color.Gray)) { Text("Connecting...") }
return
}
AndroidView(
modifier = modifier,
factory = { ctx ->
SurfaceViewRenderer(ctx).apply {
// null check factory içinde de yapılabilir
webRtcConnection?.eglBase?.eglBaseContext?.let { eglContext ->
init(eglContext, null)
setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL)
} ?: Log.e("VideoDisplay", "EGL Context was null in factory (isLocal: $isLocalView)")
}
},
update = { surfaceViewRenderer ->
// Yerel mi yoksa uzak mı ekleneceğine karar verme mantığı
webRtcConnection?.let { conn -> // Null check
surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL) // Gerekirse yeniden uygulayın
if (isLocalView) {
conn.detachRemoteView(surfaceViewRenderer) // Önce temizleyin
conn.attachLocalView(surfaceViewRenderer)
} else {
conn.detachLocalView(surfaceViewRenderer) // Önce temizleyin
conn.attachRemoteView(surfaceViewRenderer)
}
} ?: Log.w("VideoDisplay", "webRtcConnection was null in update (isLocal: $isLocalView)")
},
onRelease = { surfaceViewRenderer ->
// RELEASE ÖNCESİ DETACH ESASTIR
Log.d("VideoRenderer", "Releasing SurfaceViewRenderer (isLocal: $isLocalView)")
webRtcConnection?.let { conn ->
conn.detachLocalView(surfaceViewRenderer)
conn.detachRemoteView(surfaceViewRenderer)
}
surfaceViewRenderer.release() // WebRTC resources serbest bırakın
}
)
}
// Android Fragment veya Activity içinde basitleştirilmiş örnek
// webRtcConnection ve surfaceViewRenderer'ın uygun şekilde initialize edilmiş member variables olduğu varsayılır
import org.webrtc.RendererCommon;
import org.webrtc.SurfaceViewRenderer;
import group.ccr.ecv.sdk.webRtc.WebRtcConnection; // Import path'i doğrulayın
// onCreateView veya onViewCreated içinde:
// SurfaceViewRenderer surfaceViewRenderer = binding.surfaceView; // ViewBinding'den
// if (webRtcConnection != null && webRtcConnection.getEglBase() != null && webRtcConnection.getEglBase().getEglBaseContext() != null) {
// surfaceViewRenderer.init(webRtcConnection.getEglBase().getEglBaseContext(), null);
// surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FILL);
// if (isLocalView) { // isLocalView UI mantığı tarafından belirlenir
// webRtcConnection.attachLocalView(surfaceViewRenderer);
// } else {
// webRtcConnection.attachRemoteView(surfaceViewRenderer);
// }
// } else {
// // Log error: Connection or EGL Context is null
// }
// onDestroyView içinde:
// if (webRtcConnection != null && surfaceViewRenderer != null) {
// // Hem local hem de remote için detach çağrılmalı, hangisinin attach edildiğini bilmeseniz bile güvenlidir
// webRtcConnection.detachLocalView(surfaceViewRenderer);
// webRtcConnection.detachRemoteView(surfaceViewRenderer);
// surfaceViewRenderer.release();
// }
4. Event Handling
SDK, state değişikliklerini ve olayları eşzamansız olarak Events aracılığıyla iletir.
Events Abone Olma:
-
Kotlin (Flow): Coroutines ve
collectkullanarakvideoCall.eventFlowüzerinde toplayın. Bu,LaunchedEffectveyalifecycleScope.launch/repeatOnLifecycleile kullanıldığında lifecycle-aware'dir.import androidx.compose.runtime.LaunchedEffect import androidx.lifecycle.Lifecycle import androidx.lifecycle.lifecycleScope import androidx.lifecycle.repeatOnLifecycle import group.ccr.ecv.sdk.videoCall.VideoCall // VideoCall class'ınızı import edin import group.ccr.ecv.sdk.videoCall.events.CallEventTypes import kotlinx.coroutines.flow.collect import android.util.Log // Bir Composable içinde (LaunchedEffect kullanarak) @Composable fun MyCallScreen(videoCall: VideoCall?) { // VideoCall instance'ını iletin LaunchedEffect(videoCall) { // videoCall instance'ına key'lenir videoCall?.eventFlow?.collect { event -> Log.d("MyApp", "SDK Event Alındı: ${event.type} - ${event.detail}") when (event.type) { CallEventTypes.GotRtcId -> { /* rtcId'yi saklayın: event.detail */ } CallEventTypes.DialSuccess -> { /* Çağrı aranıyor */ } CallEventTypes.OnQueue -> { /* Temsilci bekleniyor */ } CallEventTypes.AgentConnected -> { /* Çağrı ekranına gidin, videoCall.connect() çağırın */ } CallEventTypes.WebRtcConnected -> { /* Media bağlantısı kuruldu */ } CallEventTypes.VideoMuted -> { /* Yerel video sessize alma UI state'ini güncelleyin */ } CallEventTypes.RemoteVideoMuted -> { /* Uzak video sessize alma UI state'ini güncelleyin */ } CallEventTypes.StartHold -> { /* "Beklemede" UI'sini gösterin */ } CallEventTypes.CallEnded -> { /* Geri gidin, temizleyin. Detay: event.detail */ } CallEventTypes.Error -> { /* Hata mesajı gösterin: event.error / event.detail */ } // ... diğer ilgili events'i ele alın else -> { /* İsteğe bağlı: Ele alınmayan events'i loglayın */ } } } } // ... Composable UI'nin geri kalanı ... } // Bir Activity/Fragment içinde (lifecycleScope kullanarak) // override fun onCreate(savedInstanceState: Bundle?) { // super.onCreate(savedInstanceState) // // ... setup ... // val videoCall = // ... VideoCall instance'ını alın ... // // lifecycleScope.launch { // repeatOnLifecycle(Lifecycle.State.STARTED) { // videoCall?.eventFlow?.collect { event -> // // Event'i yukarıdaki gibi ele alın // } // } // } // } -
Java (Callback):
subscribemethod'unu birLifecycleOwnerve birEventCallbackile kullanın.import androidx.lifecycle.LifecycleOwner; // örn. Activity veya Fragment'ınız import group.ccr.ecv.sdk.EventCallback; import group.ccr.ecv.sdk.videoCall.VideoCall; import group.ccr.ecv.sdk.videoCall.events.CallEventTypes; import group.ccr.ecv.sdk.videoCall.events.VideoCallEvent; import android.util.Log; // Activity onCreate veya Fragment onViewCreated içinde LifecycleOwner lifecycleOwner = this; // Activity/Fragment'ınız LifecycleOwner uygular VideoCall videoCall = // ... videoCall instance'ını alın ... if (videoCall != null) { videoCall.subscribe(lifecycleOwner, new EventCallback() { @Override public void onEvent(VideoCallEvent event) { // UI güncellemelerini main thread'de yapmak genellikle daha güvenlidir runOnUiThread(() -> { Log.d("MyApp", "SDK Event Alındı: " + event.getType() + " - " + event.getDetail()); // event.getType() üzerinde switch ifadesi veya if-else if kullanın if (event.getType() == CallEventTypes.GotRtcId) { // rtcId'yi saklayın: event.getDetail() } else if (event.getType() == CallEventTypes.AgentConnected) { // Çağrı ekranına gidin, videoCall.connect() çağırın } else if (event.getType() == CallEventTypes.CallEnded) { // Geri gidin, temizleyin. Detay: event.getDetail() } else if (event.getType() == CallEventTypes.Error) { // Hata mesajı gösterin: event.getError() / event.getDetail() } // ... diğer ilgili events'i ele alın }); } }); } // Alternatif olarak, ECVSdkModules aracılığıyla global events'e abone olun // sdkInstance.getModules().subscribe(lifecycleOwner, new EventCallback() { ... });
Anahtar Event Types (CallEventTypes):
Daha kapsamlı bir liste için Olaylar_ve_Durum_Yonetimi.md dosyasına bakın, ancak bazı kritik olanlar şunlardır:
-
GotRtcId: SDK, benzersiz çağrı tanımlayıcısını aldı. -
DialSuccess: Backend, çağrı isteğini onayladı. -
Error: Bir hata oluştu (event.errorveevent.detailkontrol edin). -
OnQueue: Arayan bir agent bekliyor. -
AgentConnected: Agent katıldı, media kurmaya hazır. -
WebRtcConnected: Peer-to-peer media bağlantısı tamamen kuruldu. -
AudioMuted/AudioUnmuted: Yerel ses gönderme state'i değişti. -
VideoMuted/VideoUnmuted: Yerel video gönderme state'i değişti. -
RemoteAudioMuted/RemoteAudioUnmuted: Uzak katılımcının ses state'i değişti. -
RemoteVideoMuted/RemoteVideoUnmuted: Uzak katılımcının video state'i değişti. -
StartHold/EndHold: Çağrı bekletme durumu değişti. -
Hangup: Yerel kapatma başlatıldı. -
CallEnded: Çağrı session'ı sona erdi (yerel veya uzak).
5. Error Handling
Hatalar genellikle Error event'i (CallEventTypes.Error) aracılığıyla bildirilir. VideoCallEvent object'i şunları içerir:
-
event.error: BirErrorTypesenum ve bir mesaj içeren birECVErrorobject'i. -
event.detail: Varsa, hatayla ilgili ek string ayrıntıları. -
event.objData: Bazen ilgili data içerebilir (örn. HTTP response).
Her zaman Error events kontrol edin ve uygun şekilde ele alın (örn. user'a bir mesaj gösterin, çağrıyı temizleyin, geri gidin).
Yapılandırma Kılavuzu
Bu kılavuz, ECV SDK ve bireysel çağrılar için mevcut çeşitli configuration seçeneklerini açıklar.
1. SDK Initialization Configuration (ECVSdkConfiguration)
Bu configuration, ana ECVSdk instance'ını oluştururken sağlanır ve SDK'nın backend infrastructure ile iletişim kurması için temel bağlantı parameters tanımlar.
Class: group.ccr.ecv.sdk.ECVSdkConfiguration
Parameters:
-
serviceUrl: String(Gerekli): Ana ECV backend API services (örn.rtcIdalma, dialing, hangup bildirimleri) için temel URL.-
Örnek:
"https://sirketiniz.ecv.backend.com"
-
-
webrtcUrl: String(İsteğe bağlı, defaultserviceUrl): Özellikle WebRTC signaling ile ilgili HTTP requests (/joinendpoint'i gibi) için kullanılan temel URL. WebRTC signaling infrastructure'ınız ayrı olarak host ediliyorsa, burada belirtin.-
Örnek:
"https://sirketiniz.ecv.webrtc.com"
-
-
signalingUrl: String(İsteğe bağlı, defaultserviceUrl): WebSocket signaling server için temel URL. SDK,http/httpsyerinews/wsskullanacaktır. WebSocket server'ınız ayrı olarak host ediliyorsa, burada belirtin.-
Örnek:
"https://sirketiniz.ecv.websocket.com"
-
-
useQueueSocket: Boolean(İsteğe bağlı, defaulttrue): Queue state güncellemelerini (AgentConnectedgibi) alma mekanizmasını belirler.-
true(ECV v1.75+ için önerilir): Real-time güncellemeler için özel bir WebSocket (QueueSocket) kullanır. Bu, ECV backend infrastructure'ınızın 1.75 veya daha yüksek bir version olmasını gerektirir. Polling'e kıyasla daha hızlı bildirimler sağlar.
-
Örnek Kullanım:
import group.ccr.ecv.sdk.ECVSdkConfiguration
// ECV v1.75+ backend için configuration (tercih edilen WebSocket kullanarak)
val configV175 = ECVSdkConfiguration(
serviceUrl = "https://prod-v175.ecv.example.com",
)
import group.ccr.ecv.sdk.ECVSdkConfiguration;
// ECV v1.75+ backend için configuration (tercih edilen WebSocket kullanarak)
// Constructor'ın serviceUrl aldığını ve diğer params'ın default olduğunu veya daha sonra ayarlandığını varsayar.
ECVSdkConfiguration configV175 = new ECVSdkConfiguration(
/* serviceUrl */ "https://prod-v175.ecv.example.com"
);
2. Çağrı Başlatma Parameters (DialCallParameters)
Bu parameters, belirli bir çağrı session'ını başlatmak için videoCall.start() çağrıldığında sağlanır.
Class: group.ccr.ecv.sdk.videoCall.api.DialCallParameters
Parameters:
-
callerName: String(İsteğe bağlı, default""): Arayan için bir görünen ad, potansiyel olarak agent'a gösterilir. -
attributes: Map<String, String>(İsteğe bağlı, defaultemptyMap()): Genel çağrı attributes veya customer data temsil eden key-value çiftlerinin bir map'i. Bu bilgi, dial process sırasında backend'e gönderilir ve routing, screen pops veya context için kullanılabilir.-
Örnek:
mapOf("customerId" to "12345", "product" to "Premium Account")
-
-
acdAttributes: Map<String, String>(İsteğe bağlı, defaultemptyMap()): Backend'deki ACD (Automatic Call Distributor) routing mantığı için tasarlanmış özel attributes.-
Örnek:
mapOf("skill" to "Billing", "language" to "en-US")
-
-
queue: String?(İsteğe bağlı, defaultnull): Çağrıyı yönlendirmek için belirli queue veya workgroup adı.nullveya boşsa, backend'deki default routing kuralları geçerlidir.-
Örnek:
"SalesQueue"
-
-
customPayload: Map<String, String>(İsteğe bağlı, defaultemptyMap()): Özel backend entegrasyonunuzun gerektirdiği diğer custom data göndermek için esnek bir map. -
additionalHeaders: Map<String, String>(İsteğe bağlı, defaultemptyMap()):/api/dialexrequest'ine custom HTTP headers eklemeye izin verir. Bu, authentication tokens veya diğer metadata için kullanılabilir.-
Örnek:
mapOf("Authorization" to "Bearer YOUR_TOKEN", "X-Tenant-ID" to "TenantA")
-
Örnek Kullanım:
import group.ccr.ecv.sdk.videoCall.api.DialCallParameters
val params = DialCallParameters(
callerName = "Jane Doe",
attributes = mapOf(
"accountNumber" to "ACC9876",
"lastInteraction" to "2023-10-26"
),
queue = "SupportQueue",
additionalHeaders = mapOf("X-Auth-Token" to "user_session_token")
)
// videoCall'un VideoCall instance'ı olduğu varsayılır
videoCall.start(params)
import group.ccr.ecv.sdk.videoCall.api.DialCallParameters;
import java.util.HashMap;
import java.util.Map;
Map<String, String> attributes = new HashMap<>();
attributes.put("accountNumber", "ACC9876");
attributes.put("lastInteraction", "2023-10-26");
Map<String, String> headers = new HashMap<>();
headers.put("X-Auth-Token", "user_session_token");
DialCallParameters params = new DialCallParameters(
/* callerName */ "Jane Doe",
/* attributes */ attributes,
/* acdAttributes */ new HashMap<>(), // Boş map
/* queue */ "SupportQueue",
/* customPayload */ new HashMap<>(), // Boş map
/* additionalHeaders */ headers
);
// videoCall'un VideoCall instance'ı olduğu varsayılır
videoCall.start(params);
3. WebRTC Connection Parameters (ConnectParameters)
Bu parameters, videoCall.connect() çağrıldığında isteğe bağlı olarak sağlanır ve WebRTC peer connection davranışını ince ayarlar. Çoğu parameter'ın mantıklı default değerleri vardır.
Class: group.ccr.ecv.sdk.webRtc.ConnectParameters
Anahtar Parameters (Defaults gösterilmiştir):
-
Connection & Signaling:
-
candidateFilteringMode: CandidateFilteringMode = CandidateFilteringMode.ALL: Hangi ICE candidates kullanılacağını kontrol eder (All, Relay only, Host only). -
connectionTimeout: Int = 10000: Genel WebRTC bağlantı denemesi için timeout (ms) (WebRtcConnection'daki manuel timer tarafından kullanılır). -
joinHttpConnectTimeout: Int = 3000:/joinrequest'i için HTTP connect timeout (ms). -
joinHttpReadTimeout: Int = 3000:/joinrequest'i için HTTP read timeout (ms). -
additionalHeaders: Map<String, String> = emptyMap():/joinrequest'ine ve WebSocket signaling connection handshake'ine eklenen custom HTTP headers. WebRTC/Signaling infrastructure'ına ulaşması gereken authentication tokens için kullanışlıdır.
-
-
Media Control & Behavior:
-
videoCallEnabled: Boolean = true: Video tracks kurulup kurulmayacağı. Audio-only çağrılar içinfalseolarak ayarlayın. -
disableVideo: Boolean = false: Gerekirse video gönderme/alma işlemini tamamen devre dışı bırakmak için üst düzey flag (genelliklevideoCallEnabledyeterlidir). -
disableAudio: Boolean = false: Gerekirse ses gönderme/alma işlemini tamamen devre dışı bırakmak için üst düzey flag. -
useSpeakerPhone: Boolean = true: Çağrıyı speakerphone kullanarak başlatın. -
autoSwitchToEarPiece: Boolean = false: Proximity sensor tetiklendiğinde (telefon kulağa yakın) otomatik olarak earpiece'e geçin. -
autoHandleCameraSwitch: Boolean = true: Belirli events meydana geldiğinde (örn. agent arka kamera ile fotoğraf istediğinde) SDK'nın kameraları otomatik olarak değiştirmesine izin verir. -
autoHandleHoldSideEffects: Boolean = true: Agent çağrıyı hold'a aldığında veya devam ettirdiğinde yerel ses/videoyu otomatik olarak sessize alır/açar.
-
-
Video Configuration:
-
videoWidth: Int = 480/videoHeight: Int = 640: İstenen video resolution. -
videoFps: Int = 0(Default ~30): İstenen video frame rate. -
videoMaxBitrate: Int = 0(WebRTC default): Video encoding için maksimum bitrate (kbps). -
videoCodec: String = "VP8": Tercih edilen video codec ("VP8", "VP9", "H264"). -
videoCodecHwAcceleration: Boolean = true: Varsa hardware acceleration'ı etkinleştirin.
-
-
Audio Configuration:
-
audioCodec: String = "OPUS": Tercih edilen audio codec ("OPUS", "ISAC"). -
audioStartBitrate: Int = 0(WebRTC default): Başlangıç audio bitrate (kbps). -
noAudioProcessing: Boolean = false: Platform audio processing'i (AEC, AGC, NS) devre dışı bırakın. -
useOpenSLES: Boolean = false: OpenSL ES audio backend'ini kullanın (Android'e özgü). -
disableBuiltInAEC: Boolean = false/disableBuiltInNS: Boolean = false: Varsa hardware AEC/NS'yi devre dışı bırakın.
-
-
Debugging:
-
tracing: Boolean = false: WebRTC dahili tracing'i etkinleştirin. -
aecDump: Boolean = false: AEC diagnostic data'yı kaydedin. -
saveInputAudioToFile: Boolean = false: Ham input audio'yu kaydedin (useOpenSLES = falsegerektirir). -
enableRtcEventLog: Boolean = false: WebRTC events'i bir dosyaya loglayın.
-
-
Data Channel (
textChannelConfig):-
textChannelConfig: DataChannelConfig? = DataChannelConfig(...): FLASH_ON/OFF, HOLD/PICKUP vb. komutlar için kullanılan yerleşik text data channel configuration'ı. Genellikle default bırakılır. Detaylar içinDataChannelConfigclass'ına bakın.
-
Örnek Kullanım:
import group.ccr.ecv.sdk.webRtc.ConnectParameters
import group.ccr.ecv.sdk.webRtc.CandidateFilteringMode
// Çoğu default genellikle yeterlidir. Yalnızca belirli parameters override edin.
val customConnectParams = ConnectParameters(
videoWidth = 1280,
videoHeight = 720,
videoCodec = "H264",
candidateFilteringMode = CandidateFilteringMode.RELAY, // Örnek: Yalnızca TURN servers kullan
additionalHeaders = mapOf("X-WebRTC-Auth" to "webrtc_token")
)
// Custom parameters kullanarak WebRtcConnection'ı alın
val webRtcConnection = videoCall.connect(parameters = customConnectParams)
webRtcConnection.startRoom(context)
import group.ccr.ecv.sdk.webRtc.ConnectParameters;
import group.ccr.ecv.sdk.webRtc.CandidateFilteringMode;
import group.ccr.ecv.sdk.webRtc.DataChannelConfig; // Varsayalım ki bu var ve gerekli
import java.util.HashMap;
import java.util.Map;
// Constructor (varsa) veya defaults kullanarak ConnectParameters oluşturun
ConnectParameters customConnectParams = new ConnectParameters(
/* videoCallEnabled */ true,
/* disableVideo */ false,
/* disableAudio */ false,
/* videoWidth */ 1280, // Custom width
/* videoHeight */ 720, // Custom height
/* videoFps */ 0, // Default
/* videoMaxBitrate */ 0, // Default
/* videoCodec */ "H264", // Custom codec
/* videoCodecHwAcceleration */ true, // Default
/* videoFlexfecEnabled */ false, // Default
/* audioCodec */ "OPUS", // Default
/* audioStartBitrate */ 0, // Default
/* noAudioProcessing */ false, // Default
/* useOpenSLES */ false, // Default
/* disableBuiltInAEC */ false, // Default
/* disableBuiltInNS */ false, // Default
/* disableBuiltInAGC */ false, // Bu param'ın constructor'da olup olmadığını kontrol edin
/* disableWebRtcAGCAndHPF */ false, // Default
/* candidateFilteringMode */ CandidateFilteringMode.RELAY, // Custom filtering
/* connectionTimeout */ 10000, // Default
/* joinHttpConnectTimeout */ 3000, // Default
/* joinHttpReadTimeout */ 3000, // Default
/* useSpeakerPhone */ true, // Default
/* autoSwitchToEarPiece */ false, // Default
/* autoHandleCameraSwitch */ true, // Default
/* autoHandleHoldSideEffects */ true, // Default
/* additionalHeaders */ new HashMap<String, String>() {{ // Custom headers
put("X-WebRTC-Auth", "webrtc_token");
}},
/* tracing */ false, // Default
/* aecDump */ false, // Default
/* saveInputAudioToFile */ false, // Default
/* enableRtcEventLog */ false, // Default
/* textChannelConfig */ new DataChannelConfig(true, "sendTextChannel", -1, false, "", true, -1, -1) // Örnek default config
);
// Custom parameters kullanarak WebRtcConnection'ı alın
WebRtcConnection webRtcConnection = videoCall.connect(customConnectParams);
webRtcConnection.startRoom(context);
// Not: Yukarıdaki Java örneği, tüm bu fields eşleşen bir constructor olduğunu varsayar.
// ConnectParameters'ın gerçek constructor signature'ını doğrulayın.
// Yalnızca default bir constructor varsa, default object'i oluşturun
// ve public ise veya setters varsa fields değiştirin.
4. Demo Uygulama Environment Configuration
Demo Uygulama, farklı SDK configurations kalıcı olarak yönetmeye olanak tanıyan bir EnvironmentSelectorScreen içerir. Bu öncelikle test ve tanıtım içindir.
-
EnvironmentData Class: Birname,SerializableECVSdkConfigurationve birisDefaultflag'i tutar. -
SerializableECVSdkConfiguration: Kaydetme/yükleme için kullanılan,ECVSdkConfiguration'ı yansıtan serializable bir version. -
Default Environments:
loadInitialEnvironments()içinde tanımlanır, UI'den silinemez. Bir default'u düzenlemek yeni bir custom environment oluşturur. -
Custom Environments: User tarafından "+" butonuyla veya bir default'u düzenleyerek eklenir. Bunlar silinebilir.
-
Storage: Custom environment'lar Android DataStore (
saveEnvironments/loadEnvironments) kullanılarak kaydedilir. -
Dinamik Çağrı Attributes (
EnvironmentCallAttribute): Demo, environment başına attributes (örn. farklı queues) tanımlamaya olanak tanır; bunlarMainScreen'de dropdown olarak sunulur veDialCallParameters'a dahil edilir.
Production uygulamanızda muhtemelen şunları yapacaksınız:
-
Sabit bir
ECVSdkConfiguration'a sahip olacaksınız, -
Veya,
ECVSdk'yı initialize etmeden önce kendi backend service'inizden configuration ayrıntılarını dinamik olarak alacaksınız. -
customerIdveya hedefqueuegibi çağrıya özgü data'yı, demonun environment attribute sistemi yerine uygulamanızın context'ine göre doğrudanDialCallParameters'a ileteceksiniz.
Demo Uygulama Rehberi
Giriş
Bu kılavuz, sağlanan Android Demo Uygulaması (ecv175_android) boyunca size yol gösterir. Birincil amacı, ECV Görüntülü Görüşme SDK'sını işlevsel, ancak basitleştirilmiş bir uygulamada nasıl entegre edeceğinizi ve kullanacağınızı göstermektir.
Demo, UI'si için Jetpack Compose kullanır ve yaygın bir navigation pattern'ini izler. UI'nin kendisi production'a hazır olmasa da, SDK ile etkileşim şekli değerli örnekler sunar. Java kod parçacıkları, SDK kullanımını göstermek amacıyla kavramsal olarak sağlanmıştır, ancak geleneksel bir Java/XML uygulamasındaki UI implementation önemli ölçüde farklılık gösterecektir.
Anahtar ekranları ve SDK'nın yeteneklerinden nasıl yararlandıklarını inceleyeceğiz:
-
Global State Management (
AppState.kt) -
Environment Seçimi (
EnvironmentSelectorScreen.kt) -
Çağrı Başlatma (
MainScreen.kt) -
Bekleme Queue (
QueueScreen.kt) -
Aktif Çağrı (
CallScreen.kt)
Ön Koşullar
-
SDK'yı ve dependency'lerini (
.aardosyaları,lifecycle-runtime-ktx) environment'ınızdaREADME_tr.md'de açıklandığı gibi başarıyla kurmuş olun. -
ecv175_androiddemo projesini Android Studio'da build edip çalıştırabiliyor olun. -
Kotlin/Compose veya Java/XML Android geliştirmeye aşina olun.
1. Global State Management (AppState.kt)
Gerçek bir uygulamada, initialize edilmiş ECVSdk instance'ını ve potansiyel olarak o anda aktif olan VideoCall instance'ını farklı ekranlardan (Activities/Fragments/Composables) erişilebilmesi için yönetmenin bir yoluna ihtiyacınız vardır.
Demo uygulama bu amaçla AppState adında basit bir Kotlin object kullanır:
Kotlin Örneği (AppState.kt konsepti):
// AppState.kt'den basitleştirilmiş konsept
object AppState {
var sdkInstance: ECVSdk? = null
// ... diğer demo'ya özgü state ...
fun initializeSdk(context: Context, environment: Environment) {
// ... (daha önce gösterilen mantık) ...
sdkInstance = ECVSdk(instanceName = "DemoSdk-${environment.name}", config = sdkConfig)
// ...
}
}
// Erişim: AppState.sdkInstance
Java Eşdeğeri (örn. Application class'ında veya Singleton'da):
import android.app.Application;
import android.content.Context;
// Init logic için gerekliyse Environment/Configuration class'larınızı import edin
import group.ccr.ecv.sdk.ECVSdk;
import group.ccr.ecv.sdk.ECVSdkConfiguration;
import android.util.Log; // Log için import
public class MyApp extends Application {
private static ECVSdk sdkInstance;
@Override
public void onCreate() {
super.onCreate();
// SDK'yı burada veya talep üzerine initialize edin
// initializeSdk(this, /* Configuration kaynağınızı iletin */);
}
// SDK'yı initialize etme method'u (örnek)
public static synchronized void initializeSdk(Context context, ECVSdkConfiguration config) {
if (sdkInstance == null) {
try {
sdkInstance = new ECVSdk("MyAppSdkInstance", config);
Log.i("MyApp", "SDK Initialized");
} catch (Exception e) {
Log.e("MyApp", "SDK Initialization failed", e);
}
}
}
// Kolay erişim için static getter
public static ECVSdk getSdkInstance() {
if (sdkInstance == null) {
// İsteğe bağlı olarak burada initialize edin veya bir error fırlatın
throw new IllegalStateException("SDK not initialized. Call initializeSdk first.");
}
return sdkInstance;
}
}
// Erişim: MyApp.getSdkInstance()
Anahtar Çıkarımlar:
-
Merkezi Instance:
sdkInstance'ı global olarak saklamak, uygulama genelinde erişilebilir olmasını sağlar. Mimarınıza uygun bir pattern seçin (Application class, Singleton, DI). -
Initialization: SDK'yı, genellikle uygulama başlangıcında, uygun
ECVSdkConfigurationile bir kez initialize edin.
2. Environment Seçimi (EnvironmentSelectorScreen.kt)
Amaç: Farklı backend configurations (Staging, Production, Local Dev gibi) test etmek için demo'ya özgü ekran. Configuration'ları yükleme/kaydetme ve temel CRUD işlemlerini gösterir.
SDK Etkileşimi:
-
Configuration:
SerializableECVSdkConfigurationiçerenEnvironmentobject'lerini yönetir. Bu,ECVSdkinstance'ını initialize etmeden önce (muhtemelenAppState.initializeSdkaracılığıyla) SDK'nınECVSdkConfiguration'ına dönüştürülür. -
Kaydetme/Yükleme: Custom configurations kalıcı kılmak için DataStore (
loadEnvironments,saveEnvironments) kullanır. -
Initialization: Bir environment seçmek, seçilen configuration ile
AppState.sdkInstance'ın (yeniden) initialize edilmesini tetikler.
Kod Önemli Noktaları:
-
loadInitialEnvironments(): Hardcoded default environment'lar sağlar. -
LaunchedEffect: Ekran başladığında initial ve saved environment'ları yükler. -
onEnvironmentSelected: Bir öğe tıklandığında tetiklenen callback. Demo'da bu muhtemelenAppState'i güncelleyip uzaklaşmaya yol açar. -
EnvironmentDialog: Environment ayrıntılarını eklemek/düzenlemek için Composable.
Production Uygunluğu: Düşük. Uygulamanız tipik olarak sabit bir configuration sahip olacak veya kendi backend'inizden dinamik olarak alacaktır, son user'lara böyle bir seçim UI'si sunmayacaktır. Dinamik attribute seçimi (EnvironmentCallAttribute) de demo'ya özgüdür; çağrı data'sını doğrudan iletirsiniz.
3. Çağrı Başlatma (MainScreen.kt / Eşdeğer Activity/Fragment)
Amaç: Bir environment yapılandırıldıktan sonra görüntülü görüşme başlatmak için giriş noktası görevi görür. Permissions yönetir, temel çağrı data'sını toplar ve çağrı process'ini başlatır.
SDK Etkileşimi ve Kod Önemli Noktaları:
-
Permissions:
-
requiredPermissionstanımlar (Kamera, Mikrofon, S+ üzerinde Bluetooth Connect). -
Permissions istemek için
rememberLauncherForActivityResult(ActivityResultContracts.RequestMultiplePermissions())kullanır. -
Durumu doğrulamak için
checkAllPermissions()kullanır. -
"START" butonu yalnızca
allPermissionsGrantedtrue olduğunda etkindir.
val requiredPermissions = remember { /* ... kamera, mic, bluetooth ... */ } var allPermissionsGranted by remember { mutableStateOf(checkAllPermissions(context, requiredPermissions)) } val multiplePermissionsLauncher = rememberLauncherForActivityResult(...) { /* ... allPermissionsGranted'ı güncelle ... */ } LaunchedEffect(Unit) { if (!allPermissionsGranted) { multiplePermissionsLauncher.launch(requiredPermissions.toTypedArray()) } } // ... Buton allPermissionsGranted'a göre etkinleştirilir ... -
-
Data Toplama:
-
callerNameiçinOutlinedTextField. -
AppState.currentConfig?.callerAttributeSelectionsboş değilseSimpleSelectDropdown(bir demo helper) kullanılır,callerAttributesmap'ini doldurur.
-
-
Çağrıyı Başlatma: ("START" butonunun
onClick'i içinde)-
Çağrı Oluşturma: SDK'dan bir
VideoCallinstance'ı alın.val call = AppState.sdkInstance?.createCall() if (call == null) { // SDK'nın initialize edilmediği durumu ele alın Toast.makeText(context, "SDK not initialized", Toast.LENGTH_SHORT).show() return@Button // veya return@launch if in coroutine scope } -
(Önerilen)
GotRtcIdBekleme: Demo,createCall'dan kısa süre sonrartcId'nin hazır olacağına örtük olarak güvense de, sağlam kod genellikle event'i bekler.// onClick'ten coroutine scope kullanarak scope.launch { var rtcId: String? = null try { // Event'i VEYA bir error bekleyin val initEvent = call.eventFlow.first { it.type == CallEventTypes.GotRtcId || it.type == CallEventTypes.Error } if (initEvent.type == CallEventTypes.GotRtcId) { rtcId = initEvent.detail Log.d("MainScreen", "Got rtcId: $rtcId") } else { throw Exception("Error getting rtcId: ${initEvent.error?.message ?: initEvent.detail}") } } catch (e: Exception) { Log.e("MainScreen", "Failed to get rtcId", e) Toast.makeText(context, "Error initializing call: ${e.message}", Toast.LENGTH_LONG).show() return@launch // rtcId başarısız olursa dur } if (rtcId == null) return@launch // Exception handling doğruysa olmamalı // --- Dialing ile devam edin --- val dialParams = DialCallParameters( callerName = callerName, // UI State'inden attributes = callerAttributes // UI State'inden (Map<String, String>) // queue = "OptionalQueueName" // Gerekirse ) call.start(dialParams) // Dialing process'ini başlatın // Dial process'ini başlattıktan sonra navigate edin // QueueScreen AgentConnected'ı beklemeyi ele alacaktır navController.navigate("queue/$rtcId") Log.d("MainScreen", "Navigated to queue for rtcId: $rtcId") } -
DialCallParametersOluşturma: UI state'inden data toplayın (callerName,callerAttributes). -
start()Çağırma:call.start(dialParams)request'i backend'e gönderir. -
Navigate: Başarılı başlatma üzerine (
AgentConnectedöncesi),rtcId'yi ileterek queue ekranına gidin.navController.navigate("queue/${call.rtcId}").
Java Eşdeğeri (örn. bir Activity
onClick'inde):// SDK instance'ını alın (örn. Application class'ından) ECVSdk sdk = MyApp.getSdkInstance(); if (sdk == null) { Toast.makeText(this, "SDK not initialized", Toast.LENGTH_SHORT).show(); return; } // Çağrıyı oluşturun VideoCall videoCall = sdk.createCall(); // UI data'sını alın String callerName = editTextCallerName.getText().toString(); Map<String, String> callerAttributes = getAttributesFromUI(); // Sizin mantığınız burada // Not: Java'da GotRtcId beklemek, Kotlin'deki gibi bir flow'u doğrudan 'await' edemediğiniz için // bir callback mekanizması veya dikkatli state management gerektirir. // Basitleştirilmiş bir yaklaşım (daha az sağlam) delay kabul edilebilirse açık beklemeyi atlayabilir. // Daha sağlam bir yol, subscribe mekanizmasını kullanmayı içerir (aşağıda event handling bölümünde gösterilmiştir) // --- Daha iyi yaklaşım: Events'e Abone Olun --- // Activity'nin lifecycle scope'u içinde abone olun // (Subscribe pattern'i için aşağıdaki event handling bölümüne bakın) // GotRtcId için onEvent callback'inde: // String rtcId = event.getDetail(); // DialCallParameters dialParams = new DialCallParameters(callerName, callerAttributes, ...); // videoCall.start(dialParams); // // Queue Activity/Fragment'a gidin // Intent intent = new Intent(this, QueueActivity.class); // intent.putExtra("RTC_ID", rtcId); // startActivity(intent); // --- Bu kavramsal örnek için, GotRtcId'nin bir şekilde gerçekleştiğini varsayalım --- // --- ve sonra dial edip navigate edersiniz --- // DialCallParameters dialParams = new DialCallParameters(callerName, callerAttributes, ...); // videoCall.start(dialParams); // // rtcId'nin şimdi mevcut olduğunu varsayarsak (doğru event handling olmadan bu doğru olmayabilir) // Intent intent = new Intent(this, QueueActivity.class); // intent.putExtra("RTC_ID", videoCall.getRtcId()); // Başlangıçta boş olabilir! // startActivity(intent); -
4. Bekleme Queue (QueueScreen.kt / QueueActivity veya QueueFragment)
Amaç: Çağrı queue'dayken, bir agent'ın bağlanmasını beklerken bir bekleme göstergesi görüntüler. SDK events'e göre navigation'ı yönetir.
SDK Etkileşimi ve Kod Önemli Noktaları:
-
VideoCallInstance'ını Alma: Navigation sırasında iletilenrtcIdile ilişkiliVideoCallobject'ini alın. Çağrının artık mevcut olmayabileceği durumu ele alın.Kotlin (Demo):
val rtcId: String = // navigation args'dan var videoCall = AppState.sdkInstance?.getVideoCall(rtcId) // Null ise ele alın
Java Eşdeğeri:
// Activity onCreate veya Fragment onViewCreated içinde String rtcId = getIntent().getStringExtra("RTC_ID"); // Veya getArguments().getString(...) VideoCall videoCall = MyApp.getSdkInstance().getVideoCall(rtcId); if (videoCall == null) { Toast.makeText(this, "Call not found", Toast.LENGTH_SHORT).show(); finish(); // Bu activity'yi kapatın return; } // videoCall'u bir member variable olarak saklayın this.videoCall = videoCall; -
Event Handling: Belirli
videoCalliçin events toplayın/abone olun.Kotlin (Demo -
LaunchedEffect):LaunchedEffect(videoCall) { if (videoCall.agentConnectedState) { /* Navigate */ return@LaunchedEffect } videoCall.eventFlow.collect { event -> when (event.type) { CallEventTypes.AgentConnected -> { /* CallActivity'ye gidin */ } CallEventTypes.Error, CallEventTypes.Hangup, CallEventTypes.CallEnded -> { /* Main'e geri gidin */ } else -> {} } } }Java Eşdeğeri (
subscribekullanarak):// Activity onCreate veya Fragment onViewCreated içinde, videoCall alındıktan sonra LifecycleOwner lifecycleOwner = this; // Activity/Fragment bir LifecycleOwner'dır // Initial state'i kontrol edin if (videoCall.getAgentConnectedState()) { navigateToCallScreen(rtcId); // Sizin navigation method'unuz return; // Zaten bağlıysa abone olmayın } videoCall.subscribe(lifecycleOwner, new EventCallback() { @Override public void onEvent(VideoCallEvent event) { runOnUiThread(() -> { // UI güncellemelerinin main thread'de olduğundan emin olun if (event.getType() == CallEventTypes.AgentConnected) { navigateToCallScreen(rtcId); // Sizin navigation method'unuz } else if (event.getType() == CallEventTypes.Error || event.getType() == CallEventTypes.Hangup || event.getType() == CallEventTypes.CallEnded) { String message = event.getDetail(); if (message == null && event.getError() != null) { message = event.getError().getMessage(); } Toast.makeText(QueueActivity.this, "Exiting Queue: " + message, Toast.LENGTH_LONG).show(); navigateBackToMainScreen(); // Sizin navigation method'unuz } }); } }); -
UI: Bir
CircularProgressIndicatorve bekleme metni gösterin. Bir Hangup butonu sağlayın. -
Cleanup: User ekranı erken terk ederse çağrıyı kapatın.
Kotlin (Demo -
DisposableEffect):DisposableEffect(videoCall) { onDispose { // Composable kaldırıldığında bu çalışır // Çağrının hala aktif olup olmadığını kontrol edin (zaten kapatılmamış veya bağlanıp uzaklaşılmamış) if (!videoCall.didHangup && !videoCall.agentConnectedState) { Log.d("QueueScreen", "Disposing QueueScreen, hanging up call ${videoCall.rtcId}") videoCall.hangup() // Queue ekranından erken ayrılırken çağrıyı temizleyin } else { Log.d("QueueScreen", "Disposing QueueScreen, call already handled (hung up or connected).") } } }Java Eşdeğeri (Activity
onDestroyveya FragmentonDestroyViewiçinde):@Override protected void onDestroy() { super.onDestroy(); if (videoCall != null && !videoCall.getDidHangup() && !videoCall.getAgentConnectedState()) { Log.d("QueueActivity", "onDestroy, hanging up call " + videoCall.getRtcId()); // Hangup network calls içeriyorsa potansiyel olarak main thread dışında çalıştırın // onDestroy içinde uzun süren işlemler yapmaktan kaçının new Thread(() -> videoCall.hangup()).start(); // Basit threading örneği } }
5. Aktif Çağrı (CallScreen.kt / CallActivity veya CallFragment)
Amaç: Devam eden görüntülü görüşme için ana interface. Renderers, kontrolleri ve çağrı state'ini yönetir.
SDK Etkileşimi ve Kod Önemli Noktaları:
-
VideoCallveWebRtcConnectionAlma:QueueScreen'e benzer şekilde,rtcIdaracılığıylavideoCallalın.videoCall.connect()vewebRtcConnection.startRoom(context)çağırın.Kotlin (Demo):
val videoCall = remember(rtcId) { AppState.sdkInstance?.getVideoCall(rtcId) } var webRtcConnection: WebRtcConnection? by remember { mutableStateOf<WebRtcConnection?>(null) } LaunchedEffect(videoCall) { if (videoCall == null) { /* Handle error, navigate back */ return@LaunchedEffect } // Connection'ı alın veya oluşturun val connection = videoCall.connect() // ConnectParameters gerekirse iletin if (connection == null) { /* Handle error, navigate back */ return@LaunchedEffect } webRtcConnection = connection // State'i güncelleyin // WebRTC setup process'ini başlatın connection.startRoom(context) }Java Eşdeğeri:
// Activity onCreate veya Fragment onViewCreated içinde String rtcId = getIntent().getStringExtra("RTC_ID"); VideoCall videoCall = MyApp.getSdkInstance().getVideoCall(rtcId); if (videoCall == null) { /* handle error, finish */ return; } this.videoCall = videoCall; WebRtcConnection connection = videoCall.connect(null); // ConnectParameters gerekirse iletin if (connection == null) { /* handle error, finish */ return; } this.webRtcConnection = connection; // WebRTC'yi başlatın - zamanlamayı düşünün, view setup'ını beklemeniz gerekebilir webRtcConnection.startRoom(this); // Context iletin -
Event Handling & UI State:
videoCall.eventFlow(Kotlin) abone olun veyavideoCall.subscribe(Java) kullanarak UI elements (sessize alma ikonları, hold overlay görünürlüğü vb.) SDK events'e göre güncelleyin. UI state'ini member variables veyaViewModeliçinde saklayın. -
Çağrı Kontrolleri:
Buttontıklamalarını SDK method'larına bağlayın.Kotlin (Demo):
onToggleAudioMuteClick = { webRtcConnection?.setAudioEnabled(!audioState) } onSwitchCameraClick = { webRtcConnection?.switchCamera() } onHangupClick = { /* Show dialog */ videoCall?.hangup() }Java Eşdeğeri:
// Sessize alma butonu için OnClickListener içinde boolean currentAudioState = /* mevcut state'i alın, örn. boolean member'dan */; if (webRtcConnection != null) { webRtcConnection.setAudioEnabled(!currentAudioState); // AudioMuted/Unmuted events'e göre buton ikonunu ve currentAudioState variable'ını güncelleyin } // Kamera değiştirme butonu için OnClickListener içinde if (webRtcConnection != null) { webRtcConnection.switchCamera(); } // Kapatma butonu için OnClickListener içinde new AlertDialog.Builder(this) .setTitle("Confirm Hangup") // ... mesaj, butonlar ... .setPositiveButton("Hangup", (dialog, which) -> { if (videoCall != null) { new Thread(() -> videoCall.hangup()).start(); // Hangup'ı main thread dışında yapın // UI güncellemesi/navigation genellikle CallEnded event'i tarafından yönetilir } }) .show(); -
Video Rendering: Bu, Compose ve geleneksel Views arasında önemli ölçüde farklılık gösterir.
-
Compose:
SDK_Kullanimi.md'de detaylandırıldığı veVideoRenderercomposable'ında gösterildiği gibi lifecycle management içinfactory,updateveonReleaseileAndroidViewkullanır. -
Java/XML:
-
XML layout'unuza
<org.webrtc.SurfaceViewRenderer ... />ekleyin. -
Renderers referanslarını alın (örn. ViewBinding kullanarak).
-
Initialization:
onCreateveyaonViewCreatediçinde,surfaceViewRenderer.init(webRtcConnection.getEglBase().getEglBaseContext(), null)çağırın. -
Attachment: Connection hazır olduğunda (örn.
WebRtcConnectedsonrasında veya view kullanılabilir olduğunda)webRtcConnection.attachLocalView(...)vewebRtcConnection.attachRemoteView(...)çağırın. -
Detachment & Release: Kritik olarak,
onDestroyveyaonDestroyViewiçinde, hem local hem de remote renderers içinwebRtcConnection.detachLocalView(...),webRtcConnection.detachRemoteView(...)vesurfaceViewRenderer.release()çağırın. Bunu doğru yapmamak leak'lere ve crash'lere neden olur.
-
-
-
Immersive Mode:
onResume/onPauseveya ilgili lifecycle events'de system bars gizlemek/göstermek içinWindowInsetsControllerCompatkullanın. -
Cleanup:
onDestroyiçinde, çağrı hala aktifsevideoCall.hangup()çağrıldığından emin olun. Yukarıda belirtildiği gibi renderers release edin.videoCall.subscribe'ınLifecycleOwner'ı tarafından örtük olarak yönetilen event subscriptions durdurulur.Kotlin (Demo -
DisposableEffect):DisposableEffect(Unit) { onDispose { Log.d("CallScreen", "Disposing CallScreen Scope...") // Yalnızca çağrı zaten doğal olarak sona ermediyse kapatın if (videoCall?.didHangup == false) { // SDK flag'ini kontrol edin (varsa) videoCall.hangup() Log.d("CallScreen", "Called hangup for ${videoCall.rtcId} on dispose") } // System bars geri yükleyin if (window != null && !isInPreview) { WindowCompat.getInsetsController(window, view) ?.apply { show(WindowInsetsCompat.Type.systemBars()) } } } }
Sonuç
Demo uygulama, ECV SDK'yı kullanmak için pratik, ancak basitleştirilmiş bir yapı sunar. Anahtar entegrasyon noktaları şunları içerir:
-
ECVSdkinstance'ını initialize etme ve yönetme. -
VideoCallinstance'larını oluşturma ve yönetme. -
Permissions yönetme.
-
Çağrıları uygun parameters ile başlatma.
-
UI state'ini ve navigation'ı yönlendirmek için
VideoCallEvent'lere abone olma ve tepki verme (Kotlin Flows veyaLifecycleOwnerile Java Callbacks kullanarak). -
UI framework'ünüz içinde
SurfaceViewRenderer'ların lifecycle'ını doğru şekilde yönetme (özellikleinit,attach/detachverelease). -
User actions yanıt olarak SDK method'larını (
hangup,setAudioEnabled, vb.) çağırma.
Tam bir anlayış için diğer dokümantasyon dosyalarını (SDK_Kullanimi.md, Yapilandirma.md, Olaylar_ve_Durum_Yonetimi.md) okurken demo koduna geri başvurun. Burada gösterilen kavramları kendi uygulamanızın özel mimarisine (Compose veya View tabanlı) uyarlayın.
Events ve State Yönetimi
Giriş
ECV Android SDK'sı eşzamansız olarak çalışır. State değişikliklerini, hataları ve diğer önemli olayları uygulamanıza iletmek için event-driven bir mimari kullanır. Bu events anlamak, duyarlı ve sağlam bir UI oluşturmak için kritik öneme sahiptir.
Bu kılavuz, SDK events yapısını detaylandırır, anahtar event types listeler ve ilgili state properties bahseder.
Events Abone Olma
Bir VideoCall instance'ındaki events dinlersiniz. SDK iki ana mekanizma sunar:
-
Kotlin (Flow):
videoCall.eventFlow'a (SharedFlow<VideoCallEvent>) erişin ve Kotlin Coroutines kullanarak, tipik olarak birLaunchedEffectveyalifecycleScopeiçinde events toplayın. -
Java (Callback):
videoCall.subscribe(lifecycleOwner, eventCallback)method'unu, bir AndroidLifecycleOwner(Activity veya Fragment gibi) ve birEventCallbackimplementation sağlayarak kullanın. Subscription,LifecycleOwnertarafından otomatik olarak yönetilir.
Her iki subscription method'unun ayrıntılı kod örnekleri için SDK_Kullanimi.md'deki "Event Handling" bölümüne bakın.
Event Yapısı (VideoCallEvent)
Tüm SDK events, group.ccr.ecv.sdk.videoCall.events.VideoCallEvent data class'ının instance'ları olarak teslim edilir.
Anahtar Fields:
-
type: CallEventTypes: (Gerekli) Gerçekleşen event türünü gösteren bir enum değeri. Nasıl tepki vereceğinizi belirlemek için kontrol edeceğiniz birincil field'dır. -
detail: String?: (İsteğe bağlı) Event'e özgü ek string bilgisi sağlar (örn.GotRtcIdiçinrtcId,DialSuccessiçin bir interaction ID,CallEndediçin bir neden). -
error: ECVError?: (İsteğe bağlı) YalnızcaErrorevents için dahil edilir. BirErrorTypesenum ve açıklayıcı bir mesaj içeren birECVErrorobject'i içerir. -
objData: Any?: (İsteğe bağlı)SignalingConnectedsırasındaWebRtcConnectionveya belirli hatalarda bir HTTP response object'i gibi ilgili object'leri içerebilir. Bu data'ya erişiyorsanız tip kontrolü (isveyainstanceof) kullanın. -
data: Map<String, String>: (İsteğe bağlı, genellikle boş) Event ile ilişkili potansiyel key-value data için genel bir map. -
createdAtMillis: Long: Event'in SDK içinde oluşturulduğu timestamp (epoch milisaniye). -
videoCall: VideoCall: (Dahili olarak ayarlanır) Event'i yayanVideoCallinstance'ına geri bir reference. -
rtcId: String: (Dahili olarak ayarlanır) Event ile ilişkili çağrınınrtcId'si.
Event Type Referansı (CallEventTypes)
İşte yaygın event types bir dökümü, tipik bir çağrının phase'ine göre gruplandırılmıştır:
Phase 1: Initialization & Dialing
-
GotRtcId-
Ne zaman:
sdkInstance.createCall()backend'den benzersiz bir çağrı tanımlayıcısı başarıyla aldıktan sonra tetiklenir. -
Data:
event.detail,rtcId'yi (String) içerir. -
Yanıt:
rtcId'yi saklayın. Daha sonraVideoCallinstance'ını almak ve dialing'i başlatmak (call.start()) için buna ihtiyacınız vardır.
-
-
DialSuccess-
Ne zaman:
call.start()backend'e dial request'ini başarıyla gönderdikten ve onay aldıktan sonra (genellikle bir Interaction ID içerir) tetiklenir. -
Data:
event.detail, Interaction ID'yi içerebilir.event.objData, ham JSON response'u tutabilir. -
Yanıt: Genellikle bekleme/queue state'ine geçebileceğinizi gösterir.
-
-
Error(Init/Dial Sırasında)-
Ne zaman:
rtcIdalma başarısız olursa,call.start()parameters geçersizse, backend dial request'ini reddederse veya network sorunları oluşursa meydana gelebilir. -
Data:
event.error,ECVErroriçerir.event.detaildaha fazla context sağlayabilir.event.objData, bir HTTP response tutabilir. SpesifikErrorTypesiçinerror.errorkontrol edin (CALL_NOT_INITIALIZED,DIAL_FAILEDgibi). -
Yanıt: User'a bir hata mesajı gösterin. Daha fazla çağrı ilerlemesini önleyin. Kullanılamaz durumdaysa
VideoCallinstance'ını potansiyel olarak temizleyin.
-
Phase 2: Queue & Agent Connection
-
OnQueue-
Ne zaman: Başarılı bir dial sonrasında, arayanın şimdi bir agent beklediğini belirtmek için tetiklenir.
-
Data: Genellikle belirli bir data gerekmez.
-
Yanıt: Bir bekleme UI'si gösterin (
QueueScreengibi).
-
-
AgentConnected-
Ne zaman: Bir agent çağrıyı kabul ettiğinde tetiklenir. Bu, peer-to-peer media bağlantısını kurma tetiğidir.
-
Data: Spesifik bir şey yok.
-
Yanıt: WebRTC bağlantısını başlatın:
-
WebRtcConnectionalmak içinvideoCall.connect()çağırın. -
WebRTC setup'ını başlatmak için
webRtcConnection.startRoom(context)çağırın. -
Ana çağrı ekranına (
CallScreen) gidin.
-
-
-
Error(Queue Sırasında)-
Ne zaman: Queue timeouts (
QUEUE_SOCKET_TIMEOUT), queue mekanizmasıyla ilgili network sorunları (QUEUE_SOCKET_ERROR,QUEUE_SOCKET_CALL_DISCONNECTED) veya backend sorunları (WAIT_FOR_ROOM_FAILED, polling kullanılıyorsa) nedeniyle oluşabilir. -
Data:
event.error,ECVErroriçerir. -
Yanıt: User'a çağrının bağlanamadığını bildirin. Queue ekranından geri gidin. SDK otomatik olarak
CallEndedtetiklemezse cleanup sağlamak içinvideoCall.hangup()çağırın.
-
-
Hangup/CallEnded(Queue Sırasında)-
Ne zaman: User açıkça kapatırsa (
videoCall.hangup()) veya çağrı bir agent bağlanmadan önce backend tarafından sonlandırılırsa. -
Data:
event.detailbir neden içerebilir ("LOCAL_HANGUP", vb.). -
Yanıt: Queue ekranından geri gidin. UI'nin temizlendiğinden emin olun.
-
Phase 3: WebRTC Connection Kurulumu
Bu events genellikle WebRtcConnection tarafından dahili olarak ele alınır ancak debug veya gelişmiş kullanım durumları için gözlemlenebilir. Başarıyı işaret eden anahtar event WebRtcConnected'dır.
-
SignalingConnected: Signaling server'ına WebSocket bağlantısı kuruldu. -
SignalingRegistered: Client, room (rtcId) için signaling server ile başarıyla register oldu. -
JoinedRoom: HTTP/joinrequest başarılı, initial parameters alındı (ICE servers, initiator durumu gibi).event.objData,JoinRoomResponseiçerir. -
RtcPeerConnectionCreated: Temeldeki WebRTCPeerConnectionobject'i oluşturuldu.event.objData,PeerConnectionClient'ı tutar. -
GotOffer/SentOffer: Peer'den SDP Offer alındı / gönderildi.event.objData, JSON mesajını içerir. -
GotAnswer/SentAnswer: Peer'den SDP Answer alındı / gönderildi.event.objData, JSON mesajını içerir. -
WebRtcConnected-
Ne zaman: WebRTC PeerConnection (ICE ve DTLS) tamamen kurulduğunda ve media akışı başlayabildiğinde.
-
Data: Spesifik bir şey yok.
-
Yanıt: Genellikle media path'inin hazır olduğunu bilmek için bir tetikleyici olarak kullanılır.
CallScreen'deki initial loading göstergelerini gizleyebilirsiniz.
-
-
TextChannelConnected: Text komutları (hold, flash vb.) için kullanılan özel data channel açık.event.objData,DataChannel'ı tutar. -
WebRtcDisconnected/WebRtcFailed: WebRTC bağlantısı beklenmedik bir şekilde düştü veya başarısız oldu. Genellikle kısa süre sonra birCallEndedevent'ine yol açar.
Phase 4: Aktif Çağrı & Media
-
GotLocalStream: Yerel kamera capture başladı.event.objData,VideoCaptureriçerebilir. -
GotRemoteStream: Uzak media stream mevcut (genellikleWebRtcConnectedcivarında tetiklenir).-
Yanıt: Uzak
SurfaceViewRenderer'ın attach edildiğinden emin olun.
-
-
AudioMuted/AudioUnmuted: Yerel ses gönderme state'i değişti (setAudioEnabledtarafından tetiklenir).-
Yanıt: Mute butonu UI state'ini güncelleyin.
-
-
VideoMuted/VideoUnmuted: Yerel video gönderme state'i değişti (setVideoEnabledtarafından tetiklenir).-
Yanıt: Video mute butonu UI state'ini güncelleyin. Potansiyel olarak yerel video preview overlay'ini gösterin/gizleyin.
-
-
RemoteAudioMuted/RemoteAudioUnmuted: Uzak katılımcı sesini sessize aldı/açtı.-
Yanıt: İsteğe bağlı olarak uzak tarafın sessizde olduğunu gösteren bir indicator görüntüleyin.
-
-
RemoteVideoMuted/RemoteVideoUnmuted: Uzak katılımcı videosunu sessize aldı/açtı.-
Yanıt: Uzak video renderer üzerinde bir overlay veya indicator gösterin/gizleyin.
-
-
StartHold/EndHold: Çağrı agent tarafından hold'a alındı veya hold'dan çıkarıldı (genellikle text data channel aracılığıyla tetiklenir).-
Yanıt: "Call on Hold" overlay'ini gösterin/gizleyin. SDK (
autoHandleHoldSideEffectstrue ise) yanıt olarak yerel medyayı otomatik olarak sessize alabilir/açabilir.
-
-
TakePhotoOn/TakePhotoOff: Agent tarafından başlatılan fotoğraf yakalama feature'ları ile ilgili signals.-
Yanıt: Gerekirse UI'yi ayarlayın (örn. potansiyel olarak yerel videoyu fullscreen zorlayın). SDK (
autoHandleCameraSwitchtrue ise) kameraları otomatik olarak değiştirebilir.
-
-
TextChannelMessage: Text data channel üzerinden alınan bir komut veya mesaj.-
Data:
event.detail, ham mesaj string'ini içerir (örn. "FLASH_ON", "STATUS|VIDEO|MUTED|rtcId"). -
Yanıt: SDK, birçok standart mesajı dahili olarak ele alır (HOLD, PICKUP, MUTE durumları, FLASH komutları gibi). Implementation'ınıza özgü custom mesajlara tepki verebilirsiniz.
-
Phase 5: Çağrı Sonlandırma
-
Hangup-
Ne zaman: Yerel olarak
videoCall.hangup()çağrıldığında hemen tetiklenir. -
Data: Spesifik bir şey yok.
-
Yanıt: Backend çağrının sona erdiğini onaylamadan önce UI cleanup'ını başlatmak için kullanılabilir. Unutmayın ki
CallEndedkesin bitiş event'idir.
-
-
CallEnded-
Ne zaman: Çağrı session'ının tamamen sona erdiğini gösteren son event; yerel olarak (
hangup()tamamlanması), uzaktan (agent/peer kapatır) veya kurtarılamaz bir error nedeniyle. -
Data:
event.detailgenellikle bir neden içerir (örn. "LOCAL_HANGUP", "REMOTE_HANGUP", "PEER_DISCONNECTED", "CONNECTION_TIMEOUT", error açıklaması). -
Yanıt: Bu, aşağıdakileri yapmak için kesin sinyaldir:
-
Tüm çağrıyla ilgili UI'yi (renderers, kontroller) temizleyin.
-
Çağrı ekranından uzaklaşın.
-
Belirli
VideoCall'a bağlı tüm resources serbest bırakın. SDK tipik olarak bu event'ten sonraVideoCallinstance'ına olan dahili reference'ını kaldırır.
-
-
State Properties
Events değişiklikleri bildirirken, bazı mevcut state properties de kontrol edebilirsiniz:
-
VideoCallÜzerinde:-
rtcId: String: Benzersiz çağrı tanımlayıcısı (GotRtcIdsonrası kullanılabilir). -
agentConnectedState: Boolean:AgentConnectedevent'i gerçekleştiyse true. -
didHangup: Boolean: Yerel olarakhangup()çağrıldıysa true. -
webRtcConnection: WebRtcConnection?: İlişkili WebRTC connection (connect()çağrıldıktan sonra kullanılabilir).
-
-
WebRtcConnectionÜzerinde:-
audioEnabled: Boolean: Yerel ses göndermenin mevcut state'i (sonsetAudioEnabledçağrısını yansıtır). -
videoEnabled: Boolean: Yerel video göndermenin mevcut state'i (sonsetVideoEnabledçağrısını yansıtır). -
isClosing: Boolean: Bu connection için cleanup process'i başladıysa true.
-
Genellikle UI state'inizi güncellemek için bu properties sürekli poll etmek yerine events'e tepki vermek en iyi practice'dir. Properties gerektiğinde initial state kontrolleri veya kararlar için kullanın (örn. connect() çağırmadan önce agentConnectedState kontrol etmek).
Özelleştirme Kılavuzu
Giriş
Sağlanan Demo Uygulama (ecv175_android), ECV SDK'sının temel işlevlerini sergiler. Ancak, production uygulamanız şüphesiz farklı UI/UX gereksinimlerine, configuration method'larına ve data ihtiyaçlarına sahip olacaktır. Bu kılavuz, SDK'yı ve demo kavramlarını özel uygulamanıza uyarlama konusunda tavsiyeler sunar.
1. UI Implementation
Demo Jetpack Compose kullanır, ancak SDK'nın kendisi UI-agnostic'tir. Şunlarla oluşturulmuş uygulamalara entegre edebilirsiniz:
-
Jetpack Compose
-
Geleneksel Android Views (Activities/Fragments ile XML layouts)
-
Bunların herhangi bir kombinasyonu.
UI'niz İçin Anahtar Hususlar:
-
Kendi UI'nizi Oluşturun: Demo UI ekranlarını basitçe kopyalamayın. Uygulamanızın görünümüne, hissine ve user flow'una uyan ekranlar tasarlayın.
-
Temel SDK Etkileşimleri Aynı Kalır: UI toolkit'iniz ne olursa olsun, SDK ile temel etkileşimler devam eder:
-
Permissions: Çağrıyı başlatmadan önce Kamera, Mikrofon (ve S+ için Bluetooth Connect) permissions istemeli ve yönetmelisiniz.
-
Video Renderers: Videoyu görüntülemek için layout'unuzda
org.webrtc.SurfaceViewRendererinstance'ları sağlamanız gerekir. -
Renderer Lifecycle: Bu kritiktir.
SurfaceViewRenderer'ların lifecycle'ını doğru şekilde yönetmelisiniz:-
webRtcConnection.eglBase.eglBaseContextkullanarak initialize edin. -
webRtcConnection.attachLocalView/attachRemoteViewkullanarak yerel/uzak streams attach edin. -
webRtcConnection.detachLocalView/detachRemoteViewkullanarak detach edin. -
Artık gerekmediğinde (örn.
onDestroyView,onDispose, ActivityonDestroy)surfaceViewRenderer.release()kullanarak release edin. Doğru şekilde release etmemek, crash'lerin ve memory leak'lerin yaygın bir nedenidir. (SDK_Kullanimi.mdveDemo_Uygulama_Rehberi.md'deki örneklere bakın).
-
-
Çağrı State Gösterimi: UI'niz, SDK events'e dayalı olarak mevcut çağrı durumunu (örn. "Connecting...", "Waiting for agent...", "Connected", "On Hold") yansıtmalıdır.
-
Çağrı Kontrolleri: Aşağıdakiler için UI elements (Buttons, Icons) implement edin:
-
Sesi Sessize Alma/Açma (
webRtcConnection.setAudioEnabled(...)) -
Videoyu Açma/Kapatma (
webRtcConnection.setVideoEnabled(...)) -
Kamerayı Değiştirme (
webRtcConnection.switchCamera(...)) -
Kapatma (
videoCall.hangup())
-
-
Event Handling: UI layer'ınız, state'ini güncellemek, ekranlar arasında gezinmek ve hataları ele almak için SDK events (bkz.
Olaylar_ve_Durum_Yonetimi.md) dinlemelidir.
-
2. Configuration (ECVSdkConfiguration)
Demo'nun EnvironmentSelectorScreen'i, farklı backend environment'larını kolayca test etmek için tasarlanmıştır. Production uygulamanızda muhtemelen bu ekrana ihtiyacınız olmayacaktır.
Production Configuration Stratejileri:
-
Sabit Configuration: Uygulamanız yalnızca tek bir backend environment'ına (örn. production) bağlanıyorsa,
ECVSdk'yı initialize ederkenECVSdkConfigurationdeğerlerini hardcode edebilirsiniz.// Örnek: Application.onCreate veya DI module içinde val prodConfig = ECVSdkConfiguration( serviceUrl = "https://sizin-prod.ecv.example.com", ) val sdkInstance = ECVSdk("MyAppProdInstance", prodConfig)// Örnek: Application onCreate veya DI module içinde ECVSdkConfiguration prodConfig = new ECVSdkConfiguration( /* serviceUrl */ "https://sizin-prod.ecv.example.com", // ... diğer constructor args ... /* useQueueSocket */ true // Backend'in v1.75+ olduğunu varsayarsak // ... ); ECVSdk sdkInstance = new ECVSdk("MyAppProdInstance", prodConfig); // sdkInstance'ı global olarak saklayın -
Dinamik Configuration:
ECVSdk'yı initialize etmeden önce kendi uygulama backend'inizden configuration ayrıntılarını (serviceUrl,webrtcUrlgibi) dinamik olarak alın. Bu, uygulama güncellemeleri gerektirmeden endpoints merkezi olarak yönetmenizi sağlar.-
Uygulamanız config'i server'ınızdan alır.
-
ECVSdkConfigurationoluşturmak için alınan URL'leri kullanır. -
ECVSdk'yı initialize eder.
-
3. Çağrı Data (DialCallParameters)
Demo, seçilen environment'a göre çağrı data'sı için dinamik olarak dropdown'lar eklemek üzere EnvironmentCallAttribute kullanır. Bu, demo'nun çoklu environment setup'ına özgüdür.
Uygulamanızda, videoCall.start() çağırmadan hemen önce DialCallParameters'ı ilgili bağlamsal data ile doldurmalısınız.
Örnek:
// User profile data
val userId = "user123"
val accountType = "Premium"
val intent = "Billing Inquiry"
// Hedef queue/skill
val targetQueue = "BillingSupport"
// SDK instance'ını alın ve çağrı oluşturun
val videoCall = sdkInstance.createCall()
// ... GotRtcId bekle ...
// Uygulamaya özgü data ile parameters oluşturun
val dialParams = DialCallParameters(
callerName = "User $userId", // Veya gerçek user adı
attributes = mapOf(
"userId" to userId,
"accountType" to accountType,
"callIntent" to intent
),
acdAttributes = mapOf(
"skill" to "Billing" // Örnek ACD attribute
),
queue = targetQueue, // Hedef queue'yu açıkça ayarlayın
additionalHeaders = mapOf("Authorization" to "Bearer ${getUserAuthToken()}") // Örnek auth
)
// Çağrıyı başlatın
videoCall.start(dialParams)
// Queue/Bekleme ekranına gidin
// User profile data
String userId = "user123";
String accountType = "Premium";
String intent = "Billing Inquiry";
// Hedef queue/skill
String targetQueue = "BillingSupport";
// SDK instance'ını alın ve çağrı oluşturun
VideoCall videoCall = MyApp.getSdkInstance().createCall();
// ... GotRtcId event'ini subscribe kullanarak ele alın ...
// GotRtcId event'inin tetiklendiğini ve dial etmeye hazır olduğunuzu varsayın:
// Uygulamaya özgü data ile parameters oluşturun
Map<String, String> attributes = new HashMap<>();
attributes.put("userId", userId);
attributes.put("accountType", accountType);
attributes.put("callIntent", intent);
Map<String, String> acdAttributes = new HashMap<>();
acdAttributes.put("skill", "Billing");
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Bearer " + getUserAuthToken()); // Örnek auth
DialCallParameters dialParams = new DialCallParameters(
/* callerName */ "User " + userId, // Veya gerçek user adı
/* attributes */ attributes,
/* acdAttributes */ acdAttributes,
/* queue */ targetQueue, // Hedef queue'yu açıkça ayarlayın
/* customPayload */ new HashMap<>(),
/* additionalHeaders */ headers
);
// Çağrıyı başlatın
videoCall.start(dialParams);
// Queue/Bekleme ekranına gidin
4. Event Handling Logic
Demo, events nasıl abone olunacağını gösterirken, yanıt olarak ne yapacağınız uygulamaya özgüdür. Implementation'ınızın aşağıdakiler için anahtar events doğru şekilde ele aldığından emin olun:
5. Gelişmiş Özelleştirme (İsteğe bağlı)
-
Custom Data Channel Mesajları: Backend'iniz text data channel aracılığıyla custom komutlar gönderiyorsa (HOLD, FLASH vb. ötesinde),
TextChannelMessageevent'ini dinleyebilir ve custom uygulama davranışı implement etmek içinevent.detailparse edebilirsiniz. -
ConnectParametersAyarlama: Belirli network environment'ları veya kalite gereksinimleri için,ConnectParameters'dakivideoMaxBitrate, codecs veya ICE filtering modes gibi parameters ayarlamayı deneyebilirsiniz. Bu gelişmiş seçeneklerin ayrıntıları için WebRTC dokümantasyonuna başvurun. -
Audio Management: SDK dahili olarak
AppRTCAudioManagerkullanır.ConnectParameters'da defaults (useSpeakerPhone,autoSwitchToEarPiece) sağlansa da, daha karmaşık audio routing, temel SDK kullanımının kapsamı dışında daha derin entegrasyon veya özelleştirme gerektirebilir.
6. Permissions
Her zaman Android'in permissions isteme için en iyi practice'lerini izleyin:
-
Yalnızca gerektiğinde permissions isteyin (çağrıyı başlatmadan hemen önce).
-
Permissions (Kamera, Mikrofon) neden gerekli olduğunu açıkça açıklayın.
-
User permissions reddettiği durumları zarifçe ele alın (örn. arama işlevini devre dışı bırakın, ayarlarda nasıl etkinleştirileceğine dair talimatlar sağlayın).
7. Ek Modüller
Aşağıdaki maddeler ECV sistemi üzerinde çalıştırılabilecek ek modüllerin mobil uygulama içerisinde olan etkisini anlatmaktadır. Bu modüller default olarak aktif değildir. Aktif edilmesi için CCR satış temsilciniz ile iletişime geçmeniz gerekmektedir.
7.1 Çağrı Transferi v1.0.18.45+
Bu modül ile aktif devam eden bir çağrının farklı bir agent/kuyruk seçerek yönlendirilmesi sağlanmaktadır. Transfer edilen bir çağrının lifecycle'ı aşağıdaki şekildedir;
- Kaynak çağrı dial edilir. (çağrı oluşturma)
- Kaynak çağrı kuyrukta bekler.
- Kaynak çağrı bağlanır
- Agent transfer başlatır
- Transfer kuyruğa veya doğrudan farklı bir agent'a olabilir.
- Doğrudan agent'a yapılan transfer iptal edilebilir veya agent tarafından reject edilebilir.
- videoCall.eventFlow üzerinden "TransferPending" eventi gelir
- Bu event geldiğinde kullanıcıya arayüzde "Lütfen bekleyin çağrınınız birazdan transfer edilecek." veya benzeri bir mesaj gösterilebilir.
- Bu event sonrasında T anında TransferCalceled eventi gelebilir, cancel eventi geldiğinde gösterilen uyarı gizlenmelidir. Çağrı aynen devam edecektir.
- Agent transfer etmeden önce çağrıyı HOLD statüsüne geçirmiş olabilir, bu nedenle transfer mesajı HOLD arayüzünden daha önde olmalıdır.
- Transfer tamamlandığında eventFlow üzerinden TransferCompleted eventi gelecektir. event.detail içerisinde transfer edilen çağrıya atanan yeni RtcId değeri bulunmaktadır.
- Mevcut çağrı view'i ve bağlı bütün kaynaklar destroy edilir.
- Kullanıcı tekrar kuyruk sayfasına, bu sefer yeni RtcId ile gönderilir.
- Aynı yeni çağrı oluşturulmuş gibi kuyrukta bekler ve agent hazır olduğunda çağrıya AgentConnected eventi ile çağrılır.
Bu senaryoya ait implementasyon örneği demo uygulama içerisinde mevcuttur.
Sorun Giderme Kılavuzu
Bu kılavuz, ECV Android SDK'sını entegre ederken veya kullanırken karşılaşılan yaygın sorunlar için çözümler ve debug adımları sunar.
Yaygın Sorunlar ve Çözümler
Build & Dependency Hataları
-
Hata:
Could not find :ECV-175-SDK-vX.Y.Z:(veya diğer AAR'lar)-
Neden: Gradle gerekli
.aardosyalarını bulamıyor. -
Çözüm:
-
Üç gerekli
.aardosyasının (ECV SDK,M134-libwebrtc, `autobahn-android-legacy-20.2.1`) doğrudan projenizinapp/libsdizinine kopyalandığından emin olun. -
Uygulama düzeyindeki
build.gradle/.ktsdosyanızınrepositories { ... }bloğu içindeflatDir { dirs 'libs' }girişini içerdiğini doğrulayın. -
Dependencies bloğunuzdaki
implementation(name: '...', ext: 'aar')satırlarınınlibsdizinindeki dosya adlarıyla (.aaruzantısı olmadan) tam olarak eşleştiğini onaylayın. -
Bir Gradle Sync yapın (
File > Sync Project with Gradle Files).Build > Clean ProjectveBuild > Rebuild Projectdeneyin.
-
-
-
Hata:
Unresolved reference: lifecycleveyaandroidx.lifecycleile ilgili benzer hatalar-
Neden: Gerekli
androidx.lifecycle:lifecycle-runtime-ktxdependency'si uygulamanızın build dosyasında eksik. SDK bunu dahili olarak kullanır ancakapiaracılığıyla expose etmez. -
Çözüm:
implementation("androidx.lifecycle:lifecycle-runtime-ktx:VERSION")dependency'sini uygulama düzeyindekibuild.gradle/.ktsdosyanıza ekleyin,VERSIONyerine projenizle uyumlu güncel bir stabil version (örn.2.7.0) yazın. Detaylar içinREADME_tr.md'ye bakın. Gradle'ı senkronize edin.
-
-
Hata: Duplicate Class Found...
-
Neden: Projeniz veya başka bir library, SDK'nın AAR'ları tarafından dahili olarak kullanılan dependency'lerin çakışan version'larını içeriyor (örn. farklı WebRTC veya Autobahn version'ları, potansiyel Kotlin standard library sorunları).
-
Çözüm:
-
Çakışan library'leri tanımlamak için build hata mesajını analiz edin.
-
Transitive dependency'leri exclude etmek veya belirli version'ları force etmek için Gradle'ın dependency resolution stratejilerini kullanın. Örnek (Groovy):
implementation('some.other.library:1.0') { // ECV SDK'nın dependency'leri tarafından sağlanan çakışan bir module exclude et exclude group: 'org.webrtc', module: 'google-webrtc' } -
Projenizin core library'lerin (Kotlin gibi) uyumlu version'larını kullandığından emin olun.
-
-
-
Hata: Manifest Merger Failed (Permissions)
-
Neden: SDK'nın manifest'i, uygulamanızın manifest'i ile çakışan permissions veya features declare edebilir.
-
Çözüm:
-
Build output'undaki belirli manifest merger hata mesajını inceleyin.
-
Uygulamanızın
AndroidManifest.xml'ininCAMERA,RECORD_AUDIO,INTERNET,MODIFY_AUDIO_SETTINGSve potansiyel olarakBLUETOOTH_CONNECT(API 31+ için) gibi gerekli permissions doğru şekilde declare ettiğinden emin olun. -
Gerekirse manifest merger override kurallarını (
tools:overrideLibrary,tools:node="replace") dikkatli kullanın, ancak sonuçlarını anlayın. Genellikle, uygulamanızın gerekli permissions declare etmesi yeterlidir.
-
-
Runtime Crashes
-
Crash:
UnsatisfiedLinkError(org.webrtcveya native method'larla ilgili)-
Neden: Native WebRTC library'leri (
M134-libwebrtc.aariçindeki.sodosyaları) yüklenemedi. Bu genellikle architecture uyuşmazlıkları veya kurulum sorunlarından kaynaklanır. -
Çözüm:
-
M134-libwebrtc.aar'ın doğru şekilde dahil edildiğinden ve Gradle tarafından işlendiğinden emin olun. -
Build'inizde ABI splits kullanıyorsanız, hedef cihaz/emulator için doğru architecture'ın
.sodosyalarının dahil edildiğinden emin olun. -
Projeyi clean edip rebuild yapın. Mümkünse farklı cihazlarda/emulator'lerde test edin.
-
Başka hiçbir library'nin native library yüklemesine müdahale etmediğini doğrulayın.
-
-
-
Crash:
NullPointerException-
Neden: Bir SDK object'ine initialize edilmeden önce veya cleanup yapıldıktan sonra erişilmesi.
-
Çözüm:
-
SDK Not Initialized:
sdkInstance'a (örn.MyApp.getSdkInstance()) erişmeye çalışmadan önceECVSdk(...)çağrıldığından emin olun. Log'larda initialization hatalarını kontrol edin. -
getVideoCallNull Döndürüyor:rtcIdgeçersiz olabilir, çağrı zaten sona ermiş olabilir (CallEndedevent'i) veya SDK instance'ı yeniden initialize edilmiş olabilir. Özellikle bir Activity/Fragment'ı resume ederken veya navigate ederkenVideoCallobject'ini kullanmadan öncegetVideoCall(rtcId)'nin dönüş değerini her zaman null için kontrol edin. -
WebRtcConnectionNull:videoCall.connect()'inAgentConnectedevent'inden sonra vewebRtcConnection'a erişmeden önce başarıyla çağrıldığından emin olun.connect()veyastartRoom()process'i sırasındaki hataları kontrol edin. -
Context Null: Gerektiğinde geçerli
Contextiletildiğinden emin olun (örn.startRoom).
-
-
-
Crash:
SurfaceViewRenderer.release()veya EGL hatalarıyla ilgili-
Neden:
SurfaceViewRenderer'ın yanlış lifecycle management'ı. -
Çözüm:
-
Release MUTLAKA çağrılmalı:
surfaceViewRenderer.release()'in uygun lifecycle callback'inde (onReleaseComposeAndroidView'da,onDestroyView/onDestroyFragments/Activities için) çağrıldığından emin olun. -
Release Öncesi Detach: Kritik olarak,
release()çağırmadan önce renderer üzerindewebRtcConnection.detachLocalView(...)vewebRtcConnection.detachRemoteView(...)çağırın. -
Threading:
init,attach/detachverelease'in doğru thread'den (genellikle main UI thread) çağrıldığından emin olun. -
Double Release:
release()'in aynı renderer instance'ında birden çok kez çağrılmadığından emin olun.
-
-
-
Crash:
SecurityException(Permission Reddi)-
Neden: User tarafından verilen gerekli runtime permissions olmadan kamerayı veya mikrofonu başlatmaya çalışmak.
-
Çözüm:
-
videoCall.start()veyawebRtcConnection.startRoom()çağırmadan önce sağlam bir permission request mantığı implement edin. -
Çağrı işlevini etkinleştirmeden önce permission durumunu kontrol edin.
-
User permissions reddettiği durumları zarifçe ele alın. Bir örnek için demo'nun
MainScreen.kt'sine bakın.
-
-
Media Quality Sorunları
-
Kötü Video/Ses Kalitesi (Lag, Donma, Pixelation, Bozuk Ses):
-
Debugging:
-
Network: Bu en yaygın nedendir. Cihazdaki network bandwidth, latency ve packet loss kontrol edin (örn. hız testi uygulamaları, ping kullanarak). Farklı network'lerde test edin (Wi-Fi vs Cellular).
-
Cihaz Performance: Eski veya düşük seviye cihazlar, özellikle daha yüksek resolutions'da encoding/decoding ile mücadele edebilir.
-
ConnectParameters: Bandwidth sınırlıysavideoWidth/videoHeight/videoFpsdüşürmeyi veya birvideoMaxBitrateayarlamayı düşünün. MümkünsevideoCodecHwAcceleration'ın etkinleştirildiğinden emin olun. -
WebRTC Stats: Ayrıntılı WebRTC statistics toplamak için
peerConnectionClient.enableStatsEventskullanın (ancak bunları yorumlamak uzmanlık gerektirir).
-
-
Özel Feature Sorunları
-
Kamera Değiştirme Çalışmıyor:
-
Debugging:
-
webRtcConnection.switchCamera()çağrıldığından emin olun. -
Switch denemesi sırasındaki hatalar için log'ları kontrol edin.
-
Cihazın gerçekten birden fazla kamerası (ön/arka) olduğunu doğrulayın.
-
-
-
Hold/Pickup Otomatik Olarak Sessize Almıyor/Açmıyor:
-
Debugging:
-
ConnectParametersiçindeautoHandleHoldSideEffects = trueolduğundan emin olun (bu default'tur). -
StartHold/EndHoldevents doğru şekilde alınıp alınmadığını kontrol edin. -
"HOLD|" veya "PICKUP|" ile ilgili
TextChannelMessageevents alınıp alınmadığını kontrol edin. Text channel'ın bağlı olduğunu doğrulayın (TextChannelConnectedevent'i).
-
-
Genel Debugging Adımları
-
Logcat'i Kontrol Edin: Bu en önemli aracınızdır. SDK'da kullanılan tag'lere (
ECVSdk,VideoCall,WebRtcConnection,Signaling,QueueSocket,PeerConnectionClient,AppRTCAudioManager) ve kendi uygulama tag'lerinize (AppState,MainScreen,CallScreen, vb.) göre filtreleyin. Warnings ve errors arayın. Gerekirse loglama ayrıntı düzeyini artırın (SDK standart Android loglama seviyelerini kullanır). -
Configuration Doğrulayın:
ECVSdkConfiguration(URL'ler,useQueueSocket),DialCallParameters(attributes, queue) ve herhangi bir customConnectParametershedef environment'ınız ve gereksinimlerinizle dikkatlice karşılaştırın. -
Network Connectivity Kontrol Edin: Cihaz
serviceUrl,webrtcUrlvesignalingUrl'e ulaşabiliyor mu? Browser/ping/network araçları kullanın. Wi-Fi/Cellular connection durumunu kontrol edin. Farklı network'leri test edin. Varsa kurumsal firewall'lar hakkında bilgi alın. -
Basitleştirin: Karmaşık UI'yi, custom logic'i veya isteğe bağlı SDK feature'larını geçici olarak kaldırın. En temel, default configurations kullanın (
ECVSdkConfiguration,DialCallParameters,ConnectParameters). Basit bir çağrı çalışıyor mu? Karmaşıklığı kademeli olarak yeniden ekleyin. -
Phase'i İzole Edin: Hatanın ne zaman meydana geldiğini belirleyin: SDK init? Dialing? Queue? WebRTC connection? Media rendering? Bu, aramayı daraltmaya yardımcı olur.
-
Demo Uygulamasına Başvurun: Implementation logic'inizi, özellikle event handling, navigation ve
SurfaceViewRendererlifecycle management etrafında, sağlanan demo kaynak koduyla dikkatlice karşılaştırın. -
Tüm Events Loglayın: Aldığınız her
VideoCallEvent'i yazdırmak için event handling callback/collector'ınızın içine loglama ekleyin. Bu, çağrı flow'unu izlemeye ve nerede yanlış gidebileceğini veya hangi beklenen events eksik olduğunu görmeye yardımcı olur.
Desteğe Başvurma
Bu adımları izledikten sonra sorunlarla karşılaşmaya devam ederseniz, lütfen desteğe başvururken aşağıdaki bilgileri hazırlayın:
-
SDK Version: (örn. ECV-175-SDK-v1.2.3)
-
Test Edilen Cihaz(lar): Üretici, Model, Android OS Version.
-
Backend Version: v1.75+
-
Configuration:
ECVSdkConfiguration'ınız, ilgiliDialCallParametersve herhangi bir customConnectParameters. -
Ayrıntılı Sorun Açıklaması: Belirtiler nelerdir? Sorunu hangi adımlar yeniden oluşturuyor? Beklenen ve gerçek davranış nedir?
-
İlgili Logcat Output: Sorunun meydana geldiği sırada, SDK tag'lerini ve uygulama tag'lerinizi içeren log'ları yakalayın. Mümkünse uygun şekilde filtreleyin, ancak context sağlayın.
-
Screenshots/Videolar: Sorunu göstermeye yardımcı olacaksa.

