From 2e4713897d67a0e31be735fa21262cfe723a1ea0 Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Thu, 25 Aug 2016 17:30:44 +0200 Subject: [PATCH] offer quick reply on android N --- .../services/NotificationService.java | 59 ++++++++++++++----- .../services/XmppConnectionService.java | 39 +++++++++++- .../ui/ConversationFragment.java | 26 +------- .../siacs/conversations/utils/UIHelper.java | 29 ++++++++- 4 files changed, 109 insertions(+), 44 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/services/NotificationService.java b/src/main/java/eu/siacs/conversations/services/NotificationService.java index ea1a663b5..f6b6ce008 100644 --- a/src/main/java/eu/siacs/conversations/services/NotificationService.java +++ b/src/main/java/eu/siacs/conversations/services/NotificationService.java @@ -13,6 +13,7 @@ import android.os.SystemClock; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat.BigPictureStyle; import android.support.v4.app.NotificationCompat.Builder; +import android.support.v4.app.RemoteInput; import android.support.v4.app.TaskStackBuilder; import android.text.Html; import android.util.DisplayMetrics; @@ -118,6 +119,13 @@ public class NotificationService { } } + public void pushFromDirectReply(final Message message) { + synchronized (notifications) { + pushToStack(message); + updateNotification(false); + } + } + public void finishBacklog(boolean notify) { synchronized (notifications) { mXmppConnectionService.updateUnreadCountBadge(); @@ -170,6 +178,8 @@ public class NotificationService { public void clear(final Conversation conversation) { synchronized (notifications) { notifications.remove(conversation.getUuid()); + final NotificationManager nm = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE); + nm.cancel(conversation.getUuid(), NOTIFICATION_ID); updateNotification(false); } } @@ -190,7 +200,7 @@ public class NotificationService { this.markLastNotification(); } final Builder mBuilder; - if (notifications.size() == 1) { + if (notifications.size() == 1 && Build.VERSION.SDK_INT < Build.VERSION_CODES.N) { mBuilder = buildSingleConversations(notifications.values().iterator().next()); modifyForSoundVibrationAndLight(mBuilder, notify, preferences); notificationManager.notify(NOTIFICATION_ID, mBuilder.build()); @@ -202,7 +212,7 @@ public class NotificationService { Builder singleBuilder = buildSingleConversations(entry.getValue()); singleBuilder.setGroup(CONVERSATIONS_GROUP); modifyForSoundVibrationAndLight(singleBuilder,notify,preferences); - notificationManager.notify(entry.getKey().hashCode() % 435301 ,singleBuilder.build()); + notificationManager.notify(entry.getKey(), NOTIFICATION_ID ,singleBuilder.build()); } } } @@ -294,14 +304,19 @@ public class NotificationService { } else { modifyForTextOnly(mBuilder, messages); } - if ((message = getFirstDownloadableMessage(messages)) != null) { - mBuilder.addAction( - Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? - R.drawable.ic_file_download_white_24dp : R.drawable.ic_action_download, - mXmppConnectionService.getResources().getString(R.string.download_x_file, - UIHelper.getFileDescriptionString(mXmppConnectionService, message)), - createDownloadIntent(message) - ); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + RemoteInput remoteInput = new RemoteInput.Builder("text_reply").setLabel(UIHelper.getMessageHint(mXmppConnectionService, conversation)).build(); + NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.drawable.ic_send_text_offline, "Reply", createReplyIntent(conversation)).addRemoteInput(remoteInput).build(); + mBuilder.addAction(action); + if ((message = getFirstDownloadableMessage(messages)) != null) { + mBuilder.addAction( + Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? + R.drawable.ic_file_download_white_24dp : R.drawable.ic_action_download, + mXmppConnectionService.getResources().getString(R.string.download_x_file, + UIHelper.getFileDescriptionString(mXmppConnectionService, message)), + createDownloadIntent(message) + ); + } } if ((message = getFirstLocationMessage(messages)) != null) { mBuilder.addAction(R.drawable.ic_room_white_24dp, @@ -332,8 +347,9 @@ public class NotificationService { final BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle(); bigPictureStyle.bigPicture(bitmap); if (tmp.size() > 0) { - bigPictureStyle.setSummaryText(getMergedBodies(tmp)); - builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, tmp.get(0)).first); + CharSequence text = getMergedBodies(tmp); + bigPictureStyle.setSummaryText(text); + builder.setContentText(text); } else { builder.setContentText(mXmppConnectionService.getString( R.string.received_x_file, @@ -354,7 +370,7 @@ public class NotificationService { } for (Message message : messages) { String sender = message.getStatus() == Message.STATUS_RECEIVED ? UIHelper.getMessageDisplayName(message) : null; - messagingStyle.addMessage(message.getBody().trim(), message.getTimeSent(), sender); + messagingStyle.addMessage(UIHelper.getMessagePreview(mXmppConnectionService,message).first, message.getTimeSent(), sender); } builder.setStyle(messagingStyle); } else { @@ -364,15 +380,19 @@ public class NotificationService { } private Message getImage(final Iterable messages) { + Message image = null; for (final Message message : messages) { + if (message.getStatus() != Message.STATUS_RECEIVED) { + return null; + } if (message.getType() != Message.TYPE_TEXT && message.getTransferable() == null && message.getEncryption() != Message.ENCRYPTION_PGP && message.getFileParams().height > 0) { - return message; + image = message; } } - return null; + return image; } private Message getFirstDownloadableMessage(final Iterable messages) { @@ -448,11 +468,18 @@ public class NotificationService { intent.setAction(XmppConnectionService.ACTION_CLEAR_NOTIFICATION); if (conversation != null) { intent.putExtra("uuid", conversation.getUuid()); - return PendingIntent.getService(mXmppConnectionService, conversation.getUuid().hashCode() % 47528, intent, 0); + return PendingIntent.getService(mXmppConnectionService, conversation.getUuid().hashCode() % 247527, intent, 0); } return PendingIntent.getService(mXmppConnectionService, 0, intent, 0); } + private PendingIntent createReplyIntent(Conversation conversation) { + final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); + intent.setAction(XmppConnectionService.ACTION_REPLY_TO_CONVERSATION); + intent.putExtra("uuid",conversation.getUuid()); + return PendingIntent.getService(mXmppConnectionService, conversation.getUuid().hashCode() % 402361, intent, 0); + } + private PendingIntent createDisableForeground() { final Intent intent = new Intent(mXmppConnectionService, XmppConnectionService.class); diff --git a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java index 2719c59a7..d57176e47 100644 --- a/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java +++ b/src/main/java/eu/siacs/conversations/services/XmppConnectionService.java @@ -26,6 +26,7 @@ import android.os.SystemClock; import android.preference.PreferenceManager; import android.provider.ContactsContract; import android.security.KeyChain; +import android.support.v4.app.RemoteInput; import android.util.DisplayMetrics; import android.util.Log; import android.util.LruCache; @@ -128,6 +129,7 @@ import me.leolin.shortcutbadger.ShortcutBadger; public class XmppConnectionService extends Service { + public static final String ACTION_REPLY_TO_CONVERSATION = "reply_to_conversations"; public static final String ACTION_CLEAR_NOTIFICATION = "clear_notification"; public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground"; public static final String ACTION_TRY_AGAIN = "try_again"; @@ -526,6 +528,7 @@ public class XmppConnectionService extends Service { final String action = intent == null ? null : intent.getAction(); boolean interactive = false; if (action != null) { + final Conversation c = findConversationByUuid(intent.getStringExtra("uuid")); switch (action) { case ConnectivityManager.CONNECTIVITY_ACTION: if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) { @@ -541,7 +544,6 @@ public class XmppConnectionService extends Service { logoutAndSave(true); return START_NOT_STICKY; case ACTION_CLEAR_NOTIFICATION: - final Conversation c = findConversationByUuid(intent.getStringExtra("uuid")); if (c != null) { mNotificationService.clear(c); } else { @@ -568,6 +570,14 @@ public class XmppConnectionService extends Service { break; } break; + case ACTION_REPLY_TO_CONVERSATION: + Bundle remoteInput = RemoteInput.getResultsFromIntent(intent); + if (remoteInput != null && c != null) { + + String body = remoteInput.getString("text_reply"); + directReply(c,body); + } + break; case AudioManager.RINGER_MODE_CHANGED_ACTION: if (xaOnSilentMode()) { refreshAllPresences(); @@ -687,6 +697,33 @@ public class XmppConnectionService extends Service { return START_STICKY; } + private void directReply(Conversation conversation, String body) { + Message message = new Message(conversation,body,conversation.getNextEncryption()); + if (message.getEncryption() == Message.ENCRYPTION_PGP) { + getPgpEngine().encrypt(message, new UiCallback() { + @Override + public void success(Message message) { + message.setEncryption(Message.ENCRYPTION_DECRYPTED); + sendMessage(message); + mNotificationService.pushFromDirectReply(message); + } + + @Override + public void error(int errorCode, Message object) { + + } + + @Override + public void userInputRequried(PendingIntent pi, Message object) { + + } + }); + } else { + sendMessage(message); + mNotificationService.pushFromDirectReply(message); + } + } + private boolean xaOnSilentMode() { return getPreferences().getBoolean("xa_on_silent_mode", false); } diff --git a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java index f400feb59..19e96dafb 100644 --- a/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java +++ b/src/main/java/eu/siacs/conversations/ui/ConversationFragment.java @@ -363,31 +363,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } else if (multi && !conversation.getMucOptions().participating()) { this.mEditMessage.setHint(R.string.you_are_not_participating); } else { - switch (conversation.getNextEncryption()) { - case Message.ENCRYPTION_NONE: - if (Config.multipleEncryptionChoices()) { - mEditMessage.setHint(getString(R.string.send_unencrypted_message)); - } else { - mEditMessage.setHint(getString(R.string.send_message_to_x,conversation.getName())); - } - break; - case Message.ENCRYPTION_OTR: - mEditMessage.setHint(getString(R.string.send_otr_message)); - break; - case Message.ENCRYPTION_AXOLOTL: - AxolotlService axolotlService = conversation.getAccount().getAxolotlService(); - if (axolotlService != null && axolotlService.trustedSessionVerified(conversation)) { - mEditMessage.setHint(getString(R.string.send_omemo_x509_message)); - } else { - mEditMessage.setHint(getString(R.string.send_omemo_message)); - } - break; - case Message.ENCRYPTION_PGP: - mEditMessage.setHint(getString(R.string.send_pgp_message)); - break; - default: - break; - } + this.mEditMessage.setHint(UIHelper.getMessageHint(activity,conversation)); getActivity().invalidateOptionsMenu(); } } diff --git a/src/main/java/eu/siacs/conversations/utils/UIHelper.java b/src/main/java/eu/siacs/conversations/utils/UIHelper.java index 63dae04fe..c9b5ea67a 100644 --- a/src/main/java/eu/siacs/conversations/utils/UIHelper.java +++ b/src/main/java/eu/siacs/conversations/utils/UIHelper.java @@ -11,7 +11,9 @@ import java.util.Calendar; import java.util.Date; import java.util.Locale; +import eu.siacs.conversations.Config; import eu.siacs.conversations.R; +import eu.siacs.conversations.crypto.axolotl.AxolotlService; import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.ListItem; @@ -74,8 +76,7 @@ public class UIHelper { } else if (difference < 60 * 2) { return context.getString(R.string.minute_ago); } else if (difference < 60 * 15) { - return context.getString(R.string.minutes_ago, - Math.round(difference / 60.0)); + return context.getString(R.string.minutes_ago,Math.round(difference / 60.0)); } else if (today(date)) { java.text.DateFormat df = DateFormat.getTimeFormat(context); return df.format(date); @@ -252,6 +253,30 @@ public class UIHelper { } } + public static String getMessageHint(Context context, Conversation conversation) { + switch (conversation.getNextEncryption()) { + case Message.ENCRYPTION_NONE: + if (Config.multipleEncryptionChoices()) { + return context.getString(R.string.send_unencrypted_message); + } else { + return context.getString(R.string.send_message_to_x,conversation.getName()); + } + case Message.ENCRYPTION_OTR: + return context.getString(R.string.send_otr_message); + case Message.ENCRYPTION_AXOLOTL: + AxolotlService axolotlService = conversation.getAccount().getAxolotlService(); + if (axolotlService != null && axolotlService.trustedSessionVerified(conversation)) { + return context.getString(R.string.send_omemo_x509_message); + } else { + return context.getString(R.string.send_omemo_message); + } + case Message.ENCRYPTION_PGP: + return context.getString(R.string.send_pgp_message); + default: + return ""; + } + } + public static String getDisplayedMucCounterpart(final Jid counterpart) { if (counterpart==null) { return "";