Expo Receive Android Intents#
An Expo module that handles incoming Android intents for sharing text, images, and videos into the Bluesky app.
What It Does#
This module intercepts Android share intents (when a user shares content from another app to Bluesky) and converts them into deep links that the app can handle. It supports:
- Text sharing - Share plain text to compose a post
- Image sharing - Share single or multiple images (up to 4) to attach to a post
- Video sharing - Share a single video to attach to a post
The module operates entirely in native Android code and requires no JavaScript API calls. It automatically registers itself with Expo's module system and handles intents when the app is launched or receives new intents.
Platform Support#
- Android: Fully supported
- iOS: No-op (iOS handles share intents differently)
- Web: No-op
How It Works#
Architecture#
The module uses Expo's module lifecycle hooks to intercept Android intents at two key moments:
- OnCreate - When the app is first launched from an intent
- OnNewIntent - When the app receives a new intent while already running
Intent Processing Flow#
- Intent Reception: Android sends an
ACTION_SENDorACTION_SEND_MULTIPLEintent - Type Detection: Module determines content type (text, image, or video)
- Content Processing:
- Text: URL-encodes the text
- Images: Saves to app cache, extracts dimensions (limited to 4 images max)
- Video: Copies to app cache with extension detection, extracts dimensions
- Deep Link Generation: Creates a
bluesky://intent/composeURL with encoded parameters - App Launch: Starts a new activity with the deep link, which is handled by
useIntentHandler
Deep Link Format#
The module generates deep links in the following formats:
# Text only
bluesky://intent/compose?text=<encoded-text>
# Images (single or multiple)
bluesky://intent/compose?imageUris=<uri1>|<width>|<height>,<uri2>|<width>|<height>&text=<encoded-text>
# Video (single only)
bluesky://intent/compose?videoUri=<uri>|<width>|<height>&text=<encoded-text>
All URIs use the file:// scheme pointing to files in the app's cache directory. Dimensions are included to avoid expensive measurement operations in JavaScript.
Security Considerations#
- Images and videos are copied to the app's private cache directory before being passed to the app
- The JavaScript handler (
useIntentHandler.ts) validates image URIs with a regex to prevent external URLs - Image URIs containing
http://orhttps://are filtered out - Multiple image sharing is limited to 4 images maximum
Key Files#
Module Configuration#
- expo-module.config.json - Declares the module and registers it with Expo (Android-only)
Native Implementation#
-
ExpoReceiveAndroidIntentsModule.kt - Main module class with intent handling logic
handleIntent()- Routes intents based on typehandleTextIntent()- Processes text sharinghandleAttachmentIntent()- Processes single image/videohandleAttachmentsIntent()- Processes multiple imagesgetImageInfo()- Saves images to cache and extracts dimensionsgetVideoInfo()- Extracts video dimensions using MediaMetadataRetriever
-
android/build.gradle - Gradle build configuration
- Version: 0.4.1
- Requires: Kotlin, expo-modules-core
- Compile SDK: 33, Min SDK: 21, Target SDK: 34
-
android/src/main/AndroidManifest.xml - Empty manifest (intent filters configured in main app)
JavaScript Integration#
The deep links generated by this module are handled by:
- src/lib/hooks/useIntentHandler.ts -
useComposeIntent()parses the deep link parameters and opens the composer with pre-populated content
Installation#
No manual installation is required. Gradle automatically includes this module during the Android build process. The module is auto-linked through Expo's module system.
Configuration#
Intent filters must be configured in the main app's AndroidManifest.xml to declare which MIME types the app accepts. The module itself has an empty manifest.
Implementation Notes#
Android Version Compatibility#
The module uses version-specific APIs for Android 13+ (API 33):
getParcelableExtra()with type parameter on Android 13+- Legacy
getParcelableExtra()on older versions
File Handling#
- Temporary files are created using
File.createTempFile()in the app's cache directory - Image files use
.jpegextension and are compressed at 100% quality - Video files preserve their original extension, defaulting to
.mp4if none is detected
Limitations#
- Video sharing only supports a single video
- Multiple video sharing is not implemented
- Images are always converted to JPEG format
- Maximum of 4 images can be shared at once