From 07287df049bb7acd91ebb2ecb4736d5e4b95c77b Mon Sep 17 00:00:00 2001 From: M66B Date: Sat, 24 Oct 2020 12:47:09 +0200 Subject: [PATCH] Android auto support --- app/src/main/AndroidManifest.xml | 3 + app/src/main/java/eu/faircode/email/Core.java | 61 +++++++++++++++---- .../email/FragmentOptionsNotifications.java | 11 ++++ .../layout/fragment_options_notifications.xml | 25 +++++++- app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/car.xml | 4 ++ 6 files changed, 93 insertions(+), 13 deletions(-) create mode 100644 app/src/main/res/xml/car.xml 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 @@ + + + +