Skip to content

Conversation

@ljrk0
Copy link

@ljrk0 ljrk0 commented Jan 23, 2026

Type of change(s)

  • Bug fix
  • Feature / enhancement
  • Infrastructure / tooling (CI, build, deps, tests)
  • Documentation

What changed and why

  • Fix logic of includeTextOnlyAttachment flag in MMS export
  • Implement includeTextOnlyMMSasSMS flag for SMS export
  • Hook up both flags to actual exporting process

Note: This is just a proposed feature/implementation. If the current behavior is to be kept (despite being superficially confusing), I'd propose removing the logic around includeTextOnlyAttachment entirely as it's not used. In addition, one could forego the entire split of "export MMS/SMS only" and always export all data. My PR tries to match most closely to what perhaps was intended, not necessarily what is "best".

Tests performed

  • I did combined exports (SMS as SMS, MMS as MMS), SMS-only (SMS as SMS, text only MMS as SMS) and MMS-only (non-text-only MMS as MMS) and checked the results

Checklist

  • I read the contribution guidelines.
  • I manually tested my changes on device/emulator (if applicable).
  • [-] I updated the "Unreleased" section in CHANGELOG.md (if applicable).
  • I have self-reviewed my pull request (no typos, formatting errors, etc.).
  • All checks are passing.

ljrk0 added 5 commits January 23, 2026 00:34
Otherwise the queryCursor will fail silently (since showErrors = false) with the exception

    column 'sub' does not exist. Available columns: [_id, creator, ct_t, d_rpt, date, date_sent, locked, m_type, msg_box, read, rr, seen, text_only, st, sub_cs, sub_id, tr_id]

and will return no results at all.
Currently, the flag `includeTextOnlyAttachment` is never used in the export logic, despite being of useful value: Some MMS aren't immediately visible to users
as such (e.g., very long "SMS" messages are often sent as MMS). Users wanting to only export "true" MMS (images, etc.), may be confused as to why "SMS" are
included in the export. As such, introduce a logic to set this flag if only MMS export is checked, otherwise export them as-is.

Similarly, users exporting SMS only may be missing these exact messages. Analogously, introduce a new flag `includeTextOnlyMMSasSMS` for SMS export, that
is suppoed to export long MMS "as SMS".
This effectively converts MMS that are "text only" to SMS as part of the export process. Property values from MMS are converted to equivalents in SMS.
This flag behaved oddly, as text only attachments were *always* included, even if it is not set. However, setting it, would filter the MMS results for those MMS
exclusively, that had TEXT_ONLY set. This old behavior would've been better described by "onlyIncludeTextOnlyAttachments".

In addition, however, the logic is flawed as TEXT_ONLY is a property set by the PduPersister when receiving MMS. If the MMS wasn't processed by the Persister,
the default value of the table row, 0, is set. This is quite often the case (in my case, all MMS were thus set to be non-text-only, despite being very much
text-only). The AOSP source code also mentions that this is only a UI hint.

For exports, we thus re-implement the same logic and check whether MMS messages are indeed "text only" or not.
@naveensingh
Copy link
Member

Still working on this?

@ljrk0
Copy link
Author

ljrk0 commented Feb 2, 2026

It is effectively in RFC state: Done as-is and mergeable, but I don't know whether you agree with the design choice I described in the PR notes

@naveensingh naveensingh marked this pull request as ready for review February 3, 2026 07:56
@naveensingh naveensingh self-requested a review as a code owner February 3, 2026 07:56
@naveensingh naveensingh changed the title Rework "text only MMS" implementation feat: rework "text only MMS" implementation Feb 3, 2026
Copy link
Member

@naveensingh naveensingh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, I assumed otherwise as it was marked as a draft.

Yeah, I don't agree with the current approach in this PR. This makes the export behavior implicit and converts MMS messages to SMS, which means that on import, those text-only MMS messages will be imported as SMS. This is not ideal, given that the goal of a backup feature is back up and restore things as-is.

Not closing this because I do think exporting text-only MMS messages might be useful for some users, so if you want to continue work on this, a cleaner way to solve that usecase will be to split the "Export MMS" checkbox in the export dialog:

  • Export SMS
  • Export MMS (text only)
  • Export MMS (media)

That way, users who want text-only MMS can export them without converting them to SMS, and without hardcoding a hidden policy.

I'd propose removing the logic around includeTextOnlyAttachment entirely as it's not used.

If not the above, we can do this to clean up the code.

PS: New features/changes require an issue first, as stated in the contribution guidelines.

Comment on lines +104 to +214

/* stores text only MMS as SMS */
if (!includeTextOnlyMMSasSMS) { return smsList }
val mmsProjection = arrayOf(
Mms._ID,

Mms.SUBSCRIPTION_ID,
/* ADDRESS : getMmsAddresses() */
/* BODY : getParts() */
Mms.DATE,
Mms.DATE_SENT,
Mms.LOCKED,
/* PROTOCOL : null */
Mms.READ,
Mms.STATUS,
Mms.MESSAGE_BOX,
/* SERVICE_CENTER : null */
)

val mmsSelection = "${Mms.THREAD_ID} = ?"

threadIds.map { it.toString() }.forEach { threadId ->
val selectionArgs = arrayOf(threadId)
context.queryCursor(Mms.CONTENT_URI, mmsProjection, mmsSelection, selectionArgs) { cursor ->
val mmsId = cursor.getLongValue(Mms._ID)

val subscriptionId = cursor.getLongValue(Mms.SUBSCRIPTION_ID)
val addresses = getMmsAddresses(mmsId)
val address = addresses.first { it.type == PduHeaders.FROM }.address

/* body done at the end */

val date = cursor.getLongValue(Mms.DATE) * 1000
val dateSent = cursor.getLongValue(Mms.DATE_SENT) * 1000
val locked = cursor.getIntValue(Mms.LOCKED)
val protocol = null
val read = cursor.getIntValue(Mms.READ)
val mmsStatus = cursor.getIntValueOrNull(Mms.STATUS)
val status = when (mmsStatus) {
null, PduHeaders.STATUS_UNRECOGNIZED, PduHeaders.STATUS_INDETERMINATE -> {
Telephony.TextBasedSmsColumns.STATUS_NONE
}
PduHeaders.STATUS_EXPIRED, PduHeaders.STATUS_UNREACHABLE, PduHeaders.STATUS_REJECTED -> {
Telephony.TextBasedSmsColumns.STATUS_FAILED
}
PduHeaders.STATUS_DEFERRED, PduHeaders.STATUS_FORWARDED -> {
Telephony.TextBasedSmsColumns.STATUS_PENDING
}
PduHeaders.STATUS_RETRIEVED -> {
Telephony.TextBasedSmsColumns.STATUS_COMPLETE
}
else -> {
/* unreachable? */
Telephony.TextBasedSmsColumns.STATUS_NONE
}
}

val messageBox = cursor.getIntValue(Mms.MESSAGE_BOX)
val type = when (messageBox) {
Telephony.BaseMmsColumns.MESSAGE_BOX_INBOX -> {
Telephony.TextBasedSmsColumns.MESSAGE_TYPE_INBOX
}
Telephony.BaseMmsColumns.MESSAGE_BOX_OUTBOX -> {
Telephony.TextBasedSmsColumns.MESSAGE_TYPE_OUTBOX
}
Telephony.BaseMmsColumns.MESSAGE_BOX_DRAFTS -> {
Telephony.TextBasedSmsColumns.MESSAGE_TYPE_DRAFT
}
Telephony.BaseMmsColumns.MESSAGE_BOX_FAILED -> {
Telephony.TextBasedSmsColumns.MESSAGE_TYPE_FAILED
}
Telephony.BaseMmsColumns.MESSAGE_BOX_SENT -> {
Telephony.TextBasedSmsColumns.MESSAGE_TYPE_SENT
}
else -> {
/* unreachable */
Telephony.TextBasedSmsColumns.MESSAGE_TYPE_ALL
}
}

val serviceCenter = null

/* We do not rely on TEXT_ONLY MMS flag, see also getMmsMessages() */
val parts = getParts(mmsId)
val smil = parts.filter { it.contentType == ContentType.APP_SMIL }
val plain = parts.filter { it.contentType == ContentType.TEXT_PLAIN }
val others = parts.filter { it.contentType != ContentType.APP_SMIL && it.contentType != ContentType.TEXT_PLAIN }

if (smil.size <= 1 && plain.size == 1 && others.size == 0) {
val body = plain.first().text

smsList.add(
SmsBackup(
subscriptionId = subscriptionId,
address = address,
body = body,
date = date,
dateSent = dateSent,
locked = locked,
protocol = protocol,
read = read,
status = status,
type = type,
serviceCenter = serviceCenter
)
)
}
}
}


Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The getMmsMessages() function should have been reused here to avoid code duplication. Also, some code comments here are unnecessary.

@naveensingh naveensingh added the waiting for author If the author does not respond, the issue will be closed. Otherwise, the label will be removed. label Feb 3, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting for author If the author does not respond, the issue will be closed. Otherwise, the label will be removed.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants