Skip to content

How to Schedule Notification

Fabian edited this page Feb 10, 2026 · 2 revisions

In android to schedule tasks when app isn't open either a WorkManger or AlarmManager is needed.

An AlarmManager is more specific, here's how to go about creating it

With this pattern:

  • From your main.py schedule the AlarmManager with time you want
  • Then you can close your app.
  • After time specified your AlarmManager calls your service.py

I recommend this way because it involves less java and allows you to run other python things for max 15 mins allowed by Android.

In your buildozer.spec Specify needed files and permissions

services = Shorttask:./services/shorttask.py
android.add_src = ./src
android.permissions = POST_NOTIFICATIONS, USE_EXACT_ALARM, SCHEDULE_EXACT_ALARM
p4a.hook = ./hook.py

Create src/TheReceiver.java The java file starts your python service.

  • Change org.wally.waller variable with values in your buildozer.spec -> "package.domain.package.name"
  • Edit first and second line
package org.wally.waller;
import org.wally.waller.ServiceShorttask;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;


public class TheReceiver extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        String msg = intent.getStringExtra("message");
        if (msg == null) msg = "No message";
        Log.d("TheReceiver", "Broadcast received: " + msg);

        try {
            ServiceShorttask.start(context, msg);
            Log.d("TheReceiver", "Shorttask service start requested");
        } catch (Exception e) {
            Log.e("TheReceiver", "Failed to start Shorttask", e);
        }
    }
}

From p4a/hook.py Inserts your receiver to AndroidManifest.xml

  • Change package variable with values in your buildozer.spec -> "package.domain.package.name"
package = "org.wally.waller"

from pathlib import Path
from pythonforandroid.toolchain import ToolchainCL


def after_apk_build(toolchain: ToolchainCL):
    manifest_file = Path(toolchain._dist.dist_dir) / "src" / "main" / "AndroidManifest.xml"
    text = manifest_file.read_text(encoding="utf-8")

    receiver_xml = f"""
    <receiver
    android:name="{package}.TheReceiver"
    android:enabled="true"
    android:exported="false">
    <intent-filter>
        <action android:name="ALARM_ACTION" />
    </intent-filter>
    </receiver>
"""
    text = text.replace("</application>", f"{receiver_xml}\n</application>")

    manifest_file.write_text(text, encoding="utf-8")
    print("Manifest update completed successfully!\n", text)

From ./services/shorttask.py Send notification and run some task for 15 mins if needed.

import os, time
from android_notify import Notification

receiver_arg = os.environ.get('PYTHON_SERVICE_ARGUMENT', "")
print("python Shorttask arg", receiver_arg)

fmt = lambda s: f"{int((s % 3600) // 60)}m {int(s % 60)}s"

n = Notification(title="Service runtime: --:--")
n.send()

mins = 60 * 15
for i in range(0, mins):
    n.updateTitle(f"Service runtime: {fmt(i)}")
    time.sleep(1)

Lastly from your main.py Choose the seconds later you want and argument

def schedule_alarm(secs=10):
    import time
    from android_notify.config import get_python_activity_context, get_package_name
    from android_notify.internal.java_classes import autoclass, String, PendingIntent, Intent

    AlarmManager = autoclass('android.app.AlarmManager')
    Context = autoclass('android.content.Context')
    context = get_python_activity_context()
    alarm = context.getSystemService(Context.ALARM_SERVICE)

    intent = Intent(context, autoclass(f"{get_package_name()}.TheReceiver"))
    intent.setAction(String("ALARM_ACTION"))
    intent.putExtra(String("message"), String("Arg from Python!"))

    pending = PendingIntent.getBroadcast(
        context, 1234, intent, PendingIntent.FLAG_IMMUTABLE
    )

    trigger_time = int((time.time() + secs) * 1000)
    alarm.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, trigger_time, pending)
    # setExactAndAllowWhileIdle ensures the alarm triggers even if the device is in Doze mode, unlike set() which may be delayed.

To see logs on console run adb logcat | grep -E "python|TheReceiver|Shorttask or n.setBigText("large log")

This repo was very helpful: RedsTooN/kivy-java-alarmmanager-bridge

Clone this wiki locally