diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java b/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java index 968f84832c..9f4d52d0bf 100644 --- a/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java +++ b/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java @@ -96,6 +96,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared private CheckBox cbNotifyActionSeen; private CheckBox cbNotifyActionHide; private CheckBox cbNotifyActionSnooze; + private CheckBox cbNotifyActionTts; private TextView tvNotifyActionsPro; private SwitchCompat swLight; private Button btnSound; @@ -144,7 +145,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared "notify_newest_first", "notify_summary", "notify_trash", "notify_junk", "notify_block_sender", "notify_archive", "notify_move", "notify_reply", "notify_reply_direct", - "notify_flag", "notify_seen", "notify_hide", "notify_snooze", + "notify_flag", "notify_seen", "notify_hide", "notify_snooze", "notify_tts", "light", "sound", "notify_screen_on", "badge", "unseen_ignored", "notify_grouping", "notify_private", "notify_background_only", "notify_known", "notify_suppress_in_call", "notify_suppress_in_car", @@ -188,6 +189,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared cbNotifyActionSeen = view.findViewById(R.id.cbNotifyActionSeen); cbNotifyActionHide = view.findViewById(R.id.cbNotifyActionHide); cbNotifyActionSnooze = view.findViewById(R.id.cbNotifyActionSnooze); + cbNotifyActionTts = view.findViewById(R.id.cbNotifyActionTts); tvNotifyActionsPro = view.findViewById(R.id.tvNotifyActionsPro); swLight = view.findViewById(R.id.swLight); btnSound = view.findViewById(R.id.btnSound); @@ -516,6 +518,13 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared } }); + cbNotifyActionTts.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean checked) { + prefs.edit().putBoolean("notify_tts", checked).apply(); + } + }); + Helper.linkPro(tvNotifyActionsPro); Helper.linkPro(tvNotifyKnownPro); @@ -894,6 +903,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared cbNotifyActionSeen.setChecked(prefs.getBoolean("notify_seen", true) || !pro); cbNotifyActionHide.setChecked(prefs.getBoolean("notify_hide", false) && pro); cbNotifyActionSnooze.setChecked(prefs.getBoolean("notify_snooze", false) && pro); + cbNotifyActionTts.setChecked(prefs.getBoolean("notify_tts", false) && pro); swLight.setChecked(prefs.getBoolean("light", false)); swNotifyScreenOn.setChecked(prefs.getBoolean("notify_screen_on", false)); @@ -941,6 +951,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared cbNotifyActionSeen.setEnabled(pro && !summary); cbNotifyActionHide.setEnabled(pro && !summary); cbNotifyActionSnooze.setEnabled(pro && !summary); + cbNotifyActionTts.setEnabled(pro && !summary); swNotifyPreviewAll.setEnabled(!summary && swNotifyPreview.isChecked()); swNotifyPreviewOnly.setEnabled(!summary && swNotifyPreview.isChecked()); swWearablePreview.setEnabled(!summary && swNotifyPreview.isChecked()); diff --git a/app/src/main/java/eu/faircode/email/NotificationHelper.java b/app/src/main/java/eu/faircode/email/NotificationHelper.java index 1b3384a8a5..632186f936 100644 --- a/app/src/main/java/eu/faircode/email/NotificationHelper.java +++ b/app/src/main/java/eu/faircode/email/NotificationHelper.java @@ -720,6 +720,7 @@ class NotificationHelper { boolean notify_seen = (prefs.getBoolean("notify_seen", true) || !pro); boolean notify_hide = (prefs.getBoolean("notify_hide", false) && pro); boolean notify_snooze = (prefs.getBoolean("notify_snooze", false) && pro); + boolean notify_tts = (prefs.getBoolean("notify_tts", false) && pro); boolean notify_remove = prefs.getBoolean("notify_remove", true); boolean light = prefs.getBoolean("light", false); String sound = prefs.getString("sound", null); @@ -1334,6 +1335,27 @@ class NotificationHelper { wactions.add(actionSnooze.build()); } + if (message.content && notify_tts) { + Intent tts = new Intent(context, ServiceTTS.class); + tts.putExtra(ServiceTTS.EXTRA_FLUSH, true); + tts.putExtra(ServiceTTS.EXTRA_TEXT, ""); + tts.putExtra(ServiceTTS.EXTRA_LANGUAGE, message.language); + tts.putExtra(ServiceTTS.EXTRA_UTTERANCE_ID, "tts:" + message.id); + tts.putExtra(ServiceTTS.EXTRA_GROUP, group); + tts.putExtra(ServiceTTS.EXTRA_MESSAGE, message.id); + PendingIntent piTts = PendingIntentCompat.getService( + context, ServiceTTS.PI_TTS, tts, PendingIntent.FLAG_UPDATE_CURRENT); + NotificationCompat.Action.Builder actionTts = new NotificationCompat.Action.Builder( + R.drawable.twotone_play_arrow_24, + context.getString(R.string.title_rule_tts), + piTts) + .setShowsUserInterface(false) + .setAllowGeneratedReplies(false); + mbuilder.addAction(actionTts.build()); + + wactions.add(actionTts.build()); + } + // https://developer.android.com/training/wearables/notifications // https://developer.android.com/reference/androidx/core/app/NotificationCompat.Action.WearableExtender mbuilder.extend(new NotificationCompat.WearableExtender() diff --git a/app/src/main/java/eu/faircode/email/ServiceTTS.java b/app/src/main/java/eu/faircode/email/ServiceTTS.java index c7ba8a1d69..baf81f2020 100644 --- a/app/src/main/java/eu/faircode/email/ServiceTTS.java +++ b/app/src/main/java/eu/faircode/email/ServiceTTS.java @@ -20,13 +20,16 @@ package eu.faircode.email; */ import android.app.Notification; +import android.app.NotificationManager; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; +import android.os.Bundle; import android.os.IBinder; import android.os.OperationCanceledException; import android.speech.tts.TextToSpeech; import android.speech.tts.UtteranceProgressListener; +import android.text.TextUtils; import androidx.annotation.Nullable; import androidx.core.app.NotificationCompat; @@ -46,9 +49,13 @@ public class ServiceTTS extends ServiceBase { static final String EXTRA_TEXT = "text"; static final String EXTRA_LANGUAGE = "language"; static final String EXTRA_UTTERANCE_ID = "utterance"; + static final String EXTRA_MESSAGE = "message"; + static final String EXTRA_GROUP = "group"; static final String ACTION_TTS_COMPLETED = BuildConfig.APPLICATION_ID + ".TTS"; + static final int PI_TTS = 1; + @Override public void onCreate() { Log.i("Service TTS create"); @@ -145,9 +152,40 @@ public class ServiceTTS extends ServiceBase { final String text = intent.getStringExtra(EXTRA_TEXT); final String language = intent.getStringExtra(EXTRA_LANGUAGE); final String utteranceId = intent.getStringExtra(EXTRA_UTTERANCE_ID); + final long group = intent.getLongExtra(EXTRA_GROUP, 0); + final long message = intent.getLongExtra(EXTRA_MESSAGE, -1L); final Locale locale = (language == null ? Locale.getDefault() : new Locale(language)); + if (message > 0) { + String tag = "unseen." + group + "." + message; + Log.i("MMM cancel tag=" + tag); + NotificationManager nm = Helper.getSystemService(this, NotificationManager.class); + nm.cancel(tag, NotificationHelper.NOTIFICATION_TAGGED); + + Helper.getSerialExecutor().submit(new Runnable() { + @Override + public void run() { + try { + String body = Helper.readText(EntityMessage.getFile(ServiceTTS.this, message)); + String text = HtmlHelper.getFullText(ServiceTTS.this, body); + + // Avoid: Not enough namespace quota ... for ... + text = HtmlHelper.truncate(text, getMaxTextSize() / 3); + + intent.putExtra(EXTRA_TEXT, text); + intent.removeExtra(EXTRA_GROUP); + intent.removeExtra(EXTRA_MESSAGE); + onTts(intent); + } catch (Throwable ex) { + Log.e(ex); + } + } + }); + + return; + } + final Runnable speak = new RunnableEx("tts") { @Override public void delegate() { diff --git a/app/src/main/res/layout/fragment_options_notifications.xml b/app/src/main/res/layout/fragment_options_notifications.xml index 54fe11c64c..40cd8915e2 100644 --- a/app/src/main/res/layout/fragment_options_notifications.xml +++ b/app/src/main/res/layout/fragment_options_notifications.xml @@ -397,6 +397,16 @@ app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toBottomOf="@id/cbNotifyActionHide" /> + +