Files
SoulStream/handoff/rounds/ins-r35-kt-command.txt
T
KeelrLeek 9f0601027c android: the handoff — the pending rounds, the pipeline, and what is unverified
Everything written this session that has not been through the device: rounds 43
to 48 (each patch adversarially verified, 43's transient-notice expectation
fixed), a partial round 49, the pipeline scripts, the Android Auto design and
the icon comparison mock. Also the two harness changes that were never A/B'd —
adaptive waits, which may only shorten a declared wait and never lengthen it,
and a ship that runs the mock walk and the deploy at the same time. The A/B and
its 255-step baseline are in handoff/tools; main stays at what the phone has
actually walked.
2026-09-14 07:57:41 -05:00

65 lines
3.4 KiB
Plaintext

/** A file written wherever the user points it: Android's create-document
* sheet, and the bytes through the ContentResolver — a content URI is
* not a path, so `std::fs` on the Rust side could never have written
* this one. The file arrives whole (the playlist export builds it with
* the desktop's own writers), so nothing here knows a format. */
@Command
fun createDocument(invoke: Invoke) {
val args = invoke.parseArgs(CreateDocumentArgs::class.java)
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = args.mime ?: "application/octet-stream"
intent.putExtra(Intent.EXTRA_TITLE, args.name ?: "playlist")
startActivityForResult(invoke, intent, "documentCreated")
}
@ActivityCallback
fun documentCreated(invoke: Invoke, result: ActivityResult) {
when (result.resultCode) {
Activity.RESULT_OK -> {
val uri = result.data?.data
if (uri == null) { invoke.reject("no file came back"); return }
try {
/* The file comes off the SAME invoke, read a second time
rather than parked in a field of ours: PluginHandle's
activity-result closure holds the Invoke, so its
arguments are still here when the sheet comes back, and
no call of this can hand its bytes to another's file. */
val text = invoke.parseArgs(CreateDocumentArgs::class.java).text ?: ""
/* "wt" truncates: without it, writing a short file over a
longer one leaves the old tail behind the new bytes. */
val out = activity.contentResolver.openOutputStream(uri, "wt")
if (out == null) { invoke.reject("the file could not be opened for writing"); return }
out.use { o -> o.write(text.toByteArray(Charsets.UTF_8)) }
val res = JSObject()
res.put("uri", uri.toString())
res.put("name", displayName(uri))
invoke.resolve(res)
} catch (e: Exception) {
invoke.reject("the file could not be written: " + e.message)
}
}
/* Deliberately NOT a rejection, where pickFolder and pickImage
both reject "cancelled": a dismissed save is `Ok(None)` on the
desktop and the export says nothing at all for it. An error
here would paint a failure over a choice. */
Activity.RESULT_CANCELED -> invoke.resolve(JSObject())
else -> invoke.reject("the file could not be created")
}
}
/** The name the document actually got — the sheet lets it be changed on
* the way past. The URI's last segment when the provider will not say. */
private fun displayName(uri: Uri): String? {
try {
activity.contentResolver.query(uri, arrayOf(OpenableColumns.DISPLAY_NAME), null, null, null)?.use { c ->
val i = c.getColumnIndex(OpenableColumns.DISPLAY_NAME)
if (i >= 0 && c.moveToFirst()) {
val n = c.getString(i)
if (!n.isNullOrEmpty()) return n
}
}
} catch (_: Exception) { /* the name is a courtesy; the bytes are written */ }
return uri.lastPathSegment
}