From 39cac2878de059992cb09a1ccf09b6bcece99fae Mon Sep 17 00:00:00 2001 From: Christophe Besson Date: Thu, 15 Oct 2026 15:09:58 +0200 Subject: feat(android): no text messages backup, and one build again READ_SMS sits behind restricted settings outside a store and Play keeps it for the default SMS application: not worth a second build. Co-Authored-By: Claude Opus 5.5 --- .../main/kotlin/org/meshbay/client/MainActivity.kt | 10 +--- .../org/meshbay/client/phonesync/DocChannels.kt | 18 +----- .../kotlin/org/meshbay/client/phonesync/DocPlan.kt | 6 +- .../org/meshbay/client/phonesync/Profiles.kt | 2 +- .../kotlin/org/meshbay/client/phonesync/SmsXml.kt | 69 ---------------------- .../org/meshbay/client/photos/PhotoChannels.kt | 2 +- 6 files changed, 8 insertions(+), 99 deletions(-) delete mode 100644 packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/SmsXml.kt (limited to 'packages/meshbay-android/app/src/main/kotlin/org/meshbay/client') diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt index d523da7..5f225e2 100644 --- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt +++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/MainActivity.kt @@ -42,7 +42,6 @@ import org.meshbay.client.phonesync.CalendarSource import org.meshbay.client.phonesync.ContactSource import org.meshbay.client.phonesync.DestinationChannels import org.meshbay.client.phonesync.DocChannels -import org.meshbay.client.phonesync.Flavor import org.meshbay.client.phonesync.Profiles import org.meshbay.client.photos.PhotoChannels import org.meshbay.client.save.SaveSinks @@ -128,11 +127,7 @@ class MainActivity : Activity() { DocChannels("calendarsync", this, getSharedPreferences("calendarsync", Context.MODE_PRIVATE), destinations, java.io.File(cacheDir, "calendarsync"), CalendarSource(this), notifyId = 12, permissionRequest = 4211), - ) + listOfNotNull(Flavor.messageSource(this)?.let { source -> - DocChannels("messagesync", this, getSharedPreferences("messagesync", Context.MODE_PRIVATE), destinations, - java.io.File(cacheDir, "messagesync"), source, - notifyId = 11, permissionRequest = 4210) - }) + ) channels = Channels(hub, onHubChanged = { runOnUiThread { reloadForHub() } }, hasCatalogue = { code -> hasAsset("ui/locales/$code.js") }, keys = keys, saves = saves, cast = cast, onPlayback = { on -> runOnUiThread { playing = on; keepAlive() } }, @@ -279,8 +274,7 @@ class MainActivity : Activity() { val binary = WebViewFeature.isFeatureSupported(WebViewFeature.WEB_MESSAGE_ARRAY_BUFFER) val prelude = "const HUB_BASE = ${JSONObject.quote(hub.base)};\nconst BINARY = $binary;\n" + "const CAST = ${cast.control.available()};\n" + - "const BACKUP = $backups;\n" + - "const MESSAGES = ${docs.any { it.handles("messagesync:") }};\n" + "const BACKUP = $backups;\n" shim = WebViewCompat.addDocumentStartJavaScript(web, "(function(){$prelude$source\n})();", setOf(UiAssets.ORIGIN)) } diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/DocChannels.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/DocChannels.kt index f4da64f..dd74256 100644 --- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/DocChannels.kt +++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/DocChannels.kt @@ -16,7 +16,7 @@ import java.util.concurrent.CountDownLatch import java.util.concurrent.TimeUnit /** - * Contacts or messages backup (docs/MESHBAY_DESIGN.md §9.13): what the page + * Contacts or calendar backup (docs/MESHBAY_DESIGN.md §9.13): what the page * needs from the phone for one kind of data, and nothing it could use to read * anything else. * @@ -64,7 +64,6 @@ class DocChannels( "sent" -> { sent(args.optString(0, ""), args.optString(1, ""), args.optString(2, "")); true } "completed" -> { completed(); status() } "failed" -> failed(args.optString(0, ""), args.optString(1, "")) - "open-settings" -> { openSettings(); true } else -> throw Refused("Refused: no such channel") } @@ -126,7 +125,7 @@ class DocChannels( issued = Issued(token, export, c.ledgerKey) return JSONObject().put("item", JSONObject() .put("token", token).put("name", export.name) - .put("dir", DocPlan.dirFor(c, source.suffix) + (if (export.subdir.isEmpty()) "" else "/${export.subdir}")) + .put("dir", DocPlan.dirFor(c, source.suffix)) .put("size", export.file.length()).put("count", export.count)) } @@ -168,19 +167,6 @@ class DocChannels( permission = null } - /** - * The application's page in the system's settings. Android keeps some - * permissions (text messages) behind "restricted settings" for an - * application installed outside a store; that page is where the person - * lifts it (⋮ > Allow restricted settings), and nowhere in here can. - */ - private fun openSettings() { - val intent = android.content.Intent(android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS, - android.net.Uri.fromParts("package", activity.packageName, null)) - .addFlags(android.content.Intent.FLAG_ACTIVITY_NEW_TASK) - activity.runOnUiThread { activity.startActivity(intent) } - } - /** From Activity.onRequestPermissionsResult; true when the request was ours. */ fun deliverPermission(requestCode: Int): Boolean { if (requestCode != permissionRequest) return false diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/DocPlan.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/DocPlan.kt index 1a2a359..ecf643b 100644 --- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/DocPlan.kt +++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/DocPlan.kt @@ -13,14 +13,12 @@ data class Export( val mime: String, /** Recorded only when the node took the file: what the next export starts from. */ val marker: String, - /** Contacts or messages in it, for the page to say. */ + /** Contacts or events in it, for the page to say. */ val count: Int, - /** A folder under the kind's own, such as the year, or empty. */ - val subdir: String = "", ) /** - * One kind of the phone's data (the contacts, the messages) written out as + * One kind of the phone's data (the contacts, the calendar) written out as * a file of what is new since `marker`, the value the last file sent left. */ interface DocSource { diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/Profiles.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/Profiles.kt index 660cfaa..305e705 100644 --- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/Profiles.kt +++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/Profiles.kt @@ -9,7 +9,7 @@ import android.os.UserManager * of the application installed inside a work profile offers none of them. * * In the personal profile, every source reads that profile alone: MediaStore, - * ContactsContract, the SMS and calendar providers answer for the profile + * ContactsContract and the calendar provider answer for the profile * they are asked from, and none of the cross-profile (`ENTERPRISE_*`) URIs * is used (`test_android_shell.py` checks for them). */ diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/SmsXml.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/SmsXml.kt deleted file mode 100644 index 624e1f1..0000000 --- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/phonesync/SmsXml.kt +++ /dev/null @@ -1,69 +0,0 @@ -package org.meshbay.client.phonesync - -import java.io.Writer -import java.text.SimpleDateFormat -import java.util.Date -import java.util.Locale -import java.util.TimeZone - -/** One text message, as the platform's SMS provider stores it. */ -data class Sms( - val id: Long, - val address: String, - /** Milliseconds, received or sent. */ - val date: Long, - val dateSent: Long, - /** 1 received, 2 sent, 3 draft, 4 outbox, 5 failed, 6 queued. */ - val type: Int, - val body: String, - val read: Int, - val status: Int, - val locked: Int, - val protocol: Int, - val subject: String?, - val serviceCenter: String?, - val contactName: String?, -) - -/** - * Messages written as the `` XML that SMS backup applications read and - * restore, so a copy on the node goes back onto a phone with tools that exist. - * Pure, so the JVM tests hold the format and its escaping. - */ -object SmsXml { - fun write(messages: List, out: Writer, zone: TimeZone) { - val readable = SimpleDateFormat("d MMM yyyy HH:mm:ss", Locale.ROOT).apply { timeZone = zone } - out.write("\n") - out.write("\n") - for (m in messages) { - out.write(" \n") - } - out.write("\n") - } - - /** - * A quoted attribute value. Line breaks are kept as character references, - * which an attribute would otherwise fold into spaces; characters XML 1.0 - * cannot carry at all, even escaped, are left out. - */ - fun q(text: String): String { - val b = StringBuilder(text.length + 2).append('"') - for (c in text) when { - c == '&' -> b.append("&") - c == '<' -> b.append("<") - c == '>' -> b.append(">") - c == '"' -> b.append(""") - c == '\n' -> b.append(" ") - c == '\r' -> b.append(" ") - c == '\t' -> b.append(" ") - c < ' ' || c == '￾' || c == '￿' -> {} - else -> b.append(c) - } - return b.append('"').toString() - } -} diff --git a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/photos/PhotoChannels.kt b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/photos/PhotoChannels.kt index c1de859..21734fb 100644 --- a/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/photos/PhotoChannels.kt +++ b/packages/meshbay-android/app/src/main/kotlin/org/meshbay/client/photos/PhotoChannels.kt @@ -348,7 +348,7 @@ class PhotoChannels( private const val FAILURE_AT = "failure_at" private const val NOTIFIED = "notified" - /** Created, or renamed from "Photo backup" now that contacts and messages use it too. */ + /** Created, or renamed from "Photo backup" now that contacts and the calendar use it too. */ fun ensureChannel(context: Context) { context.getSystemService(NotificationManager::class.java) .createNotificationChannel(NotificationChannel(CHANNEL, "Backup", NotificationManager.IMPORTANCE_LOW)) -- cgit v1.2.3