SimContact.java revision a75206b1cba895ce629b9383e4fd0fef24049e7b
1/* 2 * Copyright (C) 2016 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 */ 16package com.android.contacts.common.model; 17 18import android.content.ContentProviderOperation; 19import android.database.MatrixCursor; 20import android.net.Uri; 21import android.os.Parcel; 22import android.os.Parcelable; 23import android.provider.ContactsContract; 24import android.provider.ContactsContract.CommonDataKinds.Email; 25import android.provider.ContactsContract.CommonDataKinds.Phone; 26import android.provider.ContactsContract.CommonDataKinds.StructuredName; 27import android.text.TextUtils; 28 29import com.android.contacts.common.model.account.AccountWithDataSet; 30import com.google.common.collect.ComparisonChain; 31import com.google.common.collect.Ordering; 32 33import java.util.Arrays; 34import java.util.Collection; 35import java.util.Collections; 36import java.util.Comparator; 37import java.util.List; 38import java.util.Objects; 39 40/** 41 * Holds data for contacts loaded from the SIM card. 42 */ 43public class SimContact implements Parcelable { 44 private final long mId; 45 private final String mName; 46 private final String mPhone; 47 private final String[] mEmails; 48 49 public SimContact(long id, String name, String phone) { 50 this(id, name, phone, null); 51 } 52 53 public SimContact(long id, String name, String phone, String[] emails) { 54 mId = id; 55 mName = name; 56 mPhone = phone == null ? "" : phone.trim(); 57 mEmails = emails; 58 } 59 60 public SimContact(SimContact other) { 61 this(other.mId, other.mName, other.mPhone, other.mEmails); 62 } 63 64 public long getId() { 65 return mId; 66 } 67 68 public String getName() { 69 return mName; 70 } 71 72 public String getPhone() { 73 return mPhone; 74 } 75 76 public String[] getEmails() { 77 return mEmails; 78 } 79 80 public void appendCreateContactOperations(List<ContentProviderOperation> ops, 81 AccountWithDataSet targetAccount) { 82 // There is nothing to save so skip it. 83 if (!hasName() && !hasPhone() && !hasEmails()) return; 84 85 final int rawContactOpIndex = ops.size(); 86 ops.add(ContentProviderOperation.newInsert(ContactsContract.RawContacts.CONTENT_URI) 87 .withYieldAllowed(true) 88 .withValue(ContactsContract.RawContacts.ACCOUNT_NAME, targetAccount.name) 89 .withValue(ContactsContract.RawContacts.ACCOUNT_TYPE, targetAccount.type) 90 .withValue(ContactsContract.RawContacts.DATA_SET, targetAccount.dataSet) 91 .build()); 92 if (mName != null) { 93 ops.add(createInsertOp(rawContactOpIndex, StructuredName.CONTENT_ITEM_TYPE, 94 StructuredName.DISPLAY_NAME, mName)); 95 } 96 if (!mPhone.isEmpty()) { 97 ops.add(createInsertOp(rawContactOpIndex, Phone.CONTENT_ITEM_TYPE, 98 Phone.NUMBER, mPhone)); 99 } 100 if (mEmails != null) { 101 for (String email : mEmails) { 102 ops.add(createInsertOp(rawContactOpIndex, Email.CONTENT_ITEM_TYPE, 103 Email.ADDRESS, email)); 104 } 105 } 106 } 107 108 private ContentProviderOperation createInsertOp(int rawContactOpIndex, String mimeType, 109 String column, String value) { 110 return ContentProviderOperation.newInsert(ContactsContract.Data.CONTENT_URI) 111 .withValueBackReference(ContactsContract.Data.RAW_CONTACT_ID, rawContactOpIndex) 112 .withValue(ContactsContract.Data.MIMETYPE, mimeType) 113 .withValue(column, value) 114 .build(); 115 } 116 117 public void appendAsContactRow(MatrixCursor cursor) { 118 cursor.newRow().add(ContactsContract.Contacts._ID, mId) 119 .add(ContactsContract.Contacts.DISPLAY_NAME_PRIMARY, mName) 120 .add(ContactsContract.Contacts.LOOKUP_KEY, getLookupKey()); 121 } 122 123 public boolean hasName() { 124 return !TextUtils.isEmpty(mName); 125 } 126 127 public boolean hasPhone() { 128 return !mPhone.isEmpty(); 129 } 130 131 public boolean hasEmails() { 132 return mEmails != null && mEmails.length > 0; 133 } 134 135 /** 136 * Generate a "fake" lookup key. This is needed because 137 * {@link com.android.contacts.common.ContactPhotoManager} will only generate a letter avatar 138 * if the contact has a lookup key. 139 */ 140 private String getLookupKey() { 141 if (mName != null) { 142 return "sim-n-" + Uri.encode(mName); 143 } else if (mPhone != null) { 144 return "sim-p-" + Uri.encode(mPhone); 145 } else { 146 return null; 147 } 148 } 149 150 @Override 151 public String toString() { 152 return "SimContact{" + 153 "mId=" + mId + 154 ", mName='" + mName + '\'' + 155 ", mPhone='" + mPhone + '\'' + 156 ", mEmails=" + Arrays.toString(mEmails) + 157 '}'; 158 } 159 160 @Override 161 public boolean equals(Object o) { 162 if (this == o) return true; 163 if (o == null || getClass() != o.getClass()) return false; 164 165 final SimContact that = (SimContact) o; 166 167 return mId == that.mId && Objects.equals(mName, that.mName) && 168 Objects.equals(mPhone, that.mPhone) && Arrays.equals(mEmails, that.mEmails); 169 } 170 171 @Override 172 public int hashCode() { 173 int result = (int) (mId ^ (mId >>> 32)); 174 result = 31 * result + (mName != null ? mName.hashCode() : 0); 175 result = 31 * result + (mPhone != null ? mPhone.hashCode() : 0); 176 result = 31 * result + Arrays.hashCode(mEmails); 177 return result; 178 } 179 180 @Override 181 public int describeContents() { 182 return 0; 183 } 184 185 @Override 186 public void writeToParcel(Parcel dest, int flags) { 187 dest.writeLong(mId); 188 dest.writeString(mName); 189 dest.writeString(mPhone); 190 dest.writeStringArray(mEmails); 191 } 192 193 public static final Creator<SimContact> CREATOR = new Creator<SimContact>() { 194 @Override 195 public SimContact createFromParcel(Parcel source) { 196 final long id = source.readLong(); 197 final String name = source.readString(); 198 final String phone = source.readString(); 199 final String[] emails = source.createStringArray(); 200 return new SimContact(id, name, phone, emails); 201 } 202 203 @Override 204 public SimContact[] newArray(int size) { 205 return new SimContact[size]; 206 } 207 }; 208 209 /** 210 * Convert a collection of SIM contacts to a Cursor matching a query from 211 * {@link android.provider.ContactsContract.Contacts#CONTENT_URI} with the provided projection. 212 * 213 * This allows a collection of SIM contacts to be displayed using the existing adapters for 214 * contacts. 215 */ 216 public static final MatrixCursor convertToContactsCursor(Collection<SimContact> contacts, 217 String[] projection) { 218 final MatrixCursor result = new MatrixCursor(projection); 219 for (SimContact contact : contacts) { 220 contact.appendAsContactRow(result); 221 } 222 return result; 223 } 224 225 /** 226 * Returns the index of a contact with a matching name and phone 227 * @param contacts list to search. Should be sorted using 228 * {@link SimContact#compareByPhoneThenName()} 229 * @param phone the phone to search for 230 * @param name the name to search for 231 */ 232 public static int findByPhoneAndName(List<SimContact> contacts, String phone, String name) { 233 return Collections.binarySearch(contacts, new SimContact(-1, name, phone, null), 234 compareByPhoneThenName()); 235 } 236 237 public static final Comparator<SimContact> compareByPhoneThenName() { 238 return new Comparator<SimContact>() { 239 @Override 240 public int compare(SimContact lhs, SimContact rhs) { 241 return ComparisonChain.start() 242 .compare(lhs.mPhone, rhs.mPhone) 243 .compare(lhs.mName, rhs.mName, Ordering.<String>natural().nullsFirst()) 244 .result(); 245 } 246 }; 247 } 248 249 public static final Comparator<SimContact> compareById() { 250 return new Comparator<SimContact>() { 251 @Override 252 public int compare(SimContact lhs, SimContact rhs) { 253 // We assume ids are unique. 254 return Long.compare(lhs.mId, rhs.mId); 255 } 256 }; 257 } 258} 259