@@ -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