Skip to Content
Dial v1 live 🎉
SdkAndroid SDK (Kotlin)

Android SDK (Kotlin)

Native Android SDK built with Kotlin for optimal performance and native Android integration.

Status

🔄 Coming Q3 2025 - Currently in development

Join the waitlist  to be notified when available.

Planned Features

Core Features

  • ✅ Wallet-to-wallet calls (audio/video)
  • ✅ Native ConnectionService integration
  • ✅ Push notifications (FCM)
  • ✅ Messaging
  • ✅ Voicemail
  • ✅ Call history

Android-Specific

  • ✅ ConnectionService integration
  • ✅ Notification channels
  • ✅ Picture-in-Picture mode
  • ✅ Android Auto support
  • ✅ Widgets
  • ✅ Material Design 3 components
  • ✅ Jetpack Compose support

Requirements

  • Android 8.0+ (API 26+)
  • Kotlin 1.9+
  • Android Studio Hedgehog or later

Planned Installation

Gradle (Kotlin DSL)

dependencies { implementation("wtf.dial:sdk-android:1.0.0") }

Gradle (Groovy)

dependencies { implementation 'wtf.dial:sdk-android:1.0.0' }

Planned API

Setup

import wtf.dial.sdk.DialClient val dial = DialClient( context = applicationContext, apiKey = "your-api-key", walletAddress = "0x..." )

Authentication

// Sign message with wallet val signature = wallet.signMessage("Authenticate with Dial") dial.authenticate( walletAddress = "0x...", signature = signature )

Making Calls

val call = dial.calls.start( to = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", type = CallType.VIDEO )

ConnectionService Integration

import android.telecom.ConnectionService import wtf.dial.sdk.DialConnectionService class MyConnectionService : DialConnectionService() { override fun onCreateIncomingConnection( connectionManagerAccount: PhoneAccountHandle, request: ConnectionRequest ): Connection { // Automatically integrates with Android's native call UI return createDialConnection(request) } }

Add to AndroidManifest.xml:

<service android:name=".MyConnectionService" android:permission="android.permission.BIND_TELECOM_CONNECTION_SERVICE"> <intent-filter> <action android:name="android.telecom.ConnectionService" /> </intent-filter> </service>

Jetpack Compose Integration

import androidx.compose.runtime.* import wtf.dial.sdk.compose.* @Composable fun CallScreen() { val callState = rememberDialCallState() val dial = LocalDial.current Column { if (callState.currentCall != null) { when (callState.currentCall.status) { CallStatus.RINGING -> IncomingCallView(callState.currentCall) CallStatus.ACTIVE -> ActiveCallView(callState.currentCall) else -> {} } } else { Button(onClick = { callState.startCall( to = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", type = CallType.VIDEO ) }) { Text("Make Call") } } } } @Composable fun IncomingCallView(call: DialCall) { Column( modifier = Modifier.fillMaxSize(), horizontalAlignment = Alignment.CenterHorizontally, verticalArrangement = Arrangement.Center ) { Text( text = "Incoming call from", style = MaterialTheme.typography.bodyLarge ) Text( text = call.from, style = MaterialTheme.typography.headlineMedium ) Row( modifier = Modifier.padding(top = 32.dp), horizontalArrangement = Arrangement.spacedBy(32.dp) ) { FloatingActionButton( onClick = { call.decline() }, containerColor = MaterialTheme.colorScheme.error ) { Icon(Icons.Default.CallEnd, "Decline") } FloatingActionButton( onClick = { call.answer() }, containerColor = MaterialTheme.colorScheme.primary ) { Icon(Icons.Default.Call, "Answer") } } } }

Traditional View System

import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import wtf.dial.sdk.DialClient import wtf.dial.sdk.DialCallListener class CallActivity : AppCompatActivity(), DialCallListener { private lateinit var dial: DialClient private var currentCall: DialCall? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_call) dial = DialClient.getInstance() dial.addCallListener(this) } fun makeCall(recipient: String) { lifecycleScope.launch { currentCall = dial.calls.start( to = recipient, type = CallType.VIDEO ) } } override fun onIncomingCall(call: DialCall) { currentCall = call showIncomingCallUI(call) } }

Messaging

// Send message dial.messages.send( to = "0x742d35Cc6634C0532925a3b844Bc9e7595f0bEb", content = "Hello!", type = MessageType.TEXT ) // Listen for messages dial.messages.onReceived { message -> println("New message from ${message.from}: ${message.content}") }

Push Notifications (FCM)

import com.google.firebase.messaging.FirebaseMessagingService import com.google.firebase.messaging.RemoteMessage import wtf.dial.sdk.DialNotificationHandler class MyFirebaseMessagingService : FirebaseMessagingService() { override fun onMessageReceived(remoteMessage: RemoteMessage) { DialNotificationHandler.handleNotification( context = this, data = remoteMessage.data ) } override fun onNewToken(token: String) { DialClient.getInstance().registerPushToken(token) } }

Coroutines Support

Full support for Kotlin Coroutines:

// All API calls are suspend functions lifecycleScope.launch { val call = dial.calls.start(to = "0x...", type = CallType.AUDIO) val messages = dial.messages.getHistory(with = "0x...") val voicemails = dial.voicemail.getAll() }

Flow Support

Reactive streams with Kotlin Flow:

import kotlinx.coroutines.flow.* class CallViewModel : ViewModel() { val currentCall: StateFlow<DialCall?> = dial.callFlow.stateIn( viewModelScope, SharingStarted.Lazily, null ) val messages: StateFlow<List<DialMessage>> = dial.messagesFlow.stateIn( viewModelScope, SharingStarted.Lazily, emptyList() ) }

Android Auto Integration

import androidx.car.app.CarContext import androidx.car.app.Screen import wtf.dial.sdk.auto.DialAutoScreen class DialAutoCallScreen(carContext: CarContext) : DialAutoScreen(carContext) { override fun onGetTemplate(): Template { return createCallTemplate() } }

Add to AndroidManifest.xml:

<meta-data android:name="com.google.android.gms.car.application" android:resource="@xml/automotive_app_desc" />

Widgets

import android.appwidget.AppWidgetProvider import wtf.dial.sdk.widget.CallHistoryWidget class DialWidget : CallHistoryWidget() { override fun onUpdate( context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray ) { // Automatically displays recent calls super.onUpdate(context, appWidgetManager, appWidgetIds) } }

Picture-in-Picture

import android.app.PictureInPictureParams import android.util.Rational fun enterPipMode() { val params = PictureInPictureParams.Builder() .setAspectRatio(Rational(16, 9)) .build() enterPictureInPictureMode(params) } override fun onPictureInPictureModeChanged( isInPictureInPictureMode: Boolean, newConfig: Configuration ) { if (isInPictureInPictureMode) { // Hide UI elements hideCallControls() } else { // Show UI elements showCallControls() } }

Permissions

Add to AndroidManifest.xml:

<uses-permission android:name="android.permission.INTERNET" /> <uses-permission android:name="android.permission.CAMERA" /> <uses-permission android:name="android.permission.RECORD_AUDIO" /> <uses-permission android:name="android.permission.BLUETOOTH" /> <uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" /> <uses-permission android:name="android.permission.POST_NOTIFICATIONS" /> <!-- For ConnectionService --> <uses-permission android:name="android.permission.MANAGE_OWN_CALLS" />

Runtime permission handling:

val permissions = arrayOf( Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO ) ActivityCompat.requestPermissions(this, permissions, REQUEST_CODE)

ProGuard Rules

Add to proguard-rules.pro:

-keep class wtf.dial.sdk.** { *; } -keepclassmembers class wtf.dial.sdk.** { *; }

Performance

  • Native Kotlin implementation
  • Hardware-accelerated codecs
  • Battery optimization
  • Background execution limits handling
  • Low memory footprint

Material Design 3

Full Material You support:

@Composable fun DialTheme( darkTheme: Boolean = isSystemInDarkTheme(), dynamicColor: Boolean = true, content: @Composable () -> Unit ) { val colorScheme = when { dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> { if (darkTheme) dynamicDarkColorScheme(LocalContext.current) else dynamicLightColorScheme(LocalContext.current) } darkTheme -> darkColorScheme() else -> lightColorScheme() } MaterialTheme( colorScheme = colorScheme, content = content ) }

Roadmap

Q3 2025 - Alpha Release

  • Core calling features
  • ConnectionService integration
  • Push notifications
  • Messaging

Q4 2025 - Beta Release

  • Video conferencing
  • Android Auto support
  • Widgets
  • Picture-in-Picture

Q1 2026 - Stable Release

  • Full feature parity
  • Material Design 3
  • Production-ready
  • Complete documentation

Example App

A complete example app will be available:

  • GitHub Repository 
  • Jetpack Compose implementation
  • Traditional Views implementation
  • Best practices
  • Material Design 3 patterns

Get Notified

Want early access?

Next Steps

Last updated on