Skip to content

Broadcast intents#156

Open
keithvassallomt wants to merge 4 commits intotimer-machine:developfrom
keithvassallomt:broadcast-intents
Open

Broadcast intents#156
keithvassallomt wants to merge 4 commits intotimer-machine:developfrom
keithvassallomt:broadcast-intents

Conversation

@keithvassallomt
Copy link

Summary

Adds a public broadcast intent API that allows external apps to control timers. The primary use case is voice assistant integration — for example, telling a voice assistant to set a timer, which sends a broadcast intent to create and start one. I intend to add a PR to the Dicio repo with a skill allowing it to talk to TimeR Machine via this intent system.

New module: app-broadcast

A new app-broadcast module with an exported BroadcastReceiver listening on:

io.github.deweyreed.timer.BROADCAST_TIMER_CONTROL

Supported commands

Command Extras Description
create name (String), duration_seconds (long) Creates a new countdown timer and starts it immediately. The timer includes a notifier step with music, vibration, screen, and halt behaviours.
start timer_id (int) or timer_name (String) Starts an existing timer by ID or name (case-insensitive).
pause timer_id (int) or timer_name (String) Pauses a running timer.
resume timer_id (int) or timer_name (String) Resumes a paused timer.
reset timer_id (int) or timer_name (String) Resets/stops a timer.
dismiss timer_id (int) or timer_name (String), optional Dismisses a specific timer, or all running timers if no ID/name is provided.
list (none) Broadcasts a response intent (io.github.deweyreed.timer.BROADCAST_TIMER_LIST) with arrays of running timer IDs, names, and remaining time.

Timers can be targeted by either timer_id or timer_name. Name lookup is case-insensitive and returns the first match.

Architecture

  • TimerBroadcastReceiver — Exported receiver, handles intent routing. Uses ContextCompat.startForegroundService to send commands to MachineService, consistent with the existing Tasker integration approach.
  • BroadcastPresenter — Resolves timer IDs/names and maps commands to service intents. Injected via Hilt.
  • TimerFactory — Creates TimerEntity instances for the create command, with a countdown step and a notifier step.
  • FindTimerInfoByName — New use case for case-insensitive timer lookup by name, backed by a new DAO query.

Limitations

Same as the Tasker integration: on Android 12+, startForegroundService from a broadcast receiver requires battery optimization to be disabled for the app. The existing whitelist guideline already covers this.

Other changes

  • StreamMachineIntentProvider.stopAllIntent() — New method to support the dismiss command (dismiss all timers).
  • Full-screen intent permission setting (Android 14+) — Adds a settings entry under Notifications that takes the user to the system full-screen intent permission page. This entry only appears when the permission is not granted and auto-hides once it is. On Android 14+, this permission is no longer granted by default, and there is no natural in-flow moment to request it — users would only discover it's missing when a timer fires without the full-screen overlay.

Allows external apps (e.g. voice assistants) to create, start, pause,
resume, reset, and dismiss timers via broadcast intents. Includes
handling for ForegroundServiceStartNotAllowedException on Android 12+.
Sends a response broadcast with timer IDs and names of all currently
running timers, identified via active notifications.
Extracts the formatted remaining time (e.g. "04:58") from each
timer's notification and includes it in the response broadcast
as the timer_remaining string array.
Shows a settings entry under Notifications that takes the user to the
system full-screen intent permission page. Auto-hides once permission
is granted. This is needed because the permission is not granted by
default on Android 14+ and there is no natural in-flow moment to
request it.
@keithvassallomt
Copy link
Author

Any feedback on this? I'll gladly make any changes you need to ensure the best UI/UX and that everything is in-line with how you like the code to be in TimeR Machine!

@DeweyReed
Copy link
Member

Hello! My main job has been quite busy recently, and it will get harsher in the following months. :D

Thank you for the PR.

It didn't consider the hostile background running environment on Android. Broadcasts on modern Android don't work reliably. Complicated features like this require more consideration of security and edge cases as well.

If your goal is to support voice assistants, there must be system services or existing APIs to achieve it without having to implement a complete broadcasting system.

@keithvassallomt
Copy link
Author

Hi, thanks for the feedback and no rush at all — I understand busy schedules!

You raise fair points. Let me address them:

Background execution: The implementation already handles Android 12+ foreground service restrictions — it catches ForegroundServiceStartNotAllowedException and checks for active notifications before starting services. That said, I acknowledge broadcasts aren't 100% reliable when the app is fully killed. This is a known limitation of the approach, and I'm happy to document it clearly (battery optimization must be disabled, etc.).

Security: The receiver is currently exported without permission protection. I can add a custom signature-level permission or a android:permission attribute on the receiver to prevent unauthorized apps from sending commands. I could also make the entire feature opt-in via a user setting, so the receiver is effectively disabled by default.

Regarding existing APIs: As far as I know, Android doesn't offer a standard system API for third-party timer apps to expose their functionality to voice assistants or automation tools. Google Assistant integration requires specific partnerships. Broadcast intents are the established pattern for how automation tools like Tasker communicate with third-party apps — and I noticed the project already has some awareness of Tasker-style integration. This approach follows that same convention.

To reduce risk, I'm happy to:

  • Add permission protection on the receiver
  • Make the feature opt-in via a setting (disabled by default)
  • Document the background execution limitations clearly
  • Make any other changes you think would help

Let me know what you think, and whether any of these changes would make you more comfortable with the PR. Happy to iterate on it.

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.

2 participants