diff --git a/FAQ.md b/FAQ.md index f6dd0e13c2..ad86918445 100644 --- a/FAQ.md +++ b/FAQ.md @@ -36,7 +36,6 @@ Anything on this list is in random order and *might* be added in the near future ## Frequently requested features -* Previewing message text in notifications: this is not possible because the message text is initially not downloaded. * Swipe left/right to go to previous/next message: swiping also selects message text, so this would not work reliably. * Rich text editor: besides that very few people would use this on a small mobile device, Android doesn't support a rich text editor and most rich text editor open source projects are abandoned. * Widget to read e-mail: widgets can have limited user interaction only, so a widget to read e-mail would not be very useful. diff --git a/README.md b/README.md index 209e948b51..ab838d3f27 100644 --- a/README.md +++ b/README.md @@ -37,9 +37,9 @@ See also [this FAQ](https://github.com/M66B/open-source-email/blob/master/FAQ.md * Dark/black theme * Account/identity colors * Notifications per account +* Notifications with message preview * Reply templates * Search on server -* Preview sender/subject/photo in new message notifications * Keyword management * Encryption/decryption ([OpenPGP](https://www.openpgp.org/)) * Export settings diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java index a56861e9a8..aa266380d9 100644 --- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java +++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java @@ -128,6 +128,7 @@ public class ServiceSynchronize extends LifecycleService { private final Object lock = new Object(); private TupleAccountStats lastStats = null; private ServiceManager serviceManager = new ServiceManager(); + private static ExecutorService executor = Executors.newSingleThreadExecutor(Helper.backgroundThreadFactory); private static final int NOTIFICATION_SYNCHRONIZE = 1; @@ -178,60 +179,71 @@ public class ServiceSynchronize extends LifecycleService { private LongSparseArray> notifying = new LongSparseArray<>(); @Override - public void onChanged(List messages) { - NotificationManager nm = getSystemService(NotificationManager.class); + public void onChanged(final List messages) { + executor.submit(new Runnable() { + @Override + public void run() { + try { + NotificationManager nm = getSystemService(NotificationManager.class); - Widget.update(ServiceSynchronize.this, messages.size()); + Widget.update(ServiceSynchronize.this, messages.size()); - LongSparseArray accountName = new LongSparseArray<>(); - LongSparseArray> accountMessages = new LongSparseArray<>(); + LongSparseArray accountName = new LongSparseArray<>(); + LongSparseArray> accountMessages = new LongSparseArray<>(); - for (int i = 0; i < notifying.size(); i++) - accountMessages.put(notifying.keyAt(i), new ArrayList()); + for (int i = 0; i < notifying.size(); i++) + accountMessages.put(notifying.keyAt(i), new ArrayList()); - for (TupleMessageEx message : messages) { - long account = (message.accountNotify ? message.account : 0); - accountName.put(account, account > 0 ? message.accountName : null); - if (accountMessages.indexOfKey(account) < 0) - accountMessages.put(account, new ArrayList()); - accountMessages.get(account).add(message); - if (notifying.indexOfKey(account) < 0) - notifying.put(account, new ArrayList()); - } + for (TupleMessageEx message : messages) { + long account = (message.accountNotify ? message.account : 0); + accountName.put(account, account > 0 ? message.accountName : null); + if (accountMessages.indexOfKey(account) < 0) + accountMessages.put(account, new ArrayList()); + accountMessages.get(account).add(message); + if (notifying.indexOfKey(account) < 0) + notifying.put(account, new ArrayList()); + } - for (int i = 0; i < accountMessages.size(); i++) { - long account = accountMessages.keyAt(i); - List notifications = getNotificationUnseen( - account, accountName.get(account), accountMessages.get(account)); - - List all = new ArrayList<>(); - List added = new ArrayList<>(); - List removed = notifying.get(account); - for (Notification notification : notifications) { - Integer id = (int) notification.extras.getLong("id", 0); - if (id > 0) { - all.add(id); - if (removed.contains(id)) - removed.remove(id); - else - added.add(id); - } - } + for (int i = 0; i < accountMessages.size(); i++) { + long account = accountMessages.keyAt(i); + List notifications = getNotificationUnseen( + account, accountName.get(account), accountMessages.get(account)); + + List all = new ArrayList<>(); + List added = new ArrayList<>(); + List removed = notifying.get(account); + for (Notification notification : notifications) { + Integer id = (int) notification.extras.getLong("id", 0); + if (id != 0) { + all.add(id); + if (removed.contains(id)) + removed.remove(id); + else { + removed.remove(Integer.valueOf(-id)); + added.add(id); + } + } + } - if (notifications.size() == 0) - nm.cancel("unseen:" + account, 0); + if (notifications.size() == 0) + nm.cancel("unseen:" + account, 0); - for (Integer id : removed) - nm.cancel("unseen:" + account, id); + for (Integer id : removed) + nm.cancel("unseen:" + account, Math.abs(id)); - for (Notification notification : notifications) { - Integer id = (int) notification.extras.getLong("id", 0); - if ((id == 0 && added.size() + removed.size() > 0) || added.contains(id)) - nm.notify("unseen:" + account, id, notification); - } + for (Notification notification : notifications) { + Integer id = (int) notification.extras.getLong("id", 0); + if ((id == 0 && added.size() + removed.size() > 0) || added.contains(id)) + nm.notify("unseen:" + account, Math.abs(id), notification); + } - notifying.put(account, all); - } + notifying.put(account, all); + } + } catch (Throwable ex) { + Log.e(Helper.TAG, ex + "\n" + Log.getStackTraceString(ex)); + } + } + }); } }); } @@ -497,7 +509,7 @@ public class ServiceSynchronize extends LifecycleService { for (TupleMessageEx message : messages) { Bundle args = new Bundle(); - args.putLong("id", message.id); + args.putLong("id", message.content ? message.id : -message.id); Intent thread = new Intent(this, ActivityView.class); thread.setAction("thread:" + message.thread); @@ -554,6 +566,7 @@ public class ServiceSynchronize extends LifecycleService { .setWhen(message.received) .setDeleteIntent(piDelete) .setPriority(Notification.PRIORITY_DEFAULT) + .setOnlyAlertOnce(true) .setCategory(Notification.CATEGORY_MESSAGE) .setVisibility(Notification.VISIBILITY_PRIVATE) .setGroup(group) @@ -566,6 +579,16 @@ public class ServiceSynchronize extends LifecycleService { if (!TextUtils.isEmpty(message.subject)) mbuilder.setContentText(message.subject); + if (message.content) + try { + String html = message.read(ServiceSynchronize.this); + String text = (TextUtils.isEmpty(message.subject) ? "" : message.subject + ": ") + Jsoup.parse(html).text(); + mbuilder.setStyle(new Notification.BigTextStyle().bigText(text)); + } catch (IOException ex) { + Log.e(Helper.TAG, ex + "/n" + Log.getStackTraceString(ex)); + mbuilder.setStyle(new Notification.BigTextStyle().bigText(ex.toString())); + } + if (!TextUtils.isEmpty(message.avatar)) { Cursor cursor = null; try {