Integration Guide
Permissions, notifications, troubleshooting, and best practices for Beeper Content Provider integration
Permissions
Section titled “Permissions”Required Permissions
Section titled “Required Permissions”Beeper uses custom Android permissions that must be declared in your app’s manifest:
<uses-permission android:name="com.beeper.android.permission.READ_PERMISSION" /><uses-permission android:name="com.beeper.android.permission.SEND_PERMISSION" />| Permission | Type | Purpose |
|---|---|---|
READ_PERMISSION | Runtime | Query chats, messages, contacts |
SEND_PERMISSION | Runtime | Send messages |
// Check and request at runtimeval hasRead = ContextCompat.checkSelfPermission( this, "com.beeper.android.permission.READ_PERMISSION") == PackageManager.PERMISSION_GRANTED
val hasSend = ContextCompat.checkSelfPermission( this, "com.beeper.android.permission.SEND_PERMISSION") == PackageManager.PERMISSION_GRANTED
if (!hasRead || !hasSend) { val launcher = registerForActivityResult( ActivityResultContracts.RequestMultiplePermissions() ) { results -> val grantedRead = results["com.beeper.android.permission.READ_PERMISSION"] == true val grantedSend = results["com.beeper.android.permission.SEND_PERMISSION"] == true // Handle granted/denied states } launcher.launch(arrayOf( "com.beeper.android.permission.READ_PERMISSION", "com.beeper.android.permission.SEND_PERMISSION" ))}Change Notifications
Section titled “Change Notifications”ContentObserver Setup
Section titled “ContentObserver Setup”Use ContentObserver to react to data changes in real-time:
-
Create an observer
class ChatObserver(handler: Handler) : ContentObserver(handler) {override fun onChange(selfChange: Boolean) {// Data changed - refresh your UIrefreshChatList()}} -
Register the observer
import androidx.core.net.toUrival observer = ChatObserver(Handler(Looper.getMainLooper()))contentResolver.registerContentObserver("content://com.beeper.api/chats".toUri(),true, // Notify for descendant URIsobserver) -
Unregister when done
override fun onDestroy() {super.onDestroy()contentResolver.unregisterContentObserver(observer)}
Common Observer Patterns
Section titled “Common Observer Patterns”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) } }}Only the content://com.beeper.api/chats URI currently emits change notifications. Observers on messages and contacts are not implemented yet.
Troubleshooting
Section titled “Troubleshooting”Common Issues & Solutions
Section titled “Common Issues & Solutions”No rows returned
Section titled “No rows returned”Causes:
- Beeper not installed/logged in
- Missing permissions
- Too restrictive filters
Solutions:
- Verify Beeper installation
- Check manifest permissions
- Add
limitparameter - Adjust filters
Permission denied
Section titled “Permission denied”Causes:
- Permissions not declared in manifest
- Runtime permissions not granted
Solutions:
- Add permissions to manifest
- Request permissions at runtime
Message send fails
Section titled “Message send fails”Causes:
- Invalid room ID
- Text not URL-encoded
- Network issues
Solutions:
- Verify
roomIdformat - Use
Uri.encode()for text - Check Beeper connectivity
UI not updating
Section titled “UI not updating”Causes:
- No ContentObserver registered
- Wrong Handler/Looper
- Observer not on main thread
Solutions:
- Register ContentObserver
- Use
Handler(Looper.getMainLooper()) - Refresh on main thread
Debug Checklist
Section titled “Debug Checklist”-
Verify Beeper installation
val packageManager = context.packageManagertry {packageManager.getPackageInfo("com.beeper.android", 0)// Beeper is installed} catch (e: PackageManager.NameNotFoundException) {// Beeper not installed} -
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_GRANTEDLog.d("Permissions", "$permission: $granted")} -
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)}
Frequently Asked Questions
Section titled “Frequently Asked Questions”- What’s the authority?
com.beeper.api- Do I need runtime permissions?
- Yes. Request
READ_PERMISSIONandSEND_PERMISSIONat 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
limitfor chats to avoid large queries. - Can I use this from a service?
- Yes, ContentProviders work from any Android component with a Context.
- How do I open at the first unread message?
- Use
openAtUnread=truewith exactly oneroomIdand no other filters. - Can I send media messages?
- Currently only text messages are supported via the ContentProvider API.
- Is pagination cursor-based or offset-based?
- Offset-based. Use
limitandoffsetparameters. - Are queries case-sensitive?
- Text searches are case-insensitive. Room/sender IDs are case-sensitive.
- What’s the maximum limit?
- No hard limit, but use reasonable values (50-200) for UI responsiveness.
- Can I cache query results?
- Yes, but use ContentObserver to invalidate cache on changes.
- How often do observers trigger?
- Immediately on data change. Consider debouncing for high-frequency updates.
- Should I close Cursors?
- Always. Use try-with-resources or Kotlin’s
.use{}extension.
Best Practices
Section titled “Best Practices”Always URL Encode
Section titled “Always URL Encode”// ✅ GoodUri.encode("Hello & welcome!")
// ❌ Bad"Hello & welcome!"Use Lifecycle Components
Section titled “Use Lifecycle Components”// Register in onStartoverride fun onStart() { super.onStart() registerObserver()}
// Unregister in onStopoverride fun onStop() { super.onStop() unregisterObserver()}Handle Null Results
Section titled “Handle Null Results”// Always check for nullval result = contentResolver.insert(uri, null)if (result != null) { // Success} else { // Handle failure}Batch Operations
Section titled “Batch Operations”// ✅ Single query with multiple IDs"roomIds=!room1:server,!room2:server"
// ❌ Multiple queriesquery("roomIds=!room1:server")query("roomIds=!room2:server")Testing Tips
Section titled “Testing Tips”Need Help?
Section titled “Need Help?”- Check the API Reference for detailed endpoint documentation
- Review the Quick Start for basic examples
- Contact info@beeper.com for integration support