Skip to content

3rd party actions triggered by Di2 buttons#345

Open
cnoons-cyq wants to merge 2 commits intovalterc:mainfrom
cnoons-cyq:external-actions
Open

3rd party actions triggered by Di2 buttons#345
cnoons-cyq wants to merge 2 commits intovalterc:mainfrom
cnoons-cyq:external-actions

Conversation

@cnoons-cyq
Copy link

Allows third-party apps to register as external action providers that can be triggered by Di2 switch events. Providers are discovered via broadcast receivers, communicate actions over broadcast intents, and appear as options in the Ki2 switch preference lists.

Includes a sample-provider module demonstrating the provider API.

Why

I'm working on an app for Cycliq that will use BLE on Karoo devices to control, monitor and report device status through a data field. I learned a lot through your Ki2 repo and thought this could be a cool feature for people using Ki2 and Cycliq devices down the track.

My exact use case will be things like using the Di2 shifter buttons to trigger light mode changes on a BLE device, or sending commands to cause the device to make footage locked so it isn't overwritten, or to take a picture!

My app is still a little while away, some more testing is required, but thought I could get the ball rolling early on this PR!

Reasons for Intent Based services

My first implementation used AIDL for IPC between Ki2 and providers. This would require providers to copy AIDL interface files and a shared ExternalAction Parcelable class into their project which I felt was a nuisance. I considered the option of a new ki2-ext-actions package to work around this, but I doubt you want to maintain a package for something you haven't implemented!

Using an Intent-based protocol eliminates the need for shared code and need for external dependencies: providers only need documented string constants and standard Android APIs (BroadcastReceiver, Intent, org.json).

During development, Ki2 calling startService() to communicate with providers was found to be blocked by Android's background service restrictions on Karoo 2 hardware. After this, the protocol was revised to use sendBroadcast() with setPackage() for all provider communication, which works reliably from any execution context.

Prior to discovering this I had been testing on a Karoo 3 but I don't have access to it right now, and I need to test further on the Karoo 3 to ensure core functionality works... Providers declare a manifest to handle broadcasts, giving them an opportunity to start their own services and execute their actions.

Protocol

  • Discovery: Providers declare a with intent-filter com.valterc.ki2.ACTION_PROVIDER. Ki2 discovers them via queryBroadcastReceivers().
  • Listing actions: Ki2 broadcasts LIST_ACTIONS to the provider's package. The provider responds with an ACTIONS_RESULT broadcast containing a JSON array of actions (id, label, icon URI, allowed switches bitmask).
  • Executing actions: Ki2 broadcasts PERFORM to the provider's package with flat extras (action ID, device ID, switch type/command/repeat, timestamp).
  • Provider-initiated refresh: When a provider's action list changes at runtime (in my case, when a BLE device is paired/unpaired), it broadcasts ACTIONS_CHANGED to Ki2 with its package name. Ki2 re-queries only that provider.

Changes

  • New classes: ExternalAction, ExternalActionDescriptor, ExternalActionTarget, ExternalActionManager, Ki2ActionEvent
  • Modified: Ki2Service (dispatches external actions on switch events), InputManager (resolves switch preferences to external action targets), SwitchListPreference (appends external actions to the preference picker using the
    standard ListPreference dialog)
  • New module: sample-provider — a minimal working example with zero Ki2 dependencies demonstrating the full protocol
  • No AIDL files: The protocol requires no shared code between Ki2 and providers

cnoons-cyq and others added 2 commits February 18, 2026 11:22
Allow third-party apps to register as external action providers that can be triggered by Di2 switch events. Providers are discovered via intent services, communicate actions over broadcast intents, and appear as options in the switch preference lists.

Includes a sample-provider module demonstrating the provider API.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use queryBroadcastReceivers() instead of queryIntentServices() so the
ACTION_PROVIDER intent-filter lives on the receiver directly. This
eliminates the need for a separate stub service class in providers.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant