Skip to content
  • Auto
  • Light
  • Dark
Download Beeper

Integration Guide

Beeper uses custom Android permissions that must be declared in your app’s manifest:

AndroidManifest.xml
<uses-permission android:name="com.beeper.android.permission.READ_PERMISSION" />
<uses-permission android:name="com.beeper.android.permission.SEND_PERMISSION" />

Use ContentObserver to react to data changes in real-time:

  1. Create an observer

    class ChatObserver(handler: Handler) : ContentObserver(handler) {
    override fun onChange(selfChange: Boolean) {
    // Data changed - refresh your UI
    refreshChatList()
    }
    }
  2. Register the observer

    import androidx.core.net.toUri
    val observer = ChatObserver(Handler(Looper.getMainLooper()))
    contentResolver.registerContentObserver(
    "content://com.beeper.api/chats".toUri(),
    true, // Notify for descendant URIs
    observer
    )
  3. Unregister when done

    override fun onDestroy() {
    super.onDestroy()
    contentResolver.unregisterContentObserver(observer)
    }
class ChatViewModel : ViewModel() {
private var observer: ContentObserver? = null
fun startObserving(contentResolver: ContentResolver) {
observer = object : ContentObserver(Handler(Looper.getMainLooper())) {
override fun onChange(selfChange: Boolean) {
loadChats()
}
}
contentResolver.registerContentObserver(
"content://com.beeper.api/chats".toUri(),
true,
observer!!
)
}
override fun onCleared() {
observer?.let {
getApplication<Application>()
.contentResolver
.unregisterContentObserver(it)
}
}
}

Causes:

  • Beeper not installed/logged in
  • Missing permissions
  • Too restrictive filters

Solutions:

  • Verify Beeper installation
  • Check manifest permissions
  • Add limit parameter
  • Adjust filters

Causes:

  • Permissions not declared in manifest
  • Runtime permissions not granted

Solutions:

  • Add permissions to manifest
  • Request permissions at runtime

Causes:

  • Invalid room ID
  • Text not URL-encoded
  • Network issues

Solutions:

  • Verify roomId format
  • Use Uri.encode() for text
  • Check Beeper connectivity

Causes:

  • No ContentObserver registered
  • Wrong Handler/Looper
  • Observer not on main thread

Solutions:

  • Register ContentObserver
  • Use Handler(Looper.getMainLooper())
  • Refresh on main thread
  1. Verify Beeper installation

    val packageManager = context.packageManager
    try {
    packageManager.getPackageInfo("com.beeper.android", 0)
    // Beeper is installed
    } catch (e: PackageManager.NameNotFoundException) {
    // Beeper not installed
    }
  2. Check permissions

    val permissions = listOf(
    "com.beeper.android.permission.READ_PERMISSION",
    "com.beeper.android.permission.SEND_PERMISSION"
    )
    permissions.forEach { permission ->
    val granted = checkSelfPermission(permission) ==
    PackageManager.PERMISSION_GRANTED
    Log.d("Permissions", "$permission: $granted")
    }
  3. Test basic query

    try {
    val cursor = contentResolver.query(
    "content://com.beeper.api/chats?limit=1".toUri(),
    null, null, null, null
    )
    Log.d("Debug", "Query returned ${cursor?.count ?: 0} rows")
    cursor?.close()
    } catch (e: Exception) {
    Log.e("Debug", "Query failed", e)
    }
What’s the authority?
com.beeper.api
Do I need runtime permissions?
Yes. Request READ_PERMISSION and SEND_PERMISSION at runtime in addition to declaring them in the manifest.
What’s the default limit?
Messages and contacts default to 100 if not specified. Always specify limit for chats to avoid large queries.
Can I use this from a service?
Yes, ContentProviders work from any Android component with a Context.
// ✅ Good
Uri.encode("Hello & welcome!")
// ❌ Bad
"Hello & welcome!"
// Register in onStart
override fun onStart() {
super.onStart()
registerObserver()
}
// Unregister in onStop
override fun onStop() {
super.onStop()
unregisterObserver()
}
// Always check for null
val result = contentResolver.insert(uri, null)
if (result != null) {
// Success
} else {
// Handle failure
}
// ✅ Single query with multiple IDs
"roomIds=!room1:server,!room2:server"
// ❌ Multiple queries
query("roomIds=!room1:server")
query("roomIds=!room2:server")