Skip to content

Commit b4d6fd0

Browse files
committed
ADD: New action to mention last sender in group chat. (TODO: Support emoji-icons in nickname)
Due to unknown reason, this mention message will not trigger the special alert for the recipient.
1 parent 7a11fe8 commit b4d6fd0

File tree

2 files changed

+46
-22
lines changed

2 files changed

+46
-22
lines changed

src/main/java/com/oasisfeng/nevo/decorators/wechat/ConversationManager.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ static class Conversation {
3131
static final int TYPE_BOT_MESSAGE = 3;
3232
@IntDef({ TYPE_UNKNOWN, TYPE_DIRECT_MESSAGE, TYPE_GROUP_CHAT, TYPE_BOT_MESSAGE }) @Retention(RetentionPolicy.SOURCE) @interface ConversationType {}
3333

34+
private static final String SCHEME_ORIGINAL_NAME = "ON:";
35+
3436
final int id;
3537
@Nullable String key;
3638
Person sender = SENDER_PLACEHOLDER;
@@ -64,7 +66,10 @@ else if (! TextUtils.equals(name, requireNonNull(participant.getUri()).substring
6466
return participant;
6567
}
6668

67-
private static final String SCHEME_ORIGINAL_NAME = "ON:";
69+
static CharSequence getOriginalName(final Person person) {
70+
final String uri = person.getUri();
71+
return uri != null && uri.startsWith(SCHEME_ORIGINAL_NAME) ? uri.substring(SCHEME_ORIGINAL_NAME.length()): person.getName();
72+
}
6873

6974
Conversation(final int id) { this.id = id; }
7075

src/main/java/com/oasisfeng/nevo/decorators/wechat/MessagingBuilder.java

Lines changed: 40 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -52,10 +52,12 @@ class MessagingBuilder {
5252
private static final int MAX_NUM_HISTORICAL_LINES = 10;
5353

5454
private static final String ACTION_REPLY = "REPLY";
55+
private static final String ACTION_MENTION = "MENTION";
5556
private static final String SCHEME_KEY = "key";
5657
private static final String EXTRA_REPLY_ACTION = "pending_intent";
5758
private static final String EXTRA_RESULT_KEY = "result_key";
5859
private static final String EXTRA_ORIGINAL_KEY = "original_key";
60+
private static final String EXTRA_REPLY_PREFIX = "reply_prefix";
5961

6062
/* From Notification.CarExtender */
6163
private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS";
@@ -70,6 +72,7 @@ class MessagingBuilder {
7072
private static final String KEY_PARTICIPANTS = "participants";
7173
private static final String KEY_TIMESTAMP = "timestamp";
7274
private static final String KEY_USERNAME = "key_username";
75+
private static final String MENTION_SEPARATOR = " "; // Separator between @nick and text. It's not a regular white space, but U+2005.
7376

7477
@Nullable MessagingStyle buildFromArchive(final Conversation conversation, final Notification n, final CharSequence title, final List<StatusBarNotification> archive) {
7578
// Chat history in big content view
@@ -161,20 +164,30 @@ class MessagingBuilder {
161164
final RemoteInput remote_input;
162165
if (SDK_INT >= N && on_reply != null && (remote_input = convs.getParcelable(KEY_REMOTE_INPUT)) != null) {
163166
final CharSequence[] input_history = n.extras.getCharSequenceArray(EXTRA_REMOTE_INPUT_HISTORY);
164-
final PendingIntent proxy = proxyDirectReply(sbn, on_reply, remote_input, input_history);
165-
final RemoteInput.Builder tweaked = new RemoteInput.Builder(remote_input.getResultKey()).addExtras(remote_input.getExtras())
167+
final PendingIntent proxy = proxyDirectReply(sbn, on_reply, remote_input, input_history, null);
168+
final RemoteInput.Builder reply_remote_input = new RemoteInput.Builder(remote_input.getResultKey()).addExtras(remote_input.getExtras())
166169
.setAllowFreeFormInput(true).setChoices(SmartReply.generateChoices(messaging));
167170
final String[] participants = convs.getStringArray(KEY_PARTICIPANTS);
168171
if (participants != null && participants.length > 0) {
169172
final StringBuilder label = new StringBuilder();
170173
for (final String participant : participants) label.append(',').append(participant);
171-
tweaked.setLabel(label.subSequence(1, label.length()));
172-
} else tweaked.setLabel(remote_input.getResultKey());
173-
174-
final Action.Builder action = new Action.Builder(null, mContext.getString(R.string.action_reply), proxy)
175-
.addRemoteInput(tweaked.build()).setAllowGeneratedReplies(true);
176-
if (SDK_INT >= P) action.setSemanticAction(Action.SEMANTIC_ACTION_REPLY);
177-
n.addAction(action.build());
174+
reply_remote_input.setLabel(label.subSequence(1, label.length()));
175+
} else reply_remote_input.setLabel(remote_input.getResultKey());
176+
177+
final Action.Builder reply_action = new Action.Builder(null, mContext.getString(R.string.action_reply), proxy)
178+
.addRemoteInput(reply_remote_input.build()).setAllowGeneratedReplies(true);
179+
if (SDK_INT >= P) reply_action.setSemanticAction(Action.SEMANTIC_ACTION_REPLY);
180+
n.addAction(reply_action.build());
181+
182+
if (conversation.getType() == TYPE_GROUP_CHAT) {
183+
final List<Message> messages = messaging.getMessages();
184+
final Person last_sender = messages.get(messages.size() - 1).getPerson();
185+
if (last_sender != null && last_sender != mUserSelf) {
186+
final String label = "@" + last_sender.getName(), prefix = "@" + Conversation.getOriginalName(last_sender) + MENTION_SEPARATOR;
187+
n.addAction(new Action.Builder(null, label, proxyDirectReply(sbn, on_reply, remote_input, input_history, prefix))
188+
.addRemoteInput(reply_remote_input.setLabel(label).build()).setAllowGeneratedReplies(true).build());
189+
}
190+
}
178191
}
179192
return messaging;
180193
}
@@ -193,9 +206,9 @@ private Message buildMessage(final Conversation conversation, final long when, f
193206

194207
if (conversation.key == null) try {
195208
if (on_reply != null) on_reply.send(mContext, 0, null, (p, intent, r, d, b) -> {
196-
conversation.key = intent.getStringExtra(KEY_USERNAME); // setType() below will trigger rebuilding of conversation sender.
197-
conversation.setType(conversation.key.endsWith("@chatroom") ? TYPE_GROUP_CHAT
198-
: conversation.key.startsWith("gh_") ? Conversation.TYPE_BOT_MESSAGE : Conversation.TYPE_DIRECT_MESSAGE);
209+
final String key = conversation.key = intent.getStringExtra(KEY_USERNAME); // setType() below will trigger rebuilding of conversation sender.
210+
conversation.setType(key.endsWith("@chatroom") || key.endsWith("@im.chatroom"/* WeWork */) ? TYPE_GROUP_CHAT
211+
: key.startsWith("gh_") ? Conversation.TYPE_BOT_MESSAGE : Conversation.TYPE_DIRECT_MESSAGE);
199212
}, null);
200213
} catch (final PendingIntent.CanceledException e) {
201214
Log.e(TAG, "Error parsing reply intent.", e);
@@ -236,21 +249,28 @@ private static int trimAndExtractLeadingCounter(final CharSequence text) {
236249

237250
/** Intercept the PendingIntent in RemoteInput to update the notification with replied message upon success. */
238251
private PendingIntent proxyDirectReply(final MutableStatusBarNotification sbn, final PendingIntent on_reply, final RemoteInput remote_input,
239-
final @Nullable CharSequence[] input_history) {
240-
final Intent proxy = new Intent(ACTION_REPLY).putExtra(EXTRA_REPLY_ACTION, on_reply).putExtra(EXTRA_RESULT_KEY, remote_input.getResultKey())
252+
final @Nullable CharSequence[] input_history, final @Nullable String mention_prefix) {
253+
final Intent proxy = new Intent(mention_prefix != null ? ACTION_MENTION : ACTION_REPLY) // Separate action to avoid PendingIntent overwrite.
254+
.putExtra(EXTRA_REPLY_ACTION, on_reply).putExtra(EXTRA_RESULT_KEY, remote_input.getResultKey())
241255
.setData(Uri.fromParts(SCHEME_KEY, sbn.getKey(), null)).putExtra(EXTRA_ORIGINAL_KEY, sbn.getOriginalKey());
256+
if (mention_prefix != null) proxy.putExtra(EXTRA_REPLY_PREFIX, mention_prefix);
242257
if (SDK_INT >= N && input_history != null)
243258
proxy.putCharSequenceArrayListExtra(EXTRA_REMOTE_INPUT_HISTORY, new ArrayList<>(Arrays.asList(input_history)));
244259
return PendingIntent.getBroadcast(mContext, 0, proxy.setPackage(mContext.getPackageName()), FLAG_UPDATE_CURRENT);
245260
}
246261

247262
private final BroadcastReceiver mReplyReceiver = new BroadcastReceiver() { @Override public void onReceive(final Context context, final Intent proxy_intent) {
248263
final PendingIntent reply_action = proxy_intent.getParcelableExtra(EXTRA_REPLY_ACTION);
249-
final String result_key = proxy_intent.getStringExtra(EXTRA_RESULT_KEY);
250-
final Uri data = proxy_intent.getData();
251-
final Bundle input = RemoteInput.getResultsFromIntent(proxy_intent);
252-
final CharSequence text = input != null ? input.getCharSequence(result_key) : null;
253-
if (data == null || reply_action == null || result_key == null || text == null) return; // Should never happen
264+
final String result_key = proxy_intent.getStringExtra(EXTRA_RESULT_KEY), reply_prefix = proxy_intent.getStringExtra(EXTRA_REPLY_PREFIX);
265+
final Uri data = proxy_intent.getData(); final Bundle results = RemoteInput.getResultsFromIntent(proxy_intent);
266+
final CharSequence input = results != null ? results.getCharSequence(result_key) : null;
267+
if (data == null || reply_action == null || result_key == null || input == null) return; // Should never happen
268+
final CharSequence text;
269+
if (reply_prefix != null) {
270+
text = reply_prefix + input;
271+
results.putCharSequence(result_key, text);
272+
RemoteInput.addResultsToIntent(new RemoteInput[]{ new RemoteInput.Builder(result_key).build() }, proxy_intent, results);
273+
} else text = input;
254274
final ArrayList<CharSequence> input_history = SDK_INT >= N ? proxy_intent.getCharSequenceArrayListExtra(EXTRA_REMOTE_INPUT_HISTORY) : null;
255275
final String key = data.getSchemeSpecificPart(), original_key = proxy_intent.getStringExtra(EXTRA_ORIGINAL_KEY);
256276
try {
@@ -301,8 +321,7 @@ interface Controller { void recastNotification(String key, Bundle addition); }
301321
final Uri profile_lookup = ContactsContract.Contacts.getLookupUri(context.getContentResolver(), ContactsContract.Profile.CONTENT_URI);
302322
mUserSelf = new Person.Builder().setUri(profile_lookup != null ? profile_lookup.toString() : null).setName(context.getString(R.string.self_display_name)).build();
303323

304-
final IntentFilter filter = new IntentFilter(ACTION_REPLY);
305-
filter.addDataScheme(SCHEME_KEY);
324+
final IntentFilter filter = new IntentFilter(ACTION_REPLY); filter.addAction(ACTION_MENTION); filter.addDataScheme(SCHEME_KEY);
306325
context.registerReceiver(mReplyReceiver, filter);
307326
}
308327

0 commit comments

Comments
 (0)