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