1111import android .net .Uri ;
1212import android .os .Bundle ;
1313import android .os .Parcelable ;
14+ import android .provider .ContactsContract ;
1415import android .service .notification .StatusBarNotification ;
1516import android .text .TextUtils ;
1617import android .util .ArrayMap ;
1718import android .util .Log ;
1819import android .util .LongSparseArray ;
1920
21+ import com .oasisfeng .nevo .decorators .wechat .ConversationManager .Conversation ;
2022import com .oasisfeng .nevo .sdk .MutableNotification ;
2123import com .oasisfeng .nevo .sdk .MutableStatusBarNotification ;
2224
3739import static android .os .Build .VERSION .SDK_INT ;
3840import static android .os .Build .VERSION_CODES .N ;
3941import static android .os .Build .VERSION_CODES .P ;
42+ import static com .oasisfeng .nevo .decorators .wechat .ConversationManager .Conversation .TYPE_GROUP_CHAT ;
4043import static com .oasisfeng .nevo .decorators .wechat .WeChatDecorator .SENDER_MESSAGE_SEPARATOR ;
4144
4245/**
4346 * Build the modernized {@link MessagingStyle} for WeChat conversation.
4447 *
4548 * Refactored by Oasis on 2018-8-9.
4649 */
47- public class MessagingBuilder {
50+ class MessagingBuilder {
4851
4952 private static final int MAX_NUM_HISTORICAL_LINES = 10 ;
50- private static final Person SENDER_PLACEHOLDER = new Person .Builder ().setName (" " ).build (); // Cannot be empty string, or it will be treated as null.
5153
5254 private static final String ACTION_REPLY = "REPLY" ;
5355 private static final String SCHEME_KEY = "key" ;
@@ -67,8 +69,9 @@ public class MessagingBuilder {
6769 private static final String KEY_ON_READ = "on_read" ;
6870 private static final String KEY_PARTICIPANTS = "participants" ;
6971 private static final String KEY_TIMESTAMP = "timestamp" ;
72+ private static final String KEY_USERNAME = "key_username" ;
7073
71- @ Nullable MessagingStyle buildFromArchive (final Notification n , final CharSequence title , final boolean group_chat , final List <StatusBarNotification > archive ) {
74+ @ Nullable MessagingStyle buildFromArchive (final Conversation conversation , final Notification n , final CharSequence title , final List <StatusBarNotification > archive ) {
7275 // Chat history in big content view
7376 if (archive .isEmpty ()) {
7477 Log .d (TAG , "No history" );
@@ -114,30 +117,31 @@ public class MessagingBuilder {
114117
115118 n .extras .putCharSequence (EXTRA_TEXT , text ); // Latest message text for collapsed layout.
116119
117- final MessagingStyle messaging = new MessagingStyle (mPersons . getSelf (). toPerson () );
120+ final MessagingStyle messaging = new MessagingStyle (mUserSelf );
118121 final boolean sender_inline = num_lines_with_colon == lines .size ();
119122 for (int i = 0 , size = lines .size (); i < size ; i ++) // All lines have colon in text
120- messaging .addMessage (buildMessage (lines .keyAt (i ), n .tickerText , title , lines .valueAt (i ), sender_inline ? null : title .toString (), group_chat ));
123+ messaging .addMessage (buildMessage (conversation , lines .keyAt (i ), n .tickerText , lines .valueAt (i ), sender_inline ? null : title .toString (), null ));
121124 return messaging ;
122125 }
123126
124- @ Nullable MessagingStyle buildFromExtender (final MutableStatusBarNotification sbn , final CharSequence title , final boolean is_group ) {
127+ @ Nullable MessagingStyle buildFromExtender (final Conversation conversation , final MutableStatusBarNotification sbn ) {
125128 final MutableNotification n = sbn .getNotification ();
126129 final Bundle car_extender = n .extras .getBundle (EXTRA_CAR_EXTENDER );
127130 if (car_extender == null ) return null ;
128- final Bundle conversation = car_extender .getBundle (EXTRA_CONVERSATION );
129- if (conversation == null ) {
131+ final Bundle convs = car_extender .getBundle (EXTRA_CONVERSATION );
132+ if (convs == null ) {
130133 Log .w (TAG , EXTRA_CONVERSATION + " is missing" );
131134 return null ;
132135 }
133- final Parcelable [] parcelable_messages = conversation .getParcelableArray (KEY_MESSAGES );
136+ final Parcelable [] parcelable_messages = convs .getParcelableArray (KEY_MESSAGES );
134137 if (parcelable_messages == null ) {
135138 Log .w (TAG , KEY_MESSAGES + " is missing" );
136139 return null ;
137140 }
138- final MessagingStyle messaging = new MessagingStyle (mPersons .getSelf ().toPerson ());
141+ final PendingIntent on_reply = convs .getParcelable (KEY_ON_REPLY );
142+ final MessagingStyle messaging = new MessagingStyle (mUserSelf );
139143 if (parcelable_messages .length == 0 ) { // When only one message in this conversation
140- final Message message = buildMessage (n .when , n .tickerText , title , n .extras .getCharSequence (EXTRA_TEXT ), null , is_group );
144+ final Message message = buildMessage (conversation , n .when , n .tickerText , n .extras .getCharSequence (EXTRA_TEXT ), null , on_reply );
141145 messaging .addMessage (message );
142146 } else for (int i = 0 , num_messages = parcelable_messages .length ; i < num_messages ; i ++) {
143147 final Parcelable parcelable = parcelable_messages [i ];
@@ -147,20 +151,20 @@ public class MessagingBuilder {
147151 if (text == null ) continue ;
148152 final long timestamp = car_message .getLong (KEY_TIMESTAMP );
149153 final @ Nullable String author = car_message .getString (KEY_AUTHOR ); // Apparently always null (not yet implemented by WeChat)
150- final Message message = buildMessage (timestamp , i == num_messages - 1 ? n .tickerText : null , title , text , author , is_group );
154+ final Message message = buildMessage (conversation , timestamp , i == num_messages - 1 ? n .tickerText : null , text , author , on_reply );
151155 messaging .addMessage (message );
152156 }
153157
154- final PendingIntent on_read = conversation .getParcelable (KEY_ON_READ );
158+ final PendingIntent on_read = convs .getParcelable (KEY_ON_READ );
155159 if (on_read != null ) mMarkReadPendingIntents .put (sbn .getKey (), on_read ); // Mapped by evolved key,
156160
157- final PendingIntent on_reply ; final RemoteInput remote_input ;
158- if (SDK_INT >= N && ( on_reply = conversation . getParcelable ( KEY_ON_REPLY )) != null && (remote_input = conversation .getParcelable (KEY_REMOTE_INPUT )) != null ) {
161+ final RemoteInput remote_input ;
162+ if (SDK_INT >= N && on_reply != null && (remote_input = convs .getParcelable (KEY_REMOTE_INPUT )) != null ) {
159163 final CharSequence [] input_history = n .extras .getCharSequenceArray (EXTRA_REMOTE_INPUT_HISTORY );
160164 final PendingIntent proxy = proxyDirectReply (sbn , on_reply , remote_input , input_history );
161165 final RemoteInput .Builder tweaked = new RemoteInput .Builder (remote_input .getResultKey ()).addExtras (remote_input .getExtras ())
162166 .setAllowFreeFormInput (true ).setChoices (SmartReply .generateChoices (messaging ));
163- final String [] participants = conversation .getStringArray (KEY_PARTICIPANTS );
167+ final String [] participants = convs .getStringArray (KEY_PARTICIPANTS );
164168 if (participants != null && participants .length > 0 ) {
165169 final StringBuilder label = new StringBuilder ();
166170 for (final String participant : participants ) label .append (',' ).append (participant );
@@ -175,22 +179,33 @@ public class MessagingBuilder {
175179 return messaging ;
176180 }
177181
178- private Message buildMessage (final long when , final @ Nullable CharSequence ticker , final CharSequence title ,
179- final CharSequence text , @ Nullable String sender , final boolean group_chat ) {
182+ private Message buildMessage (final Conversation conversation , final long when , final @ Nullable CharSequence ticker ,
183+ final CharSequence text , @ Nullable String sender , final @ Nullable PendingIntent on_reply ) {
180184 CharSequence actual_text = text ;
181185 if (sender == null ) {
182186 sender = extractSenderFromText (text );
183187 if (sender != null ) {
184188 actual_text = text .subSequence (sender .length () + SENDER_MESSAGE_SEPARATOR .length (), text .length ());
185- if (TextUtils .equals (title , sender )) sender = null ; // In this case, the actual sender is user itself.
189+ if (TextUtils .equals (conversation . getTitle () , sender )) sender = null ; // In this case, the actual sender is user itself.
186190 }
187191 }
188192 actual_text = EmojiTranslator .translate (actual_text );
189- if (group_chat ) {
193+
194+ if (conversation .key == null ) try {
195+ 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 );
199+ }, null );
200+ } catch (final PendingIntent .CanceledException e ) {
201+ Log .e (TAG , "Error parsing reply intent." , e );
202+ }
203+
204+ if (conversation .getType () == TYPE_GROUP_CHAT ) {
190205 final String ticker_sender = ticker != null ? extractSenderFromText (ticker ) : null ; // Group nick is used in ticker while original nick in sender.
191- final Person person = sender != null ? mPersons . get ( title . toString (), sender , ticker_sender != null ? ticker_sender : sender ). toPerson () : null ;
206+ final Person person = sender == null ? null : conversation . getGroupParticipant ( sender , ticker_sender != null ? ticker_sender : sender );
192207 return new Message (actual_text , when , person );
193- } else return new Message (actual_text , when , SENDER_PLACEHOLDER );
208+ } else return new Message (actual_text , when , conversation . sender );
194209 }
195210
196211 private static @ Nullable String extractSenderFromText (final CharSequence text ) {
@@ -283,7 +298,8 @@ interface Controller { void recastNotification(String key, Bundle addition); }
283298 MessagingBuilder (final Context context , final Controller controller ) {
284299 mContext = context ;
285300 mController = controller ;
286- mPersons = new Persons (context .getString (R .string .self_display_name ));
301+ final Uri profile_lookup = ContactsContract .Contacts .getLookupUri (context .getContentResolver (), ContactsContract .Profile .CONTENT_URI );
302+ mUserSelf = new Person .Builder ().setUri (profile_lookup != null ? profile_lookup .toString () : null ).setName (context .getString (R .string .self_display_name )).build ();
287303
288304 final IntentFilter filter = new IntentFilter (ACTION_REPLY );
289305 filter .addDataScheme (SCHEME_KEY );
@@ -296,7 +312,7 @@ void close() {
296312
297313 private final Context mContext ;
298314 private final Controller mController ;
299- private final Persons mPersons ;
315+ private final Person mUserSelf ;
300316 private final Map <String /* evolved key */ , PendingIntent > mMarkReadPendingIntents = new ArrayMap <>();
301317 private static final String TAG = WeChatDecorator .TAG ;
302318}
0 commit comments