001// SPDX-License-Identifier: GPL-3.0-or-later
002// Copyright © 2026 Aurélien Pierre
003
004package com.aurelienpierre.sioul;
005
006import android.content.BroadcastReceiver;
007import android.content.Context;
008import android.content.Intent;
009import android.os.Handler;
010import android.os.Looper;
011import android.util.Log;
012
013import java.util.concurrent.atomic.AtomicBoolean;
014
015/**
016 * A button of the time running (TimeNote): Pause, Go on or Stop, done by
017 * Rust's side (crates/sioul-app/src/timenote.rs) on a thread of its own, the
018 * broadcast held open meanwhile (goAsync), Sioul's window running or not.
019 * Rust reads first what your other devices did, so that a button a moment
020 * behind changes nothing of it, then shows the note as it follows itself:
021 * two buttons pressed in a row show in their order. Android gives a button's
022 * broadcast ten seconds: past eight it is let go, and Rust's note comes when
023 * it is done.
024 */
025public final class TimeReceiver extends BroadcastReceiver
026{
027    private static final long DEADLINE_MS = 8_000;
028
029    @Override
030    public void onReceive(Context context, Intent intent)
031    {
032        final String action = word(intent.getAction());
033        if (action.isEmpty())
034            return;
035        final Context app = context.getApplicationContext();
036        final PendingResult broadcast = goAsync();
037        final AtomicBoolean finished = new AtomicBoolean();
038        final Runnable letGo = () -> {
039            if (finished.compareAndSet(false, true))
040                broadcast.finish();
041        };
042        final Handler main = new Handler(Looper.getMainLooper());
043        main.postDelayed(letGo, DEADLINE_MS);
044        new Thread(() -> {
045            try {
046                DoseAlarms.load(app);
047                String note = TimeNote.nativeAction(action);
048                Log.i(DoseAlarms.TAG, "Time: " + action + (note == null || note.isEmpty() ? ", no session left." : ", done."));
049            } catch (Throwable e) {
050                // Sioul's library that does not load, among others: the note stays as it was.
051                Log.e(DoseAlarms.TAG, "Time: " + action + " not done", e);
052            }
053            main.removeCallbacks(letGo);
054            letGo.run();
055        }, "sioul-time").start();
056    }
057
058    /** The button's action as Rust reads it: "pause", "resume", "stop"; "" for any other. */
059    private static String word(String action)
060    {
061        if (TimeNote.PAUSE.equals(action))
062            return "pause";
063        if (TimeNote.RESUME.equals(action))
064            return "resume";
065        if (TimeNote.STOP.equals(action))
066            return "stop";
067        return "";
068    }
069}