1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.messaging.datamodel.data;
18 
19 import android.content.ContentValues;
20 import android.content.res.Resources;
21 import android.database.Cursor;
22 import android.graphics.Color;
23 import android.os.Parcel;
24 import android.os.Parcelable;
25 import android.telephony.SubscriptionInfo;
26 import android.text.TextUtils;
27 
28 import androidx.appcompat.mms.MmsManager;
29 import androidx.collection.ArrayMap;
30 
31 import com.android.ex.chips.RecipientEntry;
32 import com.android.messaging.Factory;
33 import com.android.messaging.R;
34 import com.android.messaging.datamodel.DatabaseHelper;
35 import com.android.messaging.datamodel.DatabaseHelper.ParticipantColumns;
36 import com.android.messaging.datamodel.DatabaseWrapper;
37 import com.android.messaging.sms.MmsSmsUtils;
38 import com.android.messaging.util.Assert;
39 import com.android.messaging.util.PhoneUtils;
40 import com.android.messaging.util.TextUtil;
41 
42 /**
43  * A class that encapsulates all of the data for a specific participant in a conversation.
44  */
45 public class ParticipantData implements Parcelable {
46 
47     private static final ArrayMap<Integer, String> sSubIdtoParticipantIdCache =
48             new ArrayMap<Integer, String>();
49 
50     // We always use -1 as default/invalid sub id although system may give us anything negative
51     public static final int DEFAULT_SELF_SUB_ID = MmsManager.DEFAULT_SUB_ID;
52 
53     // This needs to be something apart from valid or DEFAULT_SELF_SUB_ID
54     public static final int OTHER_THAN_SELF_SUB_ID = DEFAULT_SELF_SUB_ID - 1;
55 
56     // Active slot ids are non-negative. Using -1 to designate to inactive self participants.
57     public static final int INVALID_SLOT_ID = -1;
58 
59     // TODO: may make sense to move this to common place?
60     public static final long PARTICIPANT_CONTACT_ID_NOT_RESOLVED = -1;
61     public static final long PARTICIPANT_CONTACT_ID_NOT_FOUND = -2;
62 
63     public static class ParticipantsQuery {
64         public static final String[] PROJECTION = new String[] {
65             ParticipantColumns._ID,
66             ParticipantColumns.SUB_ID,
67             ParticipantColumns.SIM_SLOT_ID,
68             ParticipantColumns.NORMALIZED_DESTINATION,
69             ParticipantColumns.SEND_DESTINATION,
70             ParticipantColumns.DISPLAY_DESTINATION,
71             ParticipantColumns.FULL_NAME,
72             ParticipantColumns.FIRST_NAME,
73             ParticipantColumns.PROFILE_PHOTO_URI,
74             ParticipantColumns.CONTACT_ID,
75             ParticipantColumns.LOOKUP_KEY,
76             ParticipantColumns.BLOCKED,
77             ParticipantColumns.SUBSCRIPTION_COLOR,
78             ParticipantColumns.SUBSCRIPTION_NAME,
79             ParticipantColumns.CONTACT_DESTINATION,
80         };
81 
82         public static final int INDEX_ID                        = 0;
83         public static final int INDEX_SUB_ID                    = 1;
84         public static final int INDEX_SIM_SLOT_ID               = 2;
85         public static final int INDEX_NORMALIZED_DESTINATION    = 3;
86         public static final int INDEX_SEND_DESTINATION          = 4;
87         public static final int INDEX_DISPLAY_DESTINATION       = 5;
88         public static final int INDEX_FULL_NAME                 = 6;
89         public static final int INDEX_FIRST_NAME                = 7;
90         public static final int INDEX_PROFILE_PHOTO_URI         = 8;
91         public static final int INDEX_CONTACT_ID                = 9;
92         public static final int INDEX_LOOKUP_KEY                = 10;
93         public static final int INDEX_BLOCKED                   = 11;
94         public static final int INDEX_SUBSCRIPTION_COLOR        = 12;
95         public static final int INDEX_SUBSCRIPTION_NAME         = 13;
96         public static final int INDEX_CONTACT_DESTINATION       = 14;
97     }
98 
99     /**
100      * @return The MMS unknown sender participant entity
101      */
getUnknownSenderDestination()102     public static String getUnknownSenderDestination() {
103         // This is a hard coded string rather than a localized one because we don't want it to
104         // change when you change locale.
105         return "\u02BCUNKNOWN_SENDER!\u02BC";
106     }
107 
108     private String mParticipantId;
109     private int mSubId;
110     private int mSlotId;
111     private String mNormalizedDestination;
112     private String mSendDestination;
113     private String mDisplayDestination;
114     private String mContactDestination;
115     private String mFullName;
116     private String mFirstName;
117     private String mProfilePhotoUri;
118     private long mContactId;
119     private String mLookupKey;
120     private int mSubscriptionColor;
121     private String mSubscriptionName;
122     private boolean mIsEmailAddress;
123     private boolean mBlocked;
124 
125     // Don't call constructor directly
ParticipantData()126     private ParticipantData() {
127     }
128 
getFromCursor(final Cursor cursor)129     public static ParticipantData getFromCursor(final Cursor cursor) {
130         final ParticipantData pd = new ParticipantData();
131         pd.mParticipantId = cursor.getString(ParticipantsQuery.INDEX_ID);
132         pd.mSubId = cursor.getInt(ParticipantsQuery.INDEX_SUB_ID);
133         pd.mSlotId = cursor.getInt(ParticipantsQuery.INDEX_SIM_SLOT_ID);
134         pd.mNormalizedDestination = cursor.getString(
135                 ParticipantsQuery.INDEX_NORMALIZED_DESTINATION);
136         pd.mSendDestination = cursor.getString(ParticipantsQuery.INDEX_SEND_DESTINATION);
137         pd.mDisplayDestination = cursor.getString(ParticipantsQuery.INDEX_DISPLAY_DESTINATION);
138         pd.mContactDestination = cursor.getString(ParticipantsQuery.INDEX_CONTACT_DESTINATION);
139         pd.mFullName = cursor.getString(ParticipantsQuery.INDEX_FULL_NAME);
140         pd.mFirstName = cursor.getString(ParticipantsQuery.INDEX_FIRST_NAME);
141         pd.mProfilePhotoUri = cursor.getString(ParticipantsQuery.INDEX_PROFILE_PHOTO_URI);
142         pd.mContactId = cursor.getLong(ParticipantsQuery.INDEX_CONTACT_ID);
143         pd.mLookupKey = cursor.getString(ParticipantsQuery.INDEX_LOOKUP_KEY);
144         pd.mIsEmailAddress = MmsSmsUtils.isEmailAddress(pd.mSendDestination);
145         pd.mBlocked = cursor.getInt(ParticipantsQuery.INDEX_BLOCKED) != 0;
146         pd.mSubscriptionColor = cursor.getInt(ParticipantsQuery.INDEX_SUBSCRIPTION_COLOR);
147         pd.mSubscriptionName = cursor.getString(ParticipantsQuery.INDEX_SUBSCRIPTION_NAME);
148         pd.maybeSetupUnknownSender();
149         return pd;
150     }
151 
getFromId(final DatabaseWrapper dbWrapper, final String participantId)152     public static ParticipantData getFromId(final DatabaseWrapper dbWrapper,
153             final String participantId) {
154         Cursor cursor = null;
155         try {
156             cursor = dbWrapper.query(DatabaseHelper.PARTICIPANTS_TABLE,
157                     ParticipantsQuery.PROJECTION,
158                     ParticipantColumns._ID + " =?",
159                     new String[] { participantId }, null, null, null);
160 
161             if (cursor.moveToFirst()) {
162                 return ParticipantData.getFromCursor(cursor);
163             } else {
164                 return null;
165             }
166         } finally {
167             if (cursor != null) {
168                 cursor.close();
169             }
170         }
171     }
172 
getFromRecipientEntry(final RecipientEntry recipientEntry)173     public static ParticipantData getFromRecipientEntry(final RecipientEntry recipientEntry) {
174         final ParticipantData pd = new ParticipantData();
175         pd.mParticipantId = null;
176         pd.mSubId = OTHER_THAN_SELF_SUB_ID;
177         pd.mSlotId = INVALID_SLOT_ID;
178         pd.mSendDestination = TextUtil.replaceUnicodeDigits(recipientEntry.getDestination());
179         pd.mIsEmailAddress = MmsSmsUtils.isEmailAddress(pd.mSendDestination);
180         pd.mNormalizedDestination = pd.mIsEmailAddress ?
181                 pd.mSendDestination :
182                 PhoneUtils.getDefault().getCanonicalBySystemLocale(pd.mSendDestination);
183         pd.mDisplayDestination = pd.mIsEmailAddress ?
184                 pd.mNormalizedDestination :
185                 PhoneUtils.getDefault().formatForDisplay(pd.mNormalizedDestination);
186         pd.mFullName = recipientEntry.getDisplayName();
187         pd.mFirstName = null;
188         pd.mProfilePhotoUri = (recipientEntry.getPhotoThumbnailUri() == null) ? null :
189                 recipientEntry.getPhotoThumbnailUri().toString();
190         pd.mContactId = recipientEntry.getContactId();
191         if (pd.mContactId < 0) {
192             // ParticipantData only supports real contact ids (>=0) based on faith that the contacts
193             // provider will continue to only use non-negative ids.  The UI uses contactId < 0 for
194             // special handling. We convert those to 'not resolved'
195             pd.mContactId = PARTICIPANT_CONTACT_ID_NOT_RESOLVED;
196         }
197         pd.mLookupKey = recipientEntry.getLookupKey();
198         pd.mBlocked = false;
199         pd.mSubscriptionColor = Color.TRANSPARENT;
200         pd.mSubscriptionName = null;
201         pd.maybeSetupUnknownSender();
202         return pd;
203     }
204 
205     // Shared code for getFromRawPhoneBySystemLocale and getFromRawPhoneBySimLocale
getFromRawPhone(final String phoneNumber)206     private static ParticipantData getFromRawPhone(final String phoneNumber) {
207         Assert.isTrue(phoneNumber != null);
208         final ParticipantData pd = new ParticipantData();
209         pd.mParticipantId = null;
210         pd.mSubId = OTHER_THAN_SELF_SUB_ID;
211         pd.mSlotId = INVALID_SLOT_ID;
212         pd.mSendDestination = TextUtil.replaceUnicodeDigits(phoneNumber);
213         pd.mIsEmailAddress = MmsSmsUtils.isEmailAddress(pd.mSendDestination);
214         pd.mFullName = null;
215         pd.mFirstName = null;
216         pd.mProfilePhotoUri = null;
217         pd.mContactId = PARTICIPANT_CONTACT_ID_NOT_RESOLVED;
218         pd.mLookupKey = null;
219         pd.mBlocked = false;
220         pd.mSubscriptionColor = Color.TRANSPARENT;
221         pd.mSubscriptionName = null;
222         return pd;
223     }
224 
225     /**
226      * Get an instance from a raw phone number and using system locale to normalize it.
227      *
228      * Use this when creating a participant that is for displaying UI and not associated
229      * with a specific SIM. For example, when creating a conversation using user entered
230      * phone number.
231      *
232      * @param phoneNumber The raw phone number
233      * @return instance
234      */
getFromRawPhoneBySystemLocale(final String phoneNumber)235     public static ParticipantData getFromRawPhoneBySystemLocale(final String phoneNumber) {
236         final ParticipantData pd = getFromRawPhone(phoneNumber);
237         pd.mNormalizedDestination = pd.mIsEmailAddress ?
238                 pd.mSendDestination :
239                 PhoneUtils.getDefault().getCanonicalBySystemLocale(pd.mSendDestination);
240         pd.mDisplayDestination = pd.mIsEmailAddress ?
241                 pd.mNormalizedDestination :
242                 PhoneUtils.getDefault().formatForDisplay(pd.mNormalizedDestination);
243         pd.maybeSetupUnknownSender();
244         return pd;
245     }
246 
247     /**
248      * Get an instance from a raw phone number and using SIM or system locale to normalize it.
249      *
250      * Use this when creating a participant that is associated with a specific SIM. For example,
251      * the sender of a received message or the recipient of a sending message that is already
252      * targeted at a specific SIM.
253      *
254      * @param phoneNumber The raw phone number
255      * @return instance
256      */
getFromRawPhoneBySimLocale( final String phoneNumber, final int subId)257     public static ParticipantData getFromRawPhoneBySimLocale(
258             final String phoneNumber, final int subId) {
259         final ParticipantData pd = getFromRawPhone(phoneNumber);
260         pd.mNormalizedDestination = pd.mIsEmailAddress ?
261                 pd.mSendDestination :
262                 PhoneUtils.get(subId).getCanonicalBySimLocale(pd.mSendDestination);
263         pd.mDisplayDestination = pd.mIsEmailAddress ?
264                 pd.mNormalizedDestination :
265                 PhoneUtils.getDefault().formatForDisplay(pd.mNormalizedDestination);
266         pd.maybeSetupUnknownSender();
267         return pd;
268     }
269 
getSelfParticipant(final int subId)270     public static ParticipantData getSelfParticipant(final int subId) {
271         Assert.isTrue(subId != OTHER_THAN_SELF_SUB_ID);
272         final ParticipantData pd = new ParticipantData();
273         pd.mParticipantId = null;
274         pd.mSubId = subId;
275         pd.mSlotId = INVALID_SLOT_ID;
276         pd.mIsEmailAddress = false;
277         pd.mSendDestination = null;
278         pd.mNormalizedDestination = null;
279         pd.mDisplayDestination = null;
280         pd.mFullName = null;
281         pd.mFirstName = null;
282         pd.mProfilePhotoUri = null;
283         pd.mContactId = PARTICIPANT_CONTACT_ID_NOT_RESOLVED;
284         pd.mLookupKey = null;
285         pd.mBlocked = false;
286         pd.mSubscriptionColor = Color.TRANSPARENT;
287         pd.mSubscriptionName = null;
288         return pd;
289     }
290 
getParticipantId(final DatabaseWrapper db, final int subId)291     public static String getParticipantId(final DatabaseWrapper db, final int subId) {
292         String id;
293         synchronized (sSubIdtoParticipantIdCache) {
294             id = sSubIdtoParticipantIdCache.get(subId);
295         }
296 
297         if (id != null) {
298             return id;
299         }
300 
301         try (final Cursor cursor =
302                     db.query(DatabaseHelper.PARTICIPANTS_TABLE,
303                             new String[] {ParticipantColumns._ID},
304                             ParticipantColumns.SUB_ID + " =?",
305                             new String[] {Integer.toString(subId)}, null, null, null)) {
306 
307             if (cursor.moveToFirst()) {
308                 // We found an existing participant in the database
309                 id = cursor.getString(0);
310                 synchronized (sSubIdtoParticipantIdCache) {
311                     // Add it to the cache for next time
312                     sSubIdtoParticipantIdCache.put(subId, id);
313                 }
314             }
315         }
316         return id;
317     }
318 
maybeSetupUnknownSender()319     private void maybeSetupUnknownSender() {
320         if (isUnknownSender()) {
321             // Because your locale may change, we setup the display string for the unknown sender
322             // on the fly rather than relying on the version in the database.
323             final Resources resources = Factory.get().getApplicationContext().getResources();
324             mDisplayDestination = resources.getString(R.string.unknown_sender);
325             mFullName = mDisplayDestination;
326         }
327     }
328 
getNormalizedDestination()329     public String getNormalizedDestination() {
330         return mNormalizedDestination;
331     }
332 
getSendDestination()333     public String getSendDestination() {
334         return mSendDestination;
335     }
336 
getDisplayDestination()337     public String getDisplayDestination() {
338         return mDisplayDestination;
339     }
340 
getContactDestination()341     public String getContactDestination() {
342         return mContactDestination;
343     }
344 
getFullName()345     public String getFullName() {
346         return mFullName;
347     }
348 
getFirstName()349     public String getFirstName() {
350         return mFirstName;
351     }
352 
getDisplayName(final boolean preferFullName)353     public String getDisplayName(final boolean preferFullName) {
354         if (preferFullName) {
355             // Prefer full name over first name
356             if (!TextUtils.isEmpty(mFullName)) {
357                 return mFullName;
358             }
359             if (!TextUtils.isEmpty(mFirstName)) {
360                 return mFirstName;
361             }
362         } else {
363             // Prefer first name over full name
364             if (!TextUtils.isEmpty(mFirstName)) {
365                 return mFirstName;
366             }
367             if (!TextUtils.isEmpty(mFullName)) {
368                 return mFullName;
369             }
370         }
371 
372         // Fallback to the display destination
373         if (!TextUtils.isEmpty(mDisplayDestination)) {
374             return mDisplayDestination;
375         }
376 
377         return Factory.get().getApplicationContext().getResources().getString(
378                 R.string.unknown_sender);
379     }
380 
getProfilePhotoUri()381     public String getProfilePhotoUri() {
382         return mProfilePhotoUri;
383     }
384 
getContactId()385     public long getContactId() {
386         return mContactId;
387     }
388 
getLookupKey()389     public String getLookupKey() {
390         return mLookupKey;
391     }
392 
updatePhoneNumberForSelfIfChanged()393     public boolean updatePhoneNumberForSelfIfChanged() {
394         final String phoneNumber =
395                 PhoneUtils.get(mSubId).getCanonicalForSelf(true/*allowOverride*/);
396         boolean changed = false;
397         if (isSelf() && !TextUtils.equals(phoneNumber, mNormalizedDestination)) {
398             mNormalizedDestination = phoneNumber;
399             mSendDestination = phoneNumber;
400             mDisplayDestination = mIsEmailAddress ?
401                     phoneNumber :
402                     PhoneUtils.getDefault().formatForDisplay(phoneNumber);
403             changed = true;
404         }
405         return changed;
406     }
407 
updateSubscriptionInfoForSelfIfChanged(final SubscriptionInfo subscriptionInfo)408     public boolean updateSubscriptionInfoForSelfIfChanged(final SubscriptionInfo subscriptionInfo) {
409         boolean changed = false;
410         if (isSelf()) {
411             if (subscriptionInfo == null) {
412                 // The subscription is inactive. Check if the participant is still active.
413                 if (isActiveSubscription()) {
414                     mSlotId = INVALID_SLOT_ID;
415                     mSubscriptionColor = Color.TRANSPARENT;
416                     mSubscriptionName = "";
417                     changed = true;
418                 }
419             } else {
420                 final int slotId = subscriptionInfo.getSimSlotIndex();
421                 final int color = subscriptionInfo.getIconTint();
422                 final CharSequence name = subscriptionInfo.getDisplayName();
423                 if (mSlotId != slotId || mSubscriptionColor != color || mSubscriptionName != name) {
424                     mSlotId = slotId;
425                     mSubscriptionColor = color;
426                     mSubscriptionName = name.toString();
427                     changed = true;
428                 }
429             }
430         }
431         return changed;
432     }
433 
setFullName(final String fullName)434     public void setFullName(final String fullName) {
435         mFullName = fullName;
436     }
437 
setFirstName(final String firstName)438     public void setFirstName(final String firstName) {
439         mFirstName = firstName;
440     }
441 
setProfilePhotoUri(final String profilePhotoUri)442     public void setProfilePhotoUri(final String profilePhotoUri) {
443         mProfilePhotoUri = profilePhotoUri;
444     }
445 
setContactId(final long contactId)446     public void setContactId(final long contactId) {
447         mContactId = contactId;
448     }
449 
setLookupKey(final String lookupKey)450     public void setLookupKey(final String lookupKey) {
451         mLookupKey = lookupKey;
452     }
453 
setSendDestination(final String destination)454     public void setSendDestination(final String destination) {
455         mSendDestination = destination;
456     }
457 
setContactDestination(final String destination)458     public void setContactDestination(final String destination) {
459         mContactDestination = destination;
460     }
461 
getSubId()462     public int getSubId() {
463         return mSubId;
464     }
465 
466     /**
467      * @return whether this sub is active. Note that {@link ParticipantData#DEFAULT_SELF_SUB_ID} is
468      *         is considered as active if there is any active SIM.
469      */
isActiveSubscription()470     public boolean isActiveSubscription() {
471         return mSlotId != INVALID_SLOT_ID;
472     }
473 
isDefaultSelf()474     public boolean isDefaultSelf() {
475         return mSubId == ParticipantData.DEFAULT_SELF_SUB_ID;
476     }
477 
getSlotId()478     public int getSlotId() {
479         return mSlotId;
480     }
481 
482     /**
483      * Slot IDs in the subscription manager is zero-based, but we want to show it
484      * as 1-based in UI.
485      */
getDisplaySlotId()486     public int getDisplaySlotId() {
487         return getSlotId() + 1;
488     }
489 
getSubscriptionColor()490     public int getSubscriptionColor() {
491         Assert.isTrue(isActiveSubscription());
492         // Force the alpha channel to 0xff to ensure the returned color is solid.
493         return mSubscriptionColor | 0xff000000;
494     }
495 
getSubscriptionName()496     public String getSubscriptionName() {
497         Assert.isTrue(isActiveSubscription());
498         return mSubscriptionName;
499     }
500 
getId()501     public String getId() {
502         return mParticipantId;
503     }
504 
isSelf()505     public boolean isSelf() {
506         return (mSubId != OTHER_THAN_SELF_SUB_ID);
507     }
508 
isEmail()509     public boolean isEmail() {
510         return mIsEmailAddress;
511     }
512 
isContactIdResolved()513     public boolean isContactIdResolved() {
514         return (mContactId != PARTICIPANT_CONTACT_ID_NOT_RESOLVED);
515     }
516 
isBlocked()517     public boolean isBlocked() {
518         return mBlocked;
519     }
520 
isUnknownSender()521     public boolean isUnknownSender() {
522         final String unknownSender = ParticipantData.getUnknownSenderDestination();
523         return (TextUtils.equals(mSendDestination, unknownSender));
524     }
525 
toContentValues()526     public ContentValues toContentValues() {
527         final ContentValues values = new ContentValues();
528         values.put(ParticipantColumns.SUB_ID, mSubId);
529         values.put(ParticipantColumns.SIM_SLOT_ID, mSlotId);
530         values.put(DatabaseHelper.ParticipantColumns.SEND_DESTINATION, mSendDestination);
531 
532         if (!isUnknownSender()) {
533             values.put(DatabaseHelper.ParticipantColumns.DISPLAY_DESTINATION, mDisplayDestination);
534             values.put(DatabaseHelper.ParticipantColumns.NORMALIZED_DESTINATION,
535                     mNormalizedDestination);
536             values.put(ParticipantColumns.FULL_NAME, mFullName);
537             values.put(ParticipantColumns.FIRST_NAME, mFirstName);
538         }
539 
540         values.put(ParticipantColumns.PROFILE_PHOTO_URI, mProfilePhotoUri);
541         values.put(ParticipantColumns.CONTACT_ID, mContactId);
542         values.put(ParticipantColumns.LOOKUP_KEY, mLookupKey);
543         values.put(ParticipantColumns.BLOCKED, mBlocked);
544         values.put(ParticipantColumns.SUBSCRIPTION_COLOR, mSubscriptionColor);
545         values.put(ParticipantColumns.SUBSCRIPTION_NAME, mSubscriptionName);
546         return values;
547     }
548 
ParticipantData(final Parcel in)549     public ParticipantData(final Parcel in) {
550         mParticipantId = in.readString();
551         mSubId = in.readInt();
552         mSlotId = in.readInt();
553         mNormalizedDestination = in.readString();
554         mSendDestination = in.readString();
555         mDisplayDestination = in.readString();
556         mFullName = in.readString();
557         mFirstName = in.readString();
558         mProfilePhotoUri = in.readString();
559         mContactId = in.readLong();
560         mLookupKey = in.readString();
561         mIsEmailAddress = in.readInt() != 0;
562         mBlocked = in.readInt() != 0;
563         mSubscriptionColor = in.readInt();
564         mSubscriptionName = in.readString();
565     }
566 
567     @Override
describeContents()568     public int describeContents() {
569         return 0;
570     }
571 
572     @Override
writeToParcel(final Parcel dest, final int flags)573     public void writeToParcel(final Parcel dest, final int flags) {
574         dest.writeString(mParticipantId);
575         dest.writeInt(mSubId);
576         dest.writeInt(mSlotId);
577         dest.writeString(mNormalizedDestination);
578         dest.writeString(mSendDestination);
579         dest.writeString(mDisplayDestination);
580         dest.writeString(mFullName);
581         dest.writeString(mFirstName);
582         dest.writeString(mProfilePhotoUri);
583         dest.writeLong(mContactId);
584         dest.writeString(mLookupKey);
585         dest.writeInt(mIsEmailAddress ? 1 : 0);
586         dest.writeInt(mBlocked ? 1 : 0);
587         dest.writeInt(mSubscriptionColor);
588         dest.writeString(mSubscriptionName);
589     }
590 
591     public static final Parcelable.Creator<ParticipantData> CREATOR
592     = new Parcelable.Creator<ParticipantData>() {
593         @Override
594         public ParticipantData createFromParcel(final Parcel in) {
595             return new ParticipantData(in);
596         }
597 
598         @Override
599         public ParticipantData[] newArray(final int size) {
600             return new ParticipantData[size];
601         }
602     };
603 }
604