offer quick reply on android N

This commit is contained in:
Daniel Gultsch 2016-08-25 17:30:44 +02:00
parent 542626758d
commit 2e4713897d
4 changed files with 109 additions and 44 deletions

View file

@ -13,6 +13,7 @@ import android.os.SystemClock;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.BigPictureStyle; import android.support.v4.app.NotificationCompat.BigPictureStyle;
import android.support.v4.app.NotificationCompat.Builder; import android.support.v4.app.NotificationCompat.Builder;
import android.support.v4.app.RemoteInput;
import android.support.v4.app.TaskStackBuilder; import android.support.v4.app.TaskStackBuilder;
import android.text.Html; import android.text.Html;
import android.util.DisplayMetrics; 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) { public void finishBacklog(boolean notify) {
synchronized (notifications) { synchronized (notifications) {
mXmppConnectionService.updateUnreadCountBadge(); mXmppConnectionService.updateUnreadCountBadge();
@ -170,6 +178,8 @@ public class NotificationService {
public void clear(final Conversation conversation) { public void clear(final Conversation conversation) {
synchronized (notifications) { synchronized (notifications) {
notifications.remove(conversation.getUuid()); notifications.remove(conversation.getUuid());
final NotificationManager nm = (NotificationManager) mXmppConnectionService.getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel(conversation.getUuid(), NOTIFICATION_ID);
updateNotification(false); updateNotification(false);
} }
} }
@ -190,7 +200,7 @@ public class NotificationService {
this.markLastNotification(); this.markLastNotification();
} }
final Builder mBuilder; 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()); mBuilder = buildSingleConversations(notifications.values().iterator().next());
modifyForSoundVibrationAndLight(mBuilder, notify, preferences); modifyForSoundVibrationAndLight(mBuilder, notify, preferences);
notificationManager.notify(NOTIFICATION_ID, mBuilder.build()); notificationManager.notify(NOTIFICATION_ID, mBuilder.build());
@ -202,7 +212,7 @@ public class NotificationService {
Builder singleBuilder = buildSingleConversations(entry.getValue()); Builder singleBuilder = buildSingleConversations(entry.getValue());
singleBuilder.setGroup(CONVERSATIONS_GROUP); singleBuilder.setGroup(CONVERSATIONS_GROUP);
modifyForSoundVibrationAndLight(singleBuilder,notify,preferences); modifyForSoundVibrationAndLight(singleBuilder,notify,preferences);
notificationManager.notify(entry.getKey().hashCode() % 435301 ,singleBuilder.build()); notificationManager.notify(entry.getKey(), NOTIFICATION_ID ,singleBuilder.build());
} }
} }
} }
@ -294,6 +304,10 @@ public class NotificationService {
} else { } else {
modifyForTextOnly(mBuilder, messages); modifyForTextOnly(mBuilder, messages);
} }
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) { if ((message = getFirstDownloadableMessage(messages)) != null) {
mBuilder.addAction( mBuilder.addAction(
Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ? Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP ?
@ -303,6 +317,7 @@ public class NotificationService {
createDownloadIntent(message) createDownloadIntent(message)
); );
} }
}
if ((message = getFirstLocationMessage(messages)) != null) { if ((message = getFirstLocationMessage(messages)) != null) {
mBuilder.addAction(R.drawable.ic_room_white_24dp, mBuilder.addAction(R.drawable.ic_room_white_24dp,
mXmppConnectionService.getString(R.string.show_location), mXmppConnectionService.getString(R.string.show_location),
@ -332,8 +347,9 @@ public class NotificationService {
final BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle(); final BigPictureStyle bigPictureStyle = new NotificationCompat.BigPictureStyle();
bigPictureStyle.bigPicture(bitmap); bigPictureStyle.bigPicture(bitmap);
if (tmp.size() > 0) { if (tmp.size() > 0) {
bigPictureStyle.setSummaryText(getMergedBodies(tmp)); CharSequence text = getMergedBodies(tmp);
builder.setContentText(UIHelper.getMessagePreview(mXmppConnectionService, tmp.get(0)).first); bigPictureStyle.setSummaryText(text);
builder.setContentText(text);
} else { } else {
builder.setContentText(mXmppConnectionService.getString( builder.setContentText(mXmppConnectionService.getString(
R.string.received_x_file, R.string.received_x_file,
@ -354,7 +370,7 @@ public class NotificationService {
} }
for (Message message : messages) { for (Message message : messages) {
String sender = message.getStatus() == Message.STATUS_RECEIVED ? UIHelper.getMessageDisplayName(message) : null; 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); builder.setStyle(messagingStyle);
} else { } else {
@ -364,15 +380,19 @@ public class NotificationService {
} }
private Message getImage(final Iterable<Message> messages) { private Message getImage(final Iterable<Message> messages) {
Message image = null;
for (final Message message : messages) { for (final Message message : messages) {
if (message.getStatus() != Message.STATUS_RECEIVED) {
return null;
}
if (message.getType() != Message.TYPE_TEXT if (message.getType() != Message.TYPE_TEXT
&& message.getTransferable() == null && message.getTransferable() == null
&& message.getEncryption() != Message.ENCRYPTION_PGP && message.getEncryption() != Message.ENCRYPTION_PGP
&& message.getFileParams().height > 0) { && message.getFileParams().height > 0) {
return message; image = message;
} }
} }
return null; return image;
} }
private Message getFirstDownloadableMessage(final Iterable<Message> messages) { private Message getFirstDownloadableMessage(final Iterable<Message> messages) {
@ -448,11 +468,18 @@ public class NotificationService {
intent.setAction(XmppConnectionService.ACTION_CLEAR_NOTIFICATION); intent.setAction(XmppConnectionService.ACTION_CLEAR_NOTIFICATION);
if (conversation != null) { if (conversation != null) {
intent.putExtra("uuid", conversation.getUuid()); 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); 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() { private PendingIntent createDisableForeground() {
final Intent intent = new Intent(mXmppConnectionService, final Intent intent = new Intent(mXmppConnectionService,
XmppConnectionService.class); XmppConnectionService.class);

View file

@ -26,6 +26,7 @@ import android.os.SystemClock;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.provider.ContactsContract; import android.provider.ContactsContract;
import android.security.KeyChain; import android.security.KeyChain;
import android.support.v4.app.RemoteInput;
import android.util.DisplayMetrics; import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.util.LruCache; import android.util.LruCache;
@ -128,6 +129,7 @@ import me.leolin.shortcutbadger.ShortcutBadger;
public class XmppConnectionService extends Service { 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_CLEAR_NOTIFICATION = "clear_notification";
public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground"; public static final String ACTION_DISABLE_FOREGROUND = "disable_foreground";
public static final String ACTION_TRY_AGAIN = "try_again"; 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(); final String action = intent == null ? null : intent.getAction();
boolean interactive = false; boolean interactive = false;
if (action != null) { if (action != null) {
final Conversation c = findConversationByUuid(intent.getStringExtra("uuid"));
switch (action) { switch (action) {
case ConnectivityManager.CONNECTIVITY_ACTION: case ConnectivityManager.CONNECTIVITY_ACTION:
if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) { if (hasInternetConnection() && Config.RESET_ATTEMPT_COUNT_ON_NETWORK_CHANGE) {
@ -541,7 +544,6 @@ public class XmppConnectionService extends Service {
logoutAndSave(true); logoutAndSave(true);
return START_NOT_STICKY; return START_NOT_STICKY;
case ACTION_CLEAR_NOTIFICATION: case ACTION_CLEAR_NOTIFICATION:
final Conversation c = findConversationByUuid(intent.getStringExtra("uuid"));
if (c != null) { if (c != null) {
mNotificationService.clear(c); mNotificationService.clear(c);
} else { } else {
@ -568,6 +570,14 @@ public class XmppConnectionService extends Service {
break; break;
} }
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: case AudioManager.RINGER_MODE_CHANGED_ACTION:
if (xaOnSilentMode()) { if (xaOnSilentMode()) {
refreshAllPresences(); refreshAllPresences();
@ -687,6 +697,33 @@ public class XmppConnectionService extends Service {
return START_STICKY; 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<Message>() {
@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() { private boolean xaOnSilentMode() {
return getPreferences().getBoolean("xa_on_silent_mode", false); return getPreferences().getBoolean("xa_on_silent_mode", false);
} }

View file

@ -363,31 +363,7 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa
} else if (multi && !conversation.getMucOptions().participating()) { } else if (multi && !conversation.getMucOptions().participating()) {
this.mEditMessage.setHint(R.string.you_are_not_participating); this.mEditMessage.setHint(R.string.you_are_not_participating);
} else { } else {
switch (conversation.getNextEncryption()) { this.mEditMessage.setHint(UIHelper.getMessageHint(activity,conversation));
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;
}
getActivity().invalidateOptionsMenu(); getActivity().invalidateOptionsMenu();
} }
} }

View file

@ -11,7 +11,9 @@ import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.Locale; import java.util.Locale;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R; import eu.siacs.conversations.R;
import eu.siacs.conversations.crypto.axolotl.AxolotlService;
import eu.siacs.conversations.entities.Contact; import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation; import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.ListItem; import eu.siacs.conversations.entities.ListItem;
@ -74,8 +76,7 @@ public class UIHelper {
} else if (difference < 60 * 2) { } else if (difference < 60 * 2) {
return context.getString(R.string.minute_ago); return context.getString(R.string.minute_ago);
} else if (difference < 60 * 15) { } else if (difference < 60 * 15) {
return context.getString(R.string.minutes_ago, return context.getString(R.string.minutes_ago,Math.round(difference / 60.0));
Math.round(difference / 60.0));
} else if (today(date)) { } else if (today(date)) {
java.text.DateFormat df = DateFormat.getTimeFormat(context); java.text.DateFormat df = DateFormat.getTimeFormat(context);
return df.format(date); 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) { public static String getDisplayedMucCounterpart(final Jid counterpart) {
if (counterpart==null) { if (counterpart==null) {
return ""; return "";