Android SDK Usage & Integration
Learn how to initialize the DoorstepAI SDK and integrate delivery tracking into your Android application.
SDK Initialization
Activity-Level Initialization
Initialize the SDK in your main Activity, typically in onCreate()
:
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.runtime.*
import com.doorstepai.sdks.tracking.DoorstepAI
class MainActivity : ComponentActivity() {
companion object {
private const val FIXED_API_TOKEN = SOME_API_KEY_REF // Reference from an ENV
}
private var permissionsGranted by mutableStateOf(false)
private var sdkInitialized by mutableStateOf(false)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
checkPermissionsAndInitialize()
setContent {
// Your Compose UI
}
}
fun checkPermissionsAndInitialize() {
if (DoorstepAIPermissionUtils.hasAllPermissions(this)) {
permissionsGranted = true
initializeSDK()
} else {
permissionsGranted = false
}
}
private fun initializeSDK() {
try {
DoorstepAI.init(
context = this,
notificationTitle = "Tracking...",
notificationText = "Tracking your delivery"
) { result ->
result.fold(
onSuccess = {
try {
DoorstepAI.setAPIKey(FIXED_API_TOKEN)
println("✅ DoorstepAI SDK initialized successfully + API token set")
sdkInitialized = true
} catch (e: Exception) {
println("❌ Failed to set API token: ${e.message}")
sdkInitialized = false
}
},
onFailure = { error ->
println("❌ SDK initialization failed: ${error.message}")
sdkInitialized = false
}
)
}
} catch (e: Exception) {
println("❌ Error during SDK initialization: ${e.message}")
sdkInitialized = false
}
}
}
Store your API key securely using BuildConfig fields, environment variables, or a secure configuration service. Never hardcode API keys in production builds.
Initialization Parameters
Parameter | Type | Description | Required |
---|---|---|---|
context | Context | Application context | ✅ Required |
notificationTitle | String? | Title for tracking notifications | ✅ Required |
notificationText | String? | Description for tracking notifications | ✅ Required |
callback | (Result<Unit>) -> Unit | Initialization result callback | ⚠️ Recommended |
The notification title and text are used for the foreground service notification that appears when the SDK is actively tracking deliveries. These parameters are required for proper SDK initialization.
Core Functionality
Delivery IDs are designed to be unique identifiers for each delivery session. Use meaningful, unique identifiers that help you track and manage your deliveries effectively.
Starting Delivery Tracking
The SDK provides multiple methods to start delivery tracking based on different address formats:
1. Start by Place ID
DoorstepAI.startDeliveryByPlaceID(
placeID = "some_place_id",
deliveryId = "delivery_12345"
) { result ->
result.fold(
onSuccess = { message ->
Log.i("DoorstepAI", "✅ Delivery started successfully: $message")
// Handle successful start
updateDeliveryStatus(DeliveryStatus.ACTIVE)
},
onFailure = { error ->
Log.e("DoorstepAI", "❌ Failed to start delivery", error)
// Handle error
showErrorToUser(error.message)
}
)
}
2. Start by Plus Code
DoorstepAI.startDeliveryByPlusCode(
plusCode = "some_plus_code",
deliveryId = "delivery_12345"
) { result ->
result.fold(
onSuccess = { message ->
Log.i("DoorstepAI", "✅ Delivery started with Plus Code: $message")
},
onFailure = { error ->
Log.e("DoorstepAI", "❌ Plus Code delivery failed", error)
}
)
}
3. Start by Address Components
import com.doorstepai.sdks.tracking.AddressType
val address = AddressType(
streetNumber = "123",
route = "Main Street",
subPremise = "Apt 4B",
locality = "San Francisco",
administrativeAreaLevel1 = "CA",
postalCode = "94102"
)
DoorstepAI.startDeliveryByAddressType(
address = address,
deliveryId = "delivery_12345"
) { result ->
result.fold(
onSuccess = { message ->
Log.i("DoorstepAI", "✅ Address delivery started: $message")
},
onFailure = { error ->
Log.e("DoorstepAI", "❌ Address delivery failed", error)
}
)
}
Delivery Events
Track important delivery milestones using event reporting:
Proof of Delivery (POD) Events
// When driver starts taking a photo for proof of delivery
DoorstepAI.newEvent(
eventName = "taking_pod",
deliveryId = "delivery_12345"
) { result ->
result.fold(
onSuccess = { message ->
Log.i("DoorstepAI", "POD capture initiated")
},
onFailure = { error ->
Log.e("DoorstepAI", "POD capture failed")
}
)
}
// When proof of delivery photo is captured
DoorstepAI.newEvent(
eventName = "pod_captured",
deliveryId = "delivery_12345"
) { result ->
result.fold(
onSuccess = { message ->
Log.i("DoorstepAI", "POD captured successfully")
},
onFailure = { error ->
Log.e("DoorstepAI", "POD capture failed")
}
)
}
Stopping Delivery Tracking
Stop tracking when the delivery is complete:
// Stop delivery tracking
try {
DoorstepAI.stopDelivery("delivery_12345")
Log.i("DoorstepAI", "🛑 Delivery tracking stopped")
} catch (e: Exception) {
Log.e("DoorstepAI", "❌ Error stopping delivery: ${e.message}")
}
Address Types Reference
AddressType Data Class
data class AddressType(
val streetNumber: String?, // "123"
val route: String?, // "Main Street"
val subPremise: String?, // "Apt 4B", "Unit 2"
val locality: String?, // "San Francisco"
val administrativeAreaLevel1: String?, // "CA", "California"
val postalCode: String? // "94102"
)
Usage Examples
// Minimal address
val simpleAddress = AddressType(
streetNumber = "456",
route = "Oak Avenue",
subPremise = null,
locality = "Oakland",
administrativeAreaLevel1 = "CA",
postalCode = "94610"
)
// Complete address with apartment
val detailedAddress = AddressType(
streetNumber = "789",
route = "Pine Street",
subPremise = "Suite 100",
locality = "Berkeley",
administrativeAreaLevel1 = "California",
postalCode = "94704"
)
Runtime Permissions
Permission Request with Jetpack Compose
Use the permission launcher in your Compose UI:
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
@Composable
fun DoorstepSDKTestScreen(
context: MainActivity,
permissionsGranted: Boolean,
sdkInitialized: Boolean,
modifier: Modifier = Modifier
) {
var statusText by remember { mutableStateOf("Checking permissions...") }
// Permission launcher - automatically trigger when needed
val permissionLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.RequestMultiplePermissions()
) { permissions ->
val allGranted = permissions.all { it.value }
if (allGranted) {
statusText = "All permissions granted! Initializing SDK..."
context.checkPermissionsAndInitialize()
} else {
val deniedPermissions = permissions.filter { !it.value }.keys
statusText = "Some permissions were denied: ${deniedPermissions.joinToString(", ")}"
}
}
LaunchedEffect(permissionsGranted) {
if (!permissionsGranted) {
val missingPermissions = DoorstepAIPermissionUtils.getMissingPermissions(context)
if (missingPermissions.isNotEmpty()) {
permissionLauncher.launch(missingPermissions.toTypedArray())
}
}
}
LaunchedEffect(permissionsGranted, sdkInitialized) {
statusText = when {
!permissionsGranted -> "Requesting permissions for DoorstepAI SDK..."
!sdkInitialized -> "Initializing DoorstepAI SDK..."
else -> "DoorstepAI SDK ready"
}
}
// Your Compose UI here
}
Permission Helper Class
Use the provided DoorstepAIPermissionUtils
class to manage permissions:
// Check if all permissions are granted
if (DoorstepAIPermissionUtils.hasAllPermissions(context)) {
// Initialize SDK
initializeSDK()
} else {
// Request missing permissions
val missingPermissions = DoorstepAIPermissionUtils.getMissingPermissions(context)
// Launch permission request
}
// Get permission rationale for users
val rationale = DoorstepAIPermissionUtils.getPermissionRationale()
Best Practices
1. Error Handling
Always implement comprehensive error handling for SDK methods:
fun startDeliveryWithErrorHandling(placeID: String, deliveryId: String) {
DoorstepAI.startDeliveryByPlaceID(
placeID = placeID,
deliveryId = deliveryId
) { result ->
result.fold(
onSuccess = { message ->
Log.i("DeliveryTracker", "Success: $message")
// Update UI on main thread
runOnUiThread {
updateDeliveryStatus(DeliveryStatus.ACTIVE)
showSuccessMessage("Delivery tracking started")
}
},
onFailure = { error ->
Log.e("DeliveryTracker", "Error starting delivery", error)
// Handle specific error types
runOnUiThread {
when {
error.message?.contains("network", ignoreCase = true) == true -> {
showErrorMessage("Network error. Please check your connection.")
}
error.message?.contains("permission", ignoreCase = true) == true -> {
showErrorMessage("Location permission required.")
}
else -> {
showErrorMessage("Failed to start delivery tracking.")
}
}
}
}
)
}
}
2. Lifecycle Management
Handle Android lifecycle appropriately:
class DeliveryTrackingActivity : ComponentActivity() {
private var currentDeliveryId: String? = null
override fun onResume() {
super.onResume()
// SDK automatically handles activity lifecycle
// No additional action required
}
override fun onPause() {
super.onPause()
// SDK continues tracking in background
Log.d("DeliveryTracker", "Activity paused - tracking continues")
}
override fun onDestroy() {
super.onDestroy()
// Only stop delivery if this is the final activity
if (isFinishing && currentDeliveryId != null) {
// Consider if delivery should be stopped here
// Usually you want tracking to continue even if activity is destroyed
}
}
}
3. Thread Safety
Always update UI on the main thread:
private fun handleDeliveryCallback(result: Result<String>) {
// SDK callbacks may come from background threads
runOnUiThread {
result.fold(
onSuccess = { message ->
// Safe to update UI here
statusTextView.text = "Delivery Active: $message"
progressBar.visibility = View.GONE
},
onFailure = { error ->
// Safe to update UI here
statusTextView.text = "Error: ${error.message}"
progressBar.visibility = View.GONE
}
)
}
}
Next Steps
Now that you understand the core SDK functionality:
- 💡 View Complete Examples - See full implementation examples
- 🛠️ Troubleshooting Guide - Solve common integration and runtime issues
Need help with implementation? Check out our complete examples with error handling, permission management, and best practices.
:::