001// SPDX-License-Identifier: GPL-3.0-or-later 002// Copyright © 2026 Aurélien Pierre 003 004package com.aurelienpierre.sioul; 005 006import android.app.Notification; 007import android.app.NotificationManager; 008import android.app.PendingIntent; 009import android.content.Context; 010import android.content.Intent; 011import android.graphics.drawable.Icon; 012import android.net.Uri; 013import android.util.Log; 014 015import org.json.JSONException; 016import org.json.JSONObject; 017 018/** 019 * New mail at the times it may come (docs/porch.md, "Notifications"; Rust 020 * decides what and when: crates/sioul-app/src/mailnote.rs): one notification 021 * for a batch, "Two letters", the first senders with their subjects below, 022 * the next batch in its place. The "New mail" channel, at low importance: no 023 * sound, no banner, yours to change in Android's settings. A batch with 024 * someone Always through in it (Rust's `through`), while one of Sioul's modes 025 * silences the phone: its twin that passes do-not-disturb, at Android's 026 * default importance (its sound, no banner), made then (PauseMode). A tap, 027 * or Open, brings Sioul up on the Porch (ReminderOpener). On the lock 028 * screen, the count alone. From any process: the window's, or the one that 029 * fetches mail while Sioul is away. 030 */ 031final class MailNotes 032{ 033 /** The "New mail" channel. */ 034 static final String CHANNEL = "mail"; 035 private static final String TAG = "mail"; 036 private static final int NOTE = 1; 037 /** Gone from the shade after half a day: the Porch keeps the mail. */ 038 private static final long KEPT_MS = 12 * 60 * 60 * 1000L; 039 040 private MailNotes() {} 041 042 /** Rust's notification: JSON {title, body, through, words {mail, mail-through, open}}. Any thread. */ 043 static void show(Context context, String json) 044 { 045 final JSONObject said; 046 try { 047 said = new JSONObject(json); 048 } catch (JSONException e) { 049 Log.e(DoseAlarms.TAG, "Mail: a notification that does not read. " + e); 050 return; 051 } 052 JSONObject words = said.optJSONObject("words"); 053 String usual = words == null ? "" : words.optString("mail"); 054 String passing = words == null ? "" : words.optString("mail-through"); 055 String openWord = words == null ? "" : words.optString("open"); 056 context.getSystemService(NotificationManager.class).createNotificationChannel( 057 Channels.quiet(CHANNEL, usual.isEmpty() ? EventAlarms.word(context, "mail") : usual, NotificationManager.IMPORTANCE_LOW)); 058 String channel = PauseMode.mailChannelNow(context, said.optBoolean("through", false), passing.isEmpty() ? EventAlarms.word(context, "mail-through") : passing); 059 String title = said.optString("title"); 060 String body = said.optString("body"); 061 Intent porch = new Intent(context, ReminderOpener.class).setData(Uri.fromParts(ReminderOpener.SCHEME, "porch", null)).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 062 PendingIntent open = PendingIntent.getActivity(context, NOTE, porch, PendingIntent.FLAG_IMMUTABLE | PendingIntent.FLAG_UPDATE_CURRENT); 063 // On the lock screen: how many, never who. 064 Notification count = new Notification.Builder(context, channel) 065 .setSmallIcon(R.drawable.sioul_notification) 066 .setColor(context.getColor(R.color.sioul_green)) 067 .setContentTitle(title) 068 .build(); 069 Notification note = new Notification.Builder(context, channel) 070 .setSmallIcon(R.drawable.sioul_notification) 071 .setColor(context.getColor(R.color.sioul_green)) 072 .setContentTitle(title) 073 .setContentText(body) 074 .setStyle(new Notification.BigTextStyle().bigText(body)) 075 .setCategory(Notification.CATEGORY_EMAIL) 076 .setVisibility(Notification.VISIBILITY_PRIVATE) 077 .setPublicVersion(count) 078 .setContentIntent(open) 079 .setAutoCancel(true) 080 .setTimeoutAfter(KEPT_MS) 081 .addAction(new Notification.Action.Builder(Icon.createWithResource(context, R.drawable.sioul_notification), openWord.isEmpty() ? EventAlarms.word(context, "open") : openWord, open).build()) 082 .build(); 083 context.getSystemService(NotificationManager.class).notify(TAG, NOTE, note); 084 } 085}