@@ -52,10 +52,12 @@ class MessagingBuilder {
52
52
private static final int MAX_NUM_HISTORICAL_LINES = 10 ;
53
53
54
54
private static final String ACTION_REPLY = "REPLY" ;
55
+ private static final String ACTION_MENTION = "MENTION" ;
55
56
private static final String SCHEME_KEY = "key" ;
56
57
private static final String EXTRA_REPLY_ACTION = "pending_intent" ;
57
58
private static final String EXTRA_RESULT_KEY = "result_key" ;
58
59
private static final String EXTRA_ORIGINAL_KEY = "original_key" ;
60
+ private static final String EXTRA_REPLY_PREFIX = "reply_prefix" ;
59
61
60
62
/* From Notification.CarExtender */
61
63
private static final String EXTRA_CAR_EXTENDER = "android.car.EXTENSIONS" ;
@@ -70,6 +72,7 @@ class MessagingBuilder {
70
72
private static final String KEY_PARTICIPANTS = "participants" ;
71
73
private static final String KEY_TIMESTAMP = "timestamp" ;
72
74
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.
73
76
74
77
@ Nullable MessagingStyle buildFromArchive (final Conversation conversation , final Notification n , final CharSequence title , final List <StatusBarNotification > archive ) {
75
78
// Chat history in big content view
@@ -161,20 +164,30 @@ class MessagingBuilder {
161
164
final RemoteInput remote_input ;
162
165
if (SDK_INT >= N && on_reply != null && (remote_input = convs .getParcelable (KEY_REMOTE_INPUT )) != null ) {
163
166
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 ())
166
169
.setAllowFreeFormInput (true ).setChoices (SmartReply .generateChoices (messaging ));
167
170
final String [] participants = convs .getStringArray (KEY_PARTICIPANTS );
168
171
if (participants != null && participants .length > 0 ) {
169
172
final StringBuilder label = new StringBuilder ();
170
173
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
+ }
178
191
}
179
192
return messaging ;
180
193
}
@@ -193,9 +206,9 @@ private Message buildMessage(final Conversation conversation, final long when, f
193
206
194
207
if (conversation .key == null ) try {
195
208
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 );
199
212
}, null );
200
213
} catch (final PendingIntent .CanceledException e ) {
201
214
Log .e (TAG , "Error parsing reply intent." , e );
@@ -236,21 +249,28 @@ private static int trimAndExtractLeadingCounter(final CharSequence text) {
236
249
237
250
/** Intercept the PendingIntent in RemoteInput to update the notification with replied message upon success. */
238
251
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 ())
241
255
.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 );
242
257
if (SDK_INT >= N && input_history != null )
243
258
proxy .putCharSequenceArrayListExtra (EXTRA_REMOTE_INPUT_HISTORY , new ArrayList <>(Arrays .asList (input_history )));
244
259
return PendingIntent .getBroadcast (mContext , 0 , proxy .setPackage (mContext .getPackageName ()), FLAG_UPDATE_CURRENT );
245
260
}
246
261
247
262
private final BroadcastReceiver mReplyReceiver = new BroadcastReceiver () { @ Override public void onReceive (final Context context , final Intent proxy_intent ) {
248
263
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 ;
254
274
final ArrayList <CharSequence > input_history = SDK_INT >= N ? proxy_intent .getCharSequenceArrayListExtra (EXTRA_REMOTE_INPUT_HISTORY ) : null ;
255
275
final String key = data .getSchemeSpecificPart (), original_key = proxy_intent .getStringExtra (EXTRA_ORIGINAL_KEY );
256
276
try {
@@ -301,8 +321,7 @@ interface Controller { void recastNotification(String key, Bundle addition); }
301
321
final Uri profile_lookup = ContactsContract .Contacts .getLookupUri (context .getContentResolver (), ContactsContract .Profile .CONTENT_URI );
302
322
mUserSelf = new Person .Builder ().setUri (profile_lookup != null ? profile_lookup .toString () : null ).setName (context .getString (R .string .self_display_name )).build ();
303
323
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 );
306
325
context .registerReceiver (mReplyReceiver , filter );
307
326
}
308
327
0 commit comments