[webhooks] add mms:downloaded webhook event#329
Conversation
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
🤖 Pull request artifacts
|
WalkthroughAdds end-to-end MMS downloaded handling: content observer and reader, persisted last-processed MMS ID, new full-MMS model and headers type, ReceiverService wiring to emit a new Changes
Sequence Diagram(s)sequenceDiagram
participant RS as ReceiverService
participant SS as StateStorage
participant Obs as MmsContentObserver
participant CP as AndroidContentProvider
participant Reader as MmsContentReader
participant WH as WebHooksService
RS->>Obs: start()
Obs->>SS: read mmsLastProcessedID
Obs->>CP: register observer, query max(_id)
CP-->>Obs: currentMaxId
Obs->>CP: query new MMS (id > lastId, m_type=132, msg_box=1)
CP-->>Obs: [mmsId...]
Obs->>RS: notify(mmsId)
RS->>Reader: read(context, mmsId)
Reader->>CP: query metadata, addr, parts, part data
CP-->>Reader: metadata, sender, parts, data
Reader-->>RS: MmsMessage (body, subject, attachments, date, subscriptionId)
RS->>WH: emit(WebHookEvent.MmsDownloaded, MmsDownloadedPayload)
WH-->>RS: ack
RS->>SS: set mmsLastProcessedID(newLastId)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
📝 Coding Plan
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.kt`:
- Around line 10-12: The observer currently advances the high-water mark
unconditionally after invoking onMmsDownloaded, which allows failed/transient
reads to be skipped forever; change the onMmsDownloaded callback signature in
MmsContentObserver from (mmsId: Long) -> Unit to (mmsId: Long) -> Boolean, call
it and only update newMark when it returns true, leaving the mark unchanged on
false so the MMS ID remains retryable; update all callers (notably
ReceiverService.processMmsDownloaded()) to return false for soft/transient
failures and true on success.
In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.kt`:
- Around line 82-91: The loop that reads MMS parts (partCursor) only uses the
text column for contentType "text/plain" and ignores file-backed parts; add a
helper function readTextPart(context, partId) that opens
content://mms/part/{partId} via contentResolver.openInputStream, reads and
decodes the stream to a String (respecting charset if available), and returns
the text; in the part-reading loop (where contentType == "text/plain") call
readTextPart when c.getString(2) is null or blank and add the result to
bodyParts if non-blank, making sure to catch/handle IO exceptions and always
close streams.
In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt`:
- Around line 153-155: The payload construction in ReceiverService uses the
provider row ID (mmsId) for MmsDownloadedPayload.messageId, which breaks
correlation with process() that emits mms:received using message.messageId ?:
message.transactionId; update the code that builds MmsDownloadedPayload so it
uses the same identifier logic (message.messageId ?: message.transactionId)
instead of mmsId, and if those header IDs are not available from
MmsContentReader.read()/MmsContentReader.MmsMessage, modify
MmsContentReader.read() and its MmsMessage type to surface header identifiers
(messageId and transactionId) so the downloader can set the same identifier in
MmsDownloadedPayload.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 64dbe45f-ac82-4d77-a72e-7c2ccf444621
📒 Files selected for processing (6)
app/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.ktapp/src/main/java/me/capcom/smsgateway/modules/webhooks/domain/WebHookEvent.ktapp/src/main/java/me/capcom/smsgateway/modules/webhooks/payload/MmsDownloadedPayload.kt
f3e9be3 to
3682988
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (3)
app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt (1)
111-128:⚠️ Potential issue | 🟠 MajorKeep
messageIdconsistent betweenmms:receivedandmms:downloadedevents.The
mms:receivedevent (line 100) usesmessage.messageId ?: message.transactionId, butmms:downloadeduses the provider row ID (passed fromMmsContentObserver.processMmsDownloadedasmmsId.toString()). Webhook consumers cannot reliably correlate these two stages of the same MMS if the identifiers differ.Consider surfacing the original MMS header identifiers (messageId, transactionId) from
MmsContentReaderso the downloaded event can use consistent identification.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt` around lines 111 - 128, The mms:downloaded event is currently using the provider row ID instead of the same identifier used by mms:received, so update the MMS pipeline to surface original MMS header IDs from MmsContentReader (messageId and transactionId) through MmsContentObserver.processMmsDownloaded into ReceiverService and use the same selection logic (message.messageId ?: message.transactionId) when constructing the WebHookEvent.MmsDownloaded / MmsDownloadedPayload (the messageId field) so consumers can correlate mms:received and mms:downloaded reliably.app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.kt (1)
82-97:⚠️ Potential issue | 🟠 MajorDon't advance the high-water mark past a failed MMS.
Line 95 updates
storage.mmsLastProcessedID = mmsIdunconditionally after processing, even when an exception occurs. A transient failure (e.g., provider read error) will permanently skip that MMS since the mark advances past it.Consider only advancing the mark on successful processing, or changing the callback pattern to allow soft failures.
Suggested fix
cursor.use { c -> while (c.moveToNext()) { val mmsId = c.getLong(0) try { processMmsDownloaded(mmsId) + storage.mmsLastProcessedID = mmsId } catch (e: Exception) { logsService.insert( LogEntry.Priority.ERROR, MODULE_NAME, "Failed processing downloaded MMS (id=$mmsId)", mapOf("mmsId" to mmsId) ) + break // Stop processing; retry this MMS on next onChange } - storage.mmsLastProcessedID = mmsId } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.kt` around lines 82 - 97, The loop in MmsContentObserver currently advances the high-water mark unconditionally (storage.mmsLastProcessedID = mmsId) even when processMmsDownloaded(mmsId) throws; change it so the mark is updated only after successful processing — e.g., move the storage.mmsLastProcessedID assignment into the try block after processMmsDownloaded returns (or set it only when no exception is caught), and keep the logsService.insert call in the catch so failures do not advance the mark and the MMS can be retried.app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.kt (1)
87-91:⚠️ Potential issue | 🟡 MinorHandle text/plain parts with file-backed storage.
When a
content://mms/partrow hasct='text/plain'and_datais non-null, the text should be read from the part URI stream rather than thetextcolumn. The current code only checks thetextcolumn, causing the body to be empty when text is stored file-backed (atypical but observed in some OEM implementations).Suggested fix
if (contentType == "text/plain") { - val text = c.getString(2) + val text = c.getString(2)?.takeIf { it.isNotBlank() } + ?: readTextFromPart(resolver, partId) if (!text.isNullOrBlank()) { bodyParts.add(text) }Add a helper function:
private fun readTextFromPart(resolver: ContentResolver, partId: Long): String? { return try { resolver.openInputStream(Uri.parse("content://mms/part/$partId"))?.use { input -> input.bufferedReader().readText().takeIf { it.isNotBlank() } } } catch (_: Exception) { null } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.kt` around lines 87 - 91, The text/plain branch in MmsContentReader.kt currently only reads c.getString(2) and skips file-backed parts; add a helper function readTextFromPart(resolver: ContentResolver, partId: Long): String? (as suggested) that opens content://mms/part/{partId} and returns buffered text or null, then in the contentType == "text/plain" handling check the _data/part id from the cursor (use the part id column) and if the text from c.getString(2) is null/blank but _data is present, call readTextFromPart(resolver, partId) and add its result to bodyParts if non-blank so file-backed text parts are included.
🧹 Nitpick comments (1)
app/src/main/java/me/capcom/smsgateway/modules/settings/Module.kt (1)
63-67: Consider usingsingle {}instead offactory {}for StateStorage.The
factory {}binding creates a newStateStorageinstance on each injection. While this works correctly (all instances share the same underlyingSharedPreferences), usingsingle {}would be more efficient and consistent with the intent of having a single state manager.♻️ Suggested change
- factory { + single { StateStorage( PreferencesStorage(get(), "receiver") ) }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/java/me/capcom/smsgateway/modules/settings/Module.kt` around lines 63 - 67, Replace the Koin binding for StateStorage which currently uses factory { StateStorage(PreferencesStorage(get(), "receiver")) } with a singleton binding: change the provider to single { StateStorage(PreferencesStorage(get(), "receiver")) } so the StateStorage is created once and reused; update the binding where factory is used to refer to StateStorage and ensure no other code relies on multiple instances.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.kt`:
- Around line 33-36: The code creates two HandlerThread instances (local thread
and field handlerThread) but only stores/stops handlerThread while the
ContentObserver uses the local thread, leaking the observer thread; fix by
creating a single HandlerThread and assigning it to the field (handlerThread =
HandlerThread("MmsContentObserver").apply { start() }) and then construct the
ContentObserver with Handler(handlerThread.looper) so the same thread is used
for observation and can be properly quit/stopped later (ensure any stop()/quit()
calls reference the same handlerThread field).
---
Duplicate comments:
In
`@app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.kt`:
- Around line 82-97: The loop in MmsContentObserver currently advances the
high-water mark unconditionally (storage.mmsLastProcessedID = mmsId) even when
processMmsDownloaded(mmsId) throws; change it so the mark is updated only after
successful processing — e.g., move the storage.mmsLastProcessedID assignment
into the try block after processMmsDownloaded returns (or set it only when no
exception is caught), and keep the logsService.insert call in the catch so
failures do not advance the mark and the MMS can be retried.
In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.kt`:
- Around line 87-91: The text/plain branch in MmsContentReader.kt currently only
reads c.getString(2) and skips file-backed parts; add a helper function
readTextFromPart(resolver: ContentResolver, partId: Long): String? (as
suggested) that opens content://mms/part/{partId} and returns buffered text or
null, then in the contentType == "text/plain" handling check the _data/part id
from the cursor (use the part id column) and if the text from c.getString(2) is
null/blank but _data is present, call readTextFromPart(resolver, partId) and add
its result to bodyParts if non-blank so file-backed text parts are included.
In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.kt`:
- Around line 111-128: The mms:downloaded event is currently using the provider
row ID instead of the same identifier used by mms:received, so update the MMS
pipeline to surface original MMS header IDs from MmsContentReader (messageId and
transactionId) through MmsContentObserver.processMmsDownloaded into
ReceiverService and use the same selection logic (message.messageId ?:
message.transactionId) when constructing the WebHookEvent.MmsDownloaded /
MmsDownloadedPayload (the messageId field) so consumers can correlate
mms:received and mms:downloaded reliably.
---
Nitpick comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/settings/Module.kt`:
- Around line 63-67: Replace the Koin binding for StateStorage which currently
uses factory { StateStorage(PreferencesStorage(get(), "receiver")) } with a
singleton binding: change the provider to single {
StateStorage(PreferencesStorage(get(), "receiver")) } so the StateStorage is
created once and reused; update the binding where factory is used to refer to
StateStorage and ensure no other code relies on multiple instances.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: a49310a7-a26b-4d06-948c-e54ddd1eab4f
📒 Files selected for processing (10)
app/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/MmsReceiver.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/ReceiverService.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/StateStorage.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/data/InboxMessage.ktapp/src/main/java/me/capcom/smsgateway/modules/settings/Module.ktapp/src/main/java/me/capcom/smsgateway/modules/webhooks/domain/WebHookEvent.ktapp/src/main/java/me/capcom/smsgateway/modules/webhooks/payload/MmsDownloadedPayload.kt
🚧 Files skipped from review as they are similar to previous changes (2)
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/payload/MmsDownloadedPayload.kt
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/domain/WebHookEvent.kt
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.kt`:
- Around line 87-99: The code compares contentType using exact string matches so
MIME parameters/casing break the text-vs-attachment branching; normalize the
header before branching by deriving a normalized type from contentType (e.g.,
val normalized = contentType.substringBefore(';').trim().lowercase()) and use
that normalized value for the conditional checks (replace checks against
contentType with normalized == "text/plain" and normalized !=
"application/smil"), while continuing to pass the original contentType to
readTextPart/readers if they need parameters; update the comparisons around the
contentType variable in MmsContentReader.kt (the block that uses partId,
readTextPart, and bodyParts) accordingly.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 0c4b7e54-4e9a-4b34-88dd-eb105a80cec3
📒 Files selected for processing (3)
app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.ktapp/src/main/java/me/capcom/smsgateway/modules/settings/Module.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentObserver.kt
There was a problem hiding this comment.
🧹 Nitpick comments (2)
app/src/main/assets/api/swagger.json (2)
4-32: Missingrequiredarray for non-nullable properties.Comparing with the Kotlin
Attachmentclass,partId,contentType, andsizeare non-nullable, so they should be marked as required in the schema. This ensures API consumers relying on the OpenAPI spec for validation or code generation get accurate type information.📝 Proposed fix to add required fields
"size": { "description": "Size in bytes, 0 if unknown.", "type": "integer" } }, + "required": [ + "partId", + "contentType", + "size" + ], "type": "object" },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/assets/api/swagger.json` around lines 4 - 32, The MmsDownloadedAttachment schema is missing a required array for non-nullable properties; update the "MmsDownloadedAttachment" object to include "required": ["partId","contentType","size"] so that partId, contentType, and size are declared required (matching the Kotlin Attachment class) and ensure the types remain unchanged under the properties block.
33-68: Missingrequiredarray and consider addingmaxItemsconstraint.
Based on the Kotlin
MmsDownloadedPayloadclass,attachmentsandreceivedAtare non-nullable and should be marked as required in the nested object definition.The static analysis tool flags the
attachmentsarray lacking amaxItemsconstraint. While MMS messages are carrier-size-limited, adding a reasonable upper bound (e.g., 100) improves API predictability for consumers.📝 Proposed fix to add required fields and maxItems
"attachments": { "description": "Metadata for non-text MMS parts, including optional Base64 content.", "items": { "$ref": "#/components/schemas/MmsDownloadedAttachment" }, + "maxItems": 100, "type": "array" }, "body": { "description": "Aggregated text content of the MMS message.", "nullable": true, "type": "string" }, "receivedAt": { "description": "The timestamp when the MMS message was received.", "format": "date-time", "type": "string" }, "subject": { "description": "Message subject line.", "nullable": true, "type": "string" } }, + "required": [ + "attachments", + "receivedAt" + ], "type": "object"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/assets/api/swagger.json` around lines 33 - 68, The nested object schema for MmsDownloadedPayload is missing a required array and attachments lacks a maxItems constraint: update the MmsDownloadedPayload schema (the inner object under "allOf") to include a "required" array listing "attachments" and "receivedAt", and add a sensible "maxItems" (e.g., 100) to the "attachments" array definition to bound its length and match the Kotlin non-nullability of attachments and receivedAt.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/src/main/assets/api/swagger.json`:
- Around line 4-32: The MmsDownloadedAttachment schema is missing a required
array for non-nullable properties; update the "MmsDownloadedAttachment" object
to include "required": ["partId","contentType","size"] so that partId,
contentType, and size are declared required (matching the Kotlin Attachment
class) and ensure the types remain unchanged under the properties block.
- Around line 33-68: The nested object schema for MmsDownloadedPayload is
missing a required array and attachments lacks a maxItems constraint: update the
MmsDownloadedPayload schema (the inner object under "allOf") to include a
"required" array listing "attachments" and "receivedAt", and add a sensible
"maxItems" (e.g., 100) to the "attachments" array definition to bound its length
and match the Kotlin non-nullability of attachments and receivedAt.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 68fbb8fe-b29a-4a3c-bf34-6f172b7be64a
📒 Files selected for processing (1)
app/src/main/assets/api/swagger.json
There was a problem hiding this comment.
🧹 Nitpick comments (2)
app/src/main/assets/api/swagger.json (2)
158-185: Consider aligning schema name with the new title.The schema name remains
SmsEventPayloadwhile the title was updated toMessageEventPayloadto reflect broader usage beyond SMS. This inconsistency could cause confusion for API consumers. Consider renaming the schema definition toMessageEventPayloadfor consistency, though this would be a breaking change for existing clients.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/assets/api/swagger.json` around lines 158 - 185, The schema key is still named SmsEventPayload while its title was changed to MessageEventPayload; rename the schema definition from SmsEventPayload to MessageEventPayload and update all references to components.schemas.SmsEventPayload (e.g., $ref values) across the OpenAPI spec so they point to components.schemas.MessageEventPayload, keeping the "title": "MessageEventPayload" and existing properties unchanged; if you need to avoid breaking clients instead add an aliased schema (keep SmsEventPayload as a $ref to the new MessageEventPayload) and document the deprecation.
45-50: Consider addingmaxItemsconstraint for the attachments array.Static analysis flagged that the
attachmentsarray lacks amaxItemsconstraint. While MMS messages are carrier-size-limited (typically 1-3 MB total), adding a reasonable upper bound (e.g.,"maxItems": 100) provides input validation at the API layer.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@app/src/main/assets/api/swagger.json` around lines 45 - 50, The attachments array schema (property "attachments" referencing "#/components/schemas/MmsDownloadedAttachment") needs an explicit upper bound to validate input; update the Swagger/OpenAPI fragment for "attachments" to include a "maxItems" (e.g., 100) so the array cannot exceed the reasonable size limit for MMS payloads and keep the existing "type": "array" and "$ref" intact.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@app/src/main/assets/api/swagger.json`:
- Around line 158-185: The schema key is still named SmsEventPayload while its
title was changed to MessageEventPayload; rename the schema definition from
SmsEventPayload to MessageEventPayload and update all references to
components.schemas.SmsEventPayload (e.g., $ref values) across the OpenAPI spec
so they point to components.schemas.MessageEventPayload, keeping the "title":
"MessageEventPayload" and existing properties unchanged; if you need to avoid
breaking clients instead add an aliased schema (keep SmsEventPayload as a $ref
to the new MessageEventPayload) and document the deprecation.
- Around line 45-50: The attachments array schema (property "attachments"
referencing "#/components/schemas/MmsDownloadedAttachment") needs an explicit
upper bound to validate input; update the Swagger/OpenAPI fragment for
"attachments" to include a "maxItems" (e.g., 100) so the array cannot exceed the
reasonable size limit for MMS payloads and keep the existing "type": "array" and
"$ref" intact.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: da1c91fe-62f8-41fe-a7a3-85bfbfa257bc
📒 Files selected for processing (4)
app/src/main/assets/api/swagger.jsonapp/src/main/java/me/capcom/smsgateway/modules/receiver/MmsContentReader.ktapp/src/main/java/me/capcom/smsgateway/modules/receiver/data/InboxMessage.ktapp/src/main/java/me/capcom/smsgateway/modules/webhooks/payload/MmsDownloadedPayload.kt
🚧 Files skipped from review as they are similar to previous changes (1)
- app/src/main/java/me/capcom/smsgateway/modules/webhooks/payload/MmsDownloadedPayload.kt
This adds a second-stage MMS event that fires after an MMS has been fully downloaded by the system, providing the actual message content (body, subject, attachments). The existing mms:received event only fires on initial notification — before the content is available.
Three main components:
1. MmsContentObserver — Watches for new MMS messages via a ContentObserver on
content://mms.
- Runs on a dedicated HandlerThread to avoid blocking the main thread.
- Tracks a high-water mark (the last processed MMS _id) in SharedPreferences so it never re-processes old messages across restarts.
- On initialization, seeds the high-water mark to the current max ID (so it only fires for messages arriving after the observer starts).
- When onChange fires, it queries for new rows where m_type = 132 (retrieve-conf, meaning fully downloaded) and msg_box = 1 (inbox), then invokes the callback for each.
2. MmsContentReader — A stateless utility that reads MMS content from the content provider given an MMS ID. It extracts:
- Message-level fields: subject, date (converting from seconds to millis), and sub_id (subscription/SIM, API 22+).
- Sender address: queries the addr sub-table with type = 137 (FROM).
- Parts: iterates content://mms/<id>/part — aggregates text/plain parts into a body string, skips SMIL parts, and collects metadata (MIME type, filename, file size) for all other parts as attachments. No binary data is included in the payload.
3. Integration into ReceiverService:
- Creates and starts the MmsContentObserver alongside the existing broadcast receivers.
- The callback (processMmsDownloaded) reads the MMS via MmsContentReader, resolves the SIM slot/phone number from the subscription ID, builds an MmsDownloadedPayload, and emits it as a WebHookEvent.MmsDownloaded webhook event.
4. Supporting changes:
- WebHookEvent enum gets a new MmsDownloaded("mms:downloaded") variant.
- MmsDownloadedPayload extends MessageEventPayload with body, subject, attachments list, and receivedAt timestamp.
- swagger.json documents the new event, payload schema, and attachment schema.
Data flow:
System downloads MMS →
content://mms changes →
ContentObserver fires →
query for new retrieve-conf inbox rows →
MmsContentReader parses content →
MmsDownloadedPayload built →
webhook emitted to subscribers
cd4f0b8 to
ec5d738
Compare
|
@jacobsparts, hi! Could you test the APK from this PR? Thanks! |
|
@capcom6 Hello! It is working well for my use case. Thanks and let me know if you need anything else. |
|
@jacobsparts , thank you for your feedback! |
Thank you allThank you all for your work on this, it is highly appreciated. |
Summary by CodeRabbit