diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index d062880013..d32a43ba77 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -121,6 +121,9 @@
+
diff --git a/app/src/main/java/eu/faircode/email/Core.java b/app/src/main/java/eu/faircode/email/Core.java
index 4b902ca67d..fbb4f7ae50 100644
--- a/app/src/main/java/eu/faircode/email/Core.java
+++ b/app/src/main/java/eu/faircode/email/Core.java
@@ -38,7 +38,9 @@ import android.util.Pair;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
+import androidx.core.app.Person;
import androidx.core.app.RemoteInput;
+import androidx.core.graphics.drawable.IconCompat;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.preference.PreferenceManager;
@@ -3562,6 +3564,7 @@ class Core {
boolean name_email = prefs.getBoolean("name_email", false);
boolean prefer_contact = prefs.getBoolean("prefer_contact", false);
boolean flags = prefs.getBoolean("flags", true);
+ boolean notify_messaging = prefs.getBoolean("notify_messaging", false);
boolean notify_preview = prefs.getBoolean("notify_preview", true);
boolean notify_preview_all = prefs.getBoolean("notify_preview_all", false);
boolean wearable_preview = prefs.getBoolean("wearable_preview", false);
@@ -3782,6 +3785,17 @@ class Core {
.setOnlyAlertOnce(alert_once)
.setAllowSystemGeneratedContextualActions(false);
+ NotificationCompat.MessagingStyle messagingStyle = null;
+ if (notify_messaging) {
+ // https://developer.android.com/training/cars/messaging
+ Person.Builder me = new Person.Builder()
+ .setName(MessageHelper.formatAddresses(message.to, name_email, false));
+ messagingStyle = new NotificationCompat.MessagingStyle(me.build());
+
+ if (!TextUtils.isEmpty(message.subject))
+ messagingStyle.setConversationTitle(message.subject);
+ }
+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N)
mbuilder
.setGroup(Long.toString(group))
@@ -3815,6 +3829,7 @@ class Core {
context.getString(R.string.title_advanced_notify_action_trash),
piTrash)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_DELETE)
+ .setShowsUserInterface(false)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionTrash.build());
@@ -3832,6 +3847,7 @@ class Core {
R.drawable.twotone_report_problem_24,
context.getString(R.string.title_advanced_notify_action_junk),
piJunk)
+ .setShowsUserInterface(false)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionJunk.build());
@@ -3850,6 +3866,7 @@ class Core {
context.getString(R.string.title_advanced_notify_action_archive),
piArchive)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_ARCHIVE)
+ .setShowsUserInterface(false)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionArchive.build());
@@ -3870,6 +3887,7 @@ class Core {
R.drawable.twotone_folder_24,
folder.getDisplayName(context),
piMove)
+ .setShowsUserInterface(false)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionMove.build());
@@ -3890,7 +3908,7 @@ class Core {
R.drawable.twotone_reply_24,
context.getString(R.string.title_advanced_notify_action_reply),
piReply)
- .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
+ .setShowsUserInterface(true)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionReply.build());
}
@@ -3909,6 +3927,7 @@ class Core {
context.getString(R.string.title_advanced_notify_action_reply_direct),
piReply)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY)
+ .setShowsUserInterface(false)
.setAllowGeneratedReplies(false);
RemoteInput.Builder input = new RemoteInput.Builder("text")
.setLabel(context.getString(R.string.title_advanced_notify_action_reply));
@@ -3927,6 +3946,7 @@ class Core {
context.getString(R.string.title_advanced_notify_action_flag),
piFlag)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_THUMBS_UP)
+ .setShowsUserInterface(false)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionFlag.build());
@@ -3943,6 +3963,7 @@ class Core {
context.getString(R.string.title_advanced_notify_action_seen),
piSeen)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ)
+ .setShowsUserInterface(false)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionSeen.build());
@@ -3959,6 +3980,7 @@ class Core {
context.getString(R.string.title_advanced_notify_action_snooze),
piSnooze)
.setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MUTE)
+ .setShowsUserInterface(false)
.setAllowGeneratedReplies(false);
mbuilder.addAction(actionSnooze.build());
@@ -3998,20 +4020,35 @@ class Core {
}
// Device
- StringBuilder sbm = new StringBuilder();
- if (!TextUtils.isEmpty(message.subject))
- sbm.append("").append(message.subject).append("").append("
");
+ if (messagingStyle == null) {
+ StringBuilder sbm = new StringBuilder();
+ if (!TextUtils.isEmpty(message.subject))
+ sbm.append("").append(message.subject).append("").append("
");
+
+ if (!TextUtils.isEmpty(preview))
+ sbm.append(preview);
- if (!TextUtils.isEmpty(preview))
- sbm.append(preview);
+ if (sbm.length() > 0) {
+ NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle()
+ .bigText(HtmlHelper.fromHtml(sbm.toString(), true, context));
+ if (!TextUtils.isEmpty(message.subject))
+ bigText.setSummaryText(message.subject);
- if (sbm.length() > 0) {
- NotificationCompat.BigTextStyle bigText = new NotificationCompat.BigTextStyle()
- .bigText(HtmlHelper.fromHtml(sbm.toString(), true, context));
- if (!TextUtils.isEmpty(message.subject))
- bigText.setSummaryText(message.subject);
+ mbuilder.setStyle(bigText);
+ }
+ } else {
+ Person.Builder you = new Person.Builder()
+ .setName(MessageHelper.formatAddresses(message.from, name_email, false));
+
+ if (info[0].hasPhoto())
+ you.setIcon(IconCompat.createWithBitmap(info[0].getPhotoBitmap()));
+
+ if (info[0].hasLookupUri())
+ you.setUri(info[0].getLookupUri().toString());
+
+ messagingStyle.addMessage(preview == null ? "" : preview, message.received, you.build());
- mbuilder.setStyle(bigText);
+ mbuilder.setStyle(messagingStyle);
}
} else {
if (!TextUtils.isEmpty(message.subject))
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java b/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java
index e51429a146..0cda651181 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptionsNotifications.java
@@ -87,6 +87,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
private SwitchCompat swNotifyPreviewAll;
private SwitchCompat swNotifyPreviewOnly;
private SwitchCompat swWearablePreview;
+ private SwitchCompat swMessagingStyle;
private SwitchCompat swBiometricsNotify;
private SwitchCompat swAlertOnce;
private TextView tvNoGrouping;
@@ -105,6 +106,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
"badge", "unseen_ignored",
"notify_background_only", "notify_known", "notify_summary", "notify_remove", "notify_clear",
"notify_preview", "notify_preview_all", "notify_preview_only", "wearable_preview",
+ "notify_messaging",
"biometrics_notify",
"alert_once"
};
@@ -153,6 +155,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
swNotifyPreviewAll = view.findViewById(R.id.swNotifyPreviewAll);
swNotifyPreviewOnly = view.findViewById(R.id.swNotifyPreviewOnly);
swWearablePreview = view.findViewById(R.id.swWearablePreview);
+ swMessagingStyle = view.findViewById(R.id.swMessagingStyle);
swBiometricsNotify = view.findViewById(R.id.swBiometricsNotify);
swAlertOnce = view.findViewById(R.id.swAlertOnce);
tvNoGrouping = view.findViewById(R.id.tvNoGrouping);
@@ -400,6 +403,13 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
}
});
+ swMessagingStyle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
+ prefs.edit().putBoolean("notify_messaging", checked).apply();
+ }
+ });
+
swBiometricsNotify.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@@ -521,6 +531,7 @@ public class FragmentOptionsNotifications extends FragmentBase implements Shared
swNotifyPreviewAll.setChecked(prefs.getBoolean("notify_preview_all", false));
swNotifyPreviewOnly.setChecked(prefs.getBoolean("notify_preview_only", false));
swWearablePreview.setChecked(prefs.getBoolean("wearable_preview", false));
+ swMessagingStyle.setChecked(prefs.getBoolean("notify_messaging", false));
swBiometricsNotify.setChecked(prefs.getBoolean("biometrics_notify", false));
swAlertOnce.setChecked(!prefs.getBoolean("alert_once", true));
diff --git a/app/src/main/res/layout/fragment_options_notifications.xml b/app/src/main/res/layout/fragment_options_notifications.xml
index caf99860af..10383a6e1b 100644
--- a/app/src/main/res/layout/fragment_options_notifications.xml
+++ b/app/src/main/res/layout/fragment_options_notifications.xml
@@ -534,6 +534,29 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/swWearablePreview" />
+
+
+
+
Remove new message notification on tapping on notification
Remove new message notifications on viewing message list
Only send notifications with a message preview to wearables
+ Use Android messaging style notification format
Show notification content when using biometric authentication
Use notification light
Select notification sound
@@ -585,6 +586,7 @@
For wearables that can show the full text (up to 5,000 characters)
This delays showing of notifications until the message text has been downloaded
Notifications are only sent to a wearable after the message text has been downloaded
+ This is required for Android Auto support
The target folder can be configured in the account settings
This Android version does not support notification grouping
This Android version does not support notification channels
diff --git a/app/src/main/res/xml/car.xml b/app/src/main/res/xml/car.xml
new file mode 100644
index 0000000000..66dd335d1a
--- /dev/null
+++ b/app/src/main/res/xml/car.xml
@@ -0,0 +1,4 @@
+
+
+
+