Skip to content

feat: add Locale automation integration and live Klick'r controls - #974

Open
vibhor1102 wants to merge 14 commits into
Nain57:masterfrom
vibhor1102:feature/automation-trigger
Open

feat: add Locale automation integration and live Klick'r controls#974
vibhor1102 wants to merge 14 commits into
Nain57:masterfrom
vibhor1102:feature/automation-trigger

Conversation

@vibhor1102

@vibhor1102 vibhor1102 commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

This PR adds a complete, user-facing integration between Klick'r and automation hosts implementing the Locale plug-in protocol, notably MacroDroid, Tasker, and LlamaLab Automate.

The original July scope from Discussion #949 was to replace difficult raw-intent setup with native host configuration screens, support loading/stopping scenarios, and let a Smart scenario emit a named event to an automation host. During August testing, the same standard protocol proved capable of something more broadly useful: live Klick'r state can be queried into host variables, and Klick'r can proactively request a fresh query whenever that state changes. This enables users to build their own conditions and flows rather than limiting the integration to a fixed set of commands.

The resulting integration contains five host-visible entries:

  1. Launch Klick'r scenario — select and load a specific Smart or Dumb scenario.
  2. Stop Klick'r — stop and unload the current scenario.
  3. Run current Klick'r scenario — run only the currently loaded, paused scenario when it is safe to do so.
  4. Klick'r Status — expose the current scenario name and state as variables.
  5. Klick'r external action — let a Smart scenario trigger a named event in the automation host.

No host-specific MacroDroid, Tasker, or Automate implementation is used. The common Locale contract remains the integration boundary.

Temporary scenario-switcher dependency

The first five commits in this PR are not part of this feature review. They belong to the preceding scenario-switcher branch/PR and should be ignored here. This branch uses that work because status reporting and Run Current must recognize the switcher as an open child overlay. As soon as the scenario-switcher PR is merged, this branch will be rebased onto the updated master, leaving only the eight automation-integration commits.

Demonstrations

External Action

Screenrecorder-2026-07-09-14-37-15-704.mp4

The video shows the intended low-friction flow: assign a meaningful External Action name inside Klick'r, configure the matching event once in the automation host, and then use it as a normal trigger.

Launch Scenario and MediaProjection fallback

Screenrecorder-2026-07-12-13-40-16-410.1.mp4

The video first shows the notification continuation used when Android/OEM policy prevents the screen-capture prompt from opening in the background, then a launch where the required permission/session is already available.

Additional short demonstrations for Klick'r Status and Run current Klick'r scenario can be recorded if they would help review.

User-facing behavior and usage

Launch Klick'r scenario

In the automation host, the user adds the Klick'r action and selects a Smart or Dumb scenario from Klick'r's own configuration screen. The saved host action contains the scenario database ID and type, but those implementation details are never exposed to the user.

Execution resolves the ID against the current database. A deleted scenario fails safely. Launch means select/load and prepare, not automatically press Play. This preserves Klick'r's established safety model and allows the user to confirm the overlay before execution.

When replacing an already loaded scenario, Klick'r performs an orderly replacement. A Smart scenario can reuse an active MediaProjection session, avoiding another capture prompt. An already selected scenario is left alone instead of being restarted unnecessarily.

Typical uses include loading a game-specific scenario when the game opens, preparing different scenarios from a home-screen automation, or selecting Klick'r behavior as one step in a larger host flow.

Stop Klick'r

This is an immediately configured action with no scenario picker. It stops the current Klick'r session and removes the overlay. Calling it with no active scenario is harmless.

Run current Klick'r scenario

This deliberately does only one thing: move the currently loaded scenario from paused to running.

It runs only when all of these are true:

  • a scenario is loaded;
  • it is paused;
  • the overlay is visible;
  • the root scenario overlay is unobstructed.

It is a silent no-op when there is no loaded scenario, the scenario is already running, the overlay is hidden, settings are open, the scenario switcher is open, or any other child overlay is above the root. In particular, a running scenario keeps its current session and is never stopped/restarted. Eligibility is checked beside the live service/engine state and checked again before Smart execution, including after a delayed billing flow, so a stale external request cannot override newly opened UI or unsaved edits.

This pairs naturally with Klick'r Status: a host can check for paused, then invoke Run Current.

Klick'r Status

This is a Locale condition plug-in that always returns a successful condition result when Klick'r can answer the query, allowing hosts to store both variables:

  • %klickr_scenario_name — visible scenario name, or an empty string when none is loaded.
  • %klickr_scenario_state — one of running, paused, hidden, settings, or none.

State precedence is:

  1. settings when a child overlay is above the scenario root, including the scenario switcher;
  2. hidden when the overlay stack is hidden;
  3. running or paused for the loaded scenario;
  4. none when no scenario is loaded or the accessibility service is unavailable.

Klick'r emits the standard REQUEST_QUERY signal whenever the relevant state changes. Hosts therefore update immediately instead of relying on an undocumented polling interval. The requery marker includes the loaded scenario ID, so switching between two scenarios also refreshes the public scenario-name variable even when both have the same running state. Stop/unload, hide/show, settings navigation, pause/run, and scenario switching are all covered.

This is intentionally general-purpose. Users can build conditions such as “only run my macro when Klick'r is paused,” display the active scenario elsewhere, react when the overlay is hidden, or combine Klick'r state with any other host variables/actions.

Klick'r external action

Smart scenarios gain an External Action action type carrying a non-empty, user-defined name. When executed, Klick'r sends a standard requery with that name in pass-through data. The host's configured Klick'r event becomes satisfied only when the configured name matches the fired name.

The scenario editor offers existing names for reuse so a signal can be shared consistently across events and scenarios. Names are identifiers, not secrets or authorization tokens. This allows Klick'r to delegate broad or device-specific work—changing system settings, controlling another app, sending messages, or running a complex macro—to the automation application designed for it, without expanding Klick'r with many niche actions.

The existing raw Intent action remains available and unchanged.

Protocol and host compatibility

The implementation follows the open Locale plug-in actions and bundle conventions:

  • EDIT_SETTING / FIRE_SETTING for actions;
  • EDIT_CONDITION / QUERY_CONDITION / REQUEST_QUERY for conditions and events;
  • ordered-broadcast result codes and variable bundles;
  • Tasker-compatible ResultReceiver delivery where supplied;
  • standard pass-through data for named events.

MacroDroid was the initially uncertain host because its UI does not explain when condition output variables are refreshed. End-to-end device testing confirmed that it accepts Klick'r's standard requery broadcast and updates the mapped variables immediately. The implementation contains no MacroDroid-only path. Tasker and Automate are expected to consume the same standard protocol; their final UI-level smoke tests can be done during beta testing.

Security and failure handling

FIRE_SETTING is an exported protocol broadcast, so saved action configurations are encoded as JSON and signed with HMAC-SHA256 using a private AndroidKeyStore key. Klick'r verifies the reconstructed payload before resolving or executing it. Missing, malformed, unsigned, altered, or obsolete configurations are rejected. Scenario IDs are resolved from the live database at execution time.

Condition variables and External Action names are intentionally public automation data and are not treated as secrets.

MediaProjection consent cannot be bypassed and cannot reliably be opened from every Android background state. For a Smart scenario without a reusable capture session, the launch flow:

  1. validates the request off the broadcast thread;
  2. attempts the translucent permission activity only while the device is interactive and user editing is not open;
  3. uses a notification continuation when the phone is locked, another launch is pending, Android/OEM policy blocks the activity, or no acknowledgement arrives within 1.5 seconds;
  4. records genuine direct-launch failures so the existing Don't Kill My App guidance can be shown later.

Request IDs and persisted hand-off state prevent delayed activities, stale notification taps, or overlapping requests from reviving old work. Notification permission is optional, but disabling it means the fallback cannot be displayed.

Architecture

  • Generalizes the former Quick Settings module into feature:external-launch; the existing tile continues to use the same shared service boundary.
  • Keeps protocol encoding/query logic inside the external-launch feature rather than coupling scenario engines to any automation host.
  • Extends the local accessibility-service boundary only with authoritative live-session operations/state.
  • Adds full database/domain/compatibility/UI mapping for the Smart-scenario External Action.
  • Uses one condition-query receiver for both named External Action events and Klick'r Status.
  • Deduplicates status requery broadcasts while still treating a scenario-ID change as a meaningful public change.
  • Includes translations in all currently supported application locales, plus refinements to related existing translations.

Caveats

  • Launch Scenario prepares the selected scenario; it does not automatically run it.
  • A new Smart scenario may still require visible user confirmation for Android screen capture.
  • Background-launch notification fallback depends on notification permission.
  • Run Current intentionally ignores hidden, running, unloaded, settings, switcher, and other child-overlay states.
  • settings is a protective umbrella for any overlay above the scenario root, not only the scenario editor.
  • none also covers an unavailable accessibility service; this avoids exposing a niche extra state to automation users.
  • External Action delivery is event-like. It is not a durable queue, secret channel, or acknowledgement protocol.
  • MacroDroid has been tested end to end. Tasker and Automate follow the same implemented protocol but have not yet received the same final beta UI pass.
  • The first five commits disappear after the scenario-switcher dependency is merged and this branch is rebased.

Validation

  • Device testing on Android 14 confirmed immediate MacroDroid variable updates initiated by Klick'r requery signals.
  • Human testing confirmed running, paused, hidden, settings, and none transitions, including stop/unload and hide/show behavior.
  • Run Current was tested successfully and leaves all guarded states untouched.
  • Scenario switching is covered by the implementation and automated tests; the requery dedupe key includes scenario ID.
  • Hosted build and the complete unit-test suite passed for the implementation in Actions run 32563695799.
  • The rewritten PR branch removes the fork-only hosted workflow. Upstream PR CI is expected to validate the final translation/resource commit.
  • git diff --check upstream/master...feature/automation-trigger passes.

Commit map

Temporary scenario-switcher base — ignore in this review

  1. c66d5d324 — feat: add scenario switcher foundations
  2. d82e81c48 — feat: add responsive scenario switcher dialog
  3. af28b202e — feat: integrate pause-safe scenario switching
  4. 44478d864 — test: cover scenario switching and usage state
  5. 099fb1434 — feat: translate scenario switcher interface

Automation integration — this PR

  1. bad03a227 — refactor(external-launch): generalize Quick Settings module
  2. 8c4040c29 — feat(actions): add external actions to smart scenarios
  3. 5df96a564 — feat(locale): add signed scenario-control plugin configuration
  4. 9f5bd7495 — feat(locale): add external-action event plugin
  5. 5c62380fd — feat(locale): execute scenario controls with safe launch fallback
  6. d81817f4d — feat(locale): expose live Klick'r status
  7. 11e489fa2 — feat(locale): run the current paused scenario
  8. 7db309b3e — feat(locale): translate automation integrations

After the scenario-switcher PR merges, this branch will be rebased and the final review history will contain only commits 6–13.

FAQ

Why use Locale plug-ins instead of asking users to send raw intents?

Raw intents remain useful for developers, but they expose package/action/extra details and provide poor discovery for ordinary automation users. Locale lets Klick'r provide its own picker/configuration UI inside the host while retaining a shared standard across applications.

Why is External Action separate from the existing Intent action?

It represents a named application-wide signal, closer to a reusable event/variable than a raw Android intent. Keeping it distinct improves discoverability, avoids complicating the advanced Intent editor, and allows the same name to be reused across scenarios. The existing Intent feature is preserved.

Why does Launch Scenario not press Play automatically?

Loading a scenario can require MediaProjection consent and changes the visible Klick'r session. Separating preparation from execution avoids surprising starts. Users wanting controlled execution can combine Launch Scenario, Klick'r Status, and Run Current in their host flow.

Why does Klick'r Status report the condition as satisfied even for none?

Hosts only persist output variables when the query itself succeeds. none is valid data, not a failed query. Returning success allows the host to replace stale values immediately when a scenario stops or accessibility becomes unavailable.

Does MacroDroid poll these variables?

It can query conditions itself, but the integration does not depend on a polling schedule. Klick'r sends the standard requery request when its public status changes, and MacroDroid was observed updating mapped variables immediately.

Why only two status variables?

scenario_name and scenario_state are the generally actionable, user-facing information. Database IDs, scenario type, accessibility details, and a narrowly defined can_run flag would expose internals or duplicate logic that the automation host can express itself.

Can Run Current disrupt an already running scenario?

No. The live service checks that the scenario is paused and that the visible root overlay is unobstructed. A running session is left intact; the action does not toggle, stop, reload, or restart it.

What happens when settings or the switcher is open?

Status reports settings, and Run Current does nothing. This prevents an automation from changing execution while the user has unsaved edits or is choosing another scenario.

Are Tasker and Automate separate implementations?

No. Supporting three separate proprietary paths would increase maintenance and inconsistent behavior. The implementation uses their shared Locale-compatible protocol, with standard ordered-broadcast and Tasker ResultReceiver response forms.

Can the MediaProjection prompt be skipped?

Only when Klick'r already owns a reusable active capture session. Android requires user consent for a new projection. When Android cannot present it directly, Klick'r offers a notification continuation rather than attempting to bypass the platform restriction.

Can another app forge scenario-control commands?

Not through a saved Klick'r setting payload: action configurations are signed with an install-local AndroidKeyStore key and verified before execution. External Action names and status variables are deliberately non-secret public integration data.

Why are five unrelated commits visible?

This work currently depends on the unmerged scenario-switcher branch. They are shown transparently and listed above so reviewers can ignore them. They will be removed from this PR by rebasing immediately after the scenario-switcher work lands.

Are more demonstrations available?

Yes. The existing Launch Scenario and External Action videos remain above. Short focused recordings for live status variables, requery behavior, scenario switching, and Run Current can be added if requested.

@vibhor1102

Copy link
Copy Markdown
Contributor Author

@Nain57, one remaining UI-organization choice to decide separately from the finalized Locale-plugin behavior:

Should External Action remain a distinct Smart-scenario action, as implemented here, or should it be combined with the existing raw Intent action?

I kept it separate because it gives Tasker/MacroDroid users an end-to-end friendly flow: name the signal in Klick'r, select the same name while configuring the host event, then use it as a normal automation trigger. The existing Intent action remains untouched for the developer/advanced use cases you mentioned in Discussion #949.

However, since the raw Intent action is already useful and its dialog could potentially support multiple external-communication formats, I can merge this UI into it if that is the preferred long-term organization. This is the only remaining design choice from my side; the plugin surface, launch semantics, and background fallback are finalized.

@vibhor1102

Copy link
Copy Markdown
Contributor Author

@Nain57 The decision remains open whether to:

  • integrate it as another mode within the "intents" action, and rename it
  • Or, create a new action like i initially implemented

@vibhor1102
vibhor1102 force-pushed the feature/automation-trigger branch 6 times, most recently from 76b180e to 7700196 Compare August 22, 2026 06:36
@vibhor1102
vibhor1102 force-pushed the feature/automation-trigger branch from 8431ffa to 7db309b Compare August 22, 2026 10:12
@vibhor1102 vibhor1102 changed the title feat: add Locale plugin integration for automation apps feat: add Locale automation integration and live Klick'r controls Aug 22, 2026
@vibhor1102
vibhor1102 marked this pull request as ready for review August 22, 2026 10:18
@vibhor1102

Copy link
Copy Markdown
Contributor Author

Update: changed the ic_external_action.xml from older 24x24 viewport Material Icon to newer 960x960 viewport Material Symbol, consistent with the other Material Symbols used within the repository

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