Skip to content
Download

Shipping a plugin

You write the short id — "shout". The service prefixes it with your own package name, so the published id is plugin:com.acme.tools/shout.

Easymatic derives the same prefix from what the system reports for your service and refuses anything that does not match, so a collision with another plugin or with a built-in node is impossible rather than merely unlikely.

Declare what you need in your own manifest and request it yourself. Easymatic checks each permission you list on a node against your package and warns in its Problems panel when one is missing — but it cannot request it or hold it for you.

Copy sample-plugin’s test. It runs PluginDeclarationValidator — the very object Easymatic runs your manifest through before it will show a single node — over your own declarations, at build time:

val validated = PluginDeclarationValidator.validate(manifest, "com.acme.tools")
assertEquals(emptyList<String>(), validated.rejected.map { it.reason })
// The half a declaration cannot express: whether your node classes can do what they
// declared. PluginDeclarationValidator reads a document, and a document carries no
// interfaces — so this is what catches an @PluginChoice on a node that is not a
// PluginChoiceSource.
assertEquals(emptyList<String>(), PluginNodeContracts.problems(nodes))

Without them, a rejected node shows up as a node that is simply absent from the palette, and an unfulfilled contract as a chooser that opens on an empty list — the two worst shapes a plugin bug can take, because there is nothing on screen to notice.

A plugin that is merely installed contributes nothing. It appears on Easymatic’s Setup → Plugins screen with the permissions your manifest asks for, and contributes nodes only once the user turns it on.

That screen also lists any node Easymatic refused, with the reason — the first place to look when something is missing.

Enabling records your signing certificate, not just your package name. An update signed by the same key is trusted silently; a package with your name signed by somebody else lands back disabled, with the reason shown.

Your node’s text is never translated by Easymatic

Section titled “Your node’s text is never translated by Easymatic”

Easymatic resolves its own node names, descriptions, port labels and @Label config labels through Android string resources keyed off each node’s type id. Yours cannot go through that, and it is worth knowing why rather than filing it as a bug: your declaration crosses the binder as text you have already renderedNodeSchema turns your @Label into a plain String inside your own process, long before Easymatic sees it — so there is no key for Easymatic to look up and never will be. Its resource ids would be meaningless in your APK in any case.

So a plugin node renders exactly the words it declared, in whatever language they were written, whatever the phone’s locale. This is the same fallback path a first-party node takes when its key is missing, so nothing about it is a special case.

If you want your nodes translated, do it on your side: your plugin is an ordinary Android app, so put your text in your own res/values-*/strings.xml and resolve it in your service before building the declaration. Easymatic re-reads your declarations when it binds, so the locale in force at that moment is the one the user sees.

Easymatic binds your service while a macro using your nodes is armed, while the graph editor is open, and for thirty seconds after the last call. It uses BIND_AUTO_CREATE only — your process gets an ordinary bound-service lifetime and may be killed under memory pressure.

When it comes back, Easymatic re-arms rather than resumes: your triggers are armed again from scratch, so you never have to reason about reconnection. onDestroy disarms everything still registered, so a receiver cannot outlive the reason it was registered.

Limit Value
Nodes per plugin 64
Data ports per node 16
Config fields per node 24
Execution routes per node 4
Enum options 64
Options per choices call 500
Schema nesting 8 deep
Label length 512 characters
Manifest size 256 KB
Per value, each way 256 KB
Log lines per call 50

Exceeding one costs that node, with the reason shown on the Plugins screen — never the whole plugin.

PLUGIN_PROTOCOL_VERSION is stamped onto your manifest by the SDK, and Easymatic refuses a document that does not match the version it speaks. It is the only version comparison in the system: there is no check of your versionCode, because a downgrade is as legitimate as an upgrade and neither says anything about the wire.

2 — named execution routes, @PluginChoice (both LIST and SCREEN), status(), and the settings- and chooser-Activity conventions.

3@IntentChoice, and the URI grant that goes with it. Bumped rather than accepted silently, because the manifest is parsed leniently: a version-2 host reading a version-3 declaration would not fail, it would drop the field’s type and simply not show the row — so you would see a field you declared quietly missing, with nothing saying why. A refusal naming both numbers is the better sentence.

Rebuild against the current :plugin-sdk and you are on the latest.