(title, dt, text)
| 658 | |
| 659 | # Scheduler (creates a small shell script safely quoted) |
| 660 | def schedule_termux_notification(title, dt, text): |
| 661 | try: |
| 662 | job_bin = shutil.which('termux-job-scheduler') |
| 663 | notif_bin = shutil.which('termux-notification') |
| 664 | if not notif_bin: |
| 665 | return False |
| 666 | epoch_ms = int(dt.timestamp() * 1000) |
| 667 | if job_bin: |
| 668 | job_id = f"smart_notes_{int(time.time())}" |
| 669 | script_path = os.path.join(HOME, f'.smart_notes_job_{job_id}.sh') |
| 670 | safe_title = shlex.quote(title) |
| 671 | safe_text = shlex.quote(text or '') |
| 672 | try: |
| 673 | with open(script_path, 'w') as f: |
| 674 | f.write('#!/data/data/com.termux/files/usr/bin/sh\n') |
| 675 | f.write(f'termux-notification --title {safe_title} --content {safe_text}\n') |
| 676 | os.chmod(script_path, 0o755) |
| 677 | subprocess.check_call([ |
| 678 | 'termux-job-scheduler', '-s', job_id, '-t', str(epoch_ms), |
| 679 | '--service', 'com.termux.job.JobService', '-c', f'sh {shlex.quote(script_path)}' |
| 680 | ]) |
| 681 | return True |
| 682 | except Exception: |
| 683 | return False |
| 684 | else: |
| 685 | at_bin = shutil.which('at') |
| 686 | if at_bin: |
| 687 | try: |
| 688 | # Use at if available (best-effort) |
| 689 | cmd = f'termux-notification --title {shlex.quote(title)} --content {shlex.quote(text or "")}' |
| 690 | at_time_str = dt.strftime('%H:%M %Y-%m-%d') |
| 691 | p = subprocess.Popen(['at', '-M', at_time_str], stdin=subprocess.PIPE) |
| 692 | p.communicate(input=(cmd + "\n").encode()) |
| 693 | return True |
| 694 | except Exception: |
| 695 | return False |
| 696 | except Exception: |
| 697 | return False |
| 698 | return False |
| 699 | |
| 700 | # Run pending reminders |
| 701 | def run_pending_reminders(): |
no test coverage detected