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.
This commit is contained in:
1 parent
867fd42972
commit
9f0601027c
400 files changed
+26401
-3
No files matched your search
@@ -0,0 +1,64 @@
|
||||
/** 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
|
||||
}
|
||||
Reference in new issue
Block a user