summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Android.mk11
-rw-r--r--info_lookup/src/com/cyanogen/lookup/phonenumber/response/LookupResponse.java4
-rw-r--r--libs/picaso.jarbin0 -> 120459 bytes
-rw-r--r--proguard.flags4
-rw-r--r--src/com/android/contacts/common/model/Contact.java19
-rw-r--r--src/com/android/contacts/common/model/ContactBuilder.java638
-rw-r--r--src/com/android/contacts/common/model/ContactLoader.java175
-rw-r--r--src/com/android/contacts/common/model/DirectoryId.java35
-rw-r--r--src/com/android/contacts/common/util/CallerMetaData.java17
9 files changed, 829 insertions, 74 deletions
diff --git a/Android.mk b/Android.mk
index 7c9e5a99..0387f7dd 100644
--- a/Android.mk
+++ b/Android.mk
@@ -34,7 +34,8 @@ LOCAL_STATIC_JAVA_LIBRARIES := \
android-common \
android-support-v13 \
android-support-v4 \
- libphonenumber
+ libphonenumber \
+ contacts-picaso
LOCAL_PACKAGE_NAME := com.android.contacts.common
@@ -43,5 +44,13 @@ LOCAL_PROGUARD_FLAG_FILES := proguard.flags
include $(BUILD_PACKAGE)
+include $(CLEAR_VARS)
+
+# Open-source libphonenumber libraries as found in code.google.com/p/libphonenumber
+LOCAL_PREBUILT_STATIC_JAVA_LIBRARIES := \
+ contacts-picaso:libs/picaso.jar
+
+include $(BUILD_MULTI_PREBUILT)
+
# Use the following include to make our test apk.
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/info_lookup/src/com/cyanogen/lookup/phonenumber/response/LookupResponse.java b/info_lookup/src/com/cyanogen/lookup/phonenumber/response/LookupResponse.java
index ce60ffbf..1cb51c2b 100644
--- a/info_lookup/src/com/cyanogen/lookup/phonenumber/response/LookupResponse.java
+++ b/info_lookup/src/com/cyanogen/lookup/phonenumber/response/LookupResponse.java
@@ -6,6 +6,7 @@ import android.graphics.drawable.Drawable;
* ADT to store the result of a phone number lookup
*/
public class LookupResponse {
+ public String mProviderName;
public String mName;
public String mNumber;
public String mCity;
@@ -18,7 +19,8 @@ public class LookupResponse {
@Override
public String toString() {
- return String.format("{ name = %s, number = %s, city = %s, country = %s, address = %s, photo-url : %s, spam-count = %d}",
+ return String.format("{ providerName = %s, name = %s, number = %s, city = %s, country = %s, address = %s, photo-url : %s, spam-count = %d}",
+ mProviderName != null ? mProviderName : "null" ,
mName != null ? mName : "null" ,
mNumber != null ? mNumber : "null" ,
mCity != null ? mCity : "null" ,
diff --git a/libs/picaso.jar b/libs/picaso.jar
new file mode 100644
index 00000000..6acbaa14
--- /dev/null
+++ b/libs/picaso.jar
Binary files differ
diff --git a/proguard.flags b/proguard.flags
index cd9820a6..b9115158 100644
--- a/proguard.flags
+++ b/proguard.flags
@@ -1,3 +1,7 @@
+-dontwarn com.cyanogen.**
+-dontwarn com.squareup.**
+-dontwarn com.squareup.okhttp.**
+
-keep class *
-verbose
diff --git a/src/com/android/contacts/common/model/Contact.java b/src/com/android/contacts/common/model/Contact.java
index 9b96f861..320d2653 100644
--- a/src/com/android/contacts/common/model/Contact.java
+++ b/src/com/android/contacts/common/model/Contact.java
@@ -97,6 +97,9 @@ public class Contact {
private final Contact.Status mStatus;
private final Exception mException;
+ private String mProviderName;
+ private int mSpamCount = 0;
+
/**
* Constructor for special results, namely "no contact found" and "error".
*/
@@ -493,4 +496,20 @@ public class Contact {
/* package */ void setGroupMetaData(ImmutableList<GroupMetaData> groups) {
mGroups = groups;
}
+
+ public int getSpamCount() {
+ return mSpamCount;
+ }
+
+ public void setSpamCount(int spamCount) {
+ mSpamCount = spamCount;
+ }
+
+ public String getProviderName() {
+ return mProviderName;
+ }
+
+ public void setProviderName(String providerName) {
+ mProviderName = providerName;
+ }
}
diff --git a/src/com/android/contacts/common/model/ContactBuilder.java b/src/com/android/contacts/common/model/ContactBuilder.java
new file mode 100644
index 00000000..9840e711
--- /dev/null
+++ b/src/com/android/contacts/common/model/ContactBuilder.java
@@ -0,0 +1,638 @@
+/*
+ * Copyright (C) 2014 Xiao-Long Chen <chillermillerlong@hotmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.model;
+
+import android.content.ContentValues;
+import android.text.TextUtils;
+
+import com.android.contacts.common.util.CallerMetaData;
+import com.android.contacts.common.util.Constants;
+import com.android.contacts.common.util.DataStatus;
+
+import android.net.Uri;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.Phone;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.CommonDataKinds.StructuredPostal;
+import android.provider.ContactsContract.CommonDataKinds.Website;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.Directory;
+import android.provider.ContactsContract.DisplayNameSources;
+import android.util.Log;
+
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableMap;
+
+import java.util.ArrayList;
+
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+public class ContactBuilder {
+ private static final String TAG =
+ ContactBuilder.class.getSimpleName();
+
+ private static final boolean DEBUG = false;
+
+ /** Used to choose the proper directory ID */
+ public static final int FORWARD_LOOKUP = 0;
+ public static final int PEOPLE_LOOKUP = 1;
+ public static final int REVERSE_LOOKUP = 2;
+
+ // drawable for business will be generated by PhotoLoader
+ public static final String PHOTO_URI_BUSINESS = null;
+
+ private ArrayList<Address> mAddresses = new ArrayList<Address>();
+ private ArrayList<PhoneNumber> mPhoneNumbers
+ = new ArrayList<PhoneNumber>();
+ private ArrayList<WebsiteUrl> mWebsites
+ = new ArrayList<WebsiteUrl>();
+
+ private int mDirectoryType;
+ private long mDirectoryId = DirectoryId.DEFAULT;
+
+ private Name mName;
+
+ private String mNormalizedNumber;
+ private String mFormattedNumber;
+ private int mDisplayNameSource = DisplayNameSources.ORGANIZATION;
+ private Uri mPhotoUri;
+ private String mPhotoUrl;
+
+ private boolean mIsBusiness;
+
+ private int mSpamCount;
+ private String mInfoProviderName;
+ private String mSuccinctLocation;
+
+ public ContactBuilder(int directoryType, String normalizedNumber,
+ String formattedNumber) {
+ mDirectoryType = directoryType;
+ mNormalizedNumber = normalizedNumber;
+ mFormattedNumber = formattedNumber;
+ }
+
+ public ContactBuilder(Uri encodedContactUri) throws JSONException {
+ String jsonData = encodedContactUri.getEncodedFragment();
+ String directoryId =
+ encodedContactUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ if (!TextUtils.isEmpty(directoryId)) {
+ try {
+ mDirectoryId = Long.parseLong(directoryId);
+ } catch (NumberFormatException e) {
+ Log.e(TAG, "Error parsing directory id of uri " + encodedContactUri, e);
+ }
+ }
+ try {
+ // name
+ JSONObject json = new JSONObject(jsonData);
+ JSONObject contact = json.optJSONObject(Contacts.CONTENT_ITEM_TYPE);
+ JSONObject nameObj = contact.optJSONObject(StructuredName.CONTENT_ITEM_TYPE);
+ mName = new Name(nameObj);
+
+ if (contact != null) {
+ // numbers
+ if (contact.has(Phone.CONTENT_ITEM_TYPE)) {
+ String phoneData = contact.getString(Phone.CONTENT_ITEM_TYPE);
+ Object phoneObject = new JSONTokener(phoneData).nextValue();
+ JSONArray phoneNumbers;
+ if (phoneObject instanceof JSONObject) {
+ phoneNumbers = new JSONArray();
+ phoneNumbers.put(phoneObject);
+ } else {
+ phoneNumbers = contact.getJSONArray(Phone.CONTENT_ITEM_TYPE);
+ }
+ for (int i = 0; i < phoneNumbers.length(); ++i) {
+ JSONObject phoneObj = phoneNumbers.getJSONObject(i);
+ mPhoneNumbers.add(new PhoneNumber(phoneObj));
+ }
+ }
+
+ // address
+ if (contact.has(StructuredPostal.CONTENT_ITEM_TYPE)) {
+ JSONArray addresses = contact.getJSONArray(StructuredPostal.CONTENT_ITEM_TYPE);
+ for (int i = 0; i < addresses.length(); ++i) {
+ JSONObject addrObj = addresses.getJSONObject(i);
+ mAddresses.add(new Address(addrObj));
+ }
+ }
+
+ // websites
+ if (contact.has(Website.CONTENT_ITEM_TYPE)) {
+ JSONArray websites = contact.getJSONArray(Website.CONTENT_ITEM_TYPE);
+ for (int i = 0; i < websites.length(); ++i) {
+ JSONObject websiteObj = websites.getJSONObject(i);
+ final WebsiteUrl websiteUrl = new WebsiteUrl(websiteObj);
+ if (!TextUtils.isEmpty(websiteUrl.url)) {
+ mWebsites.add(new WebsiteUrl(websiteObj));
+ }
+ }
+ }
+
+ mSpamCount = contact.optInt(CallerMetaData.SPAM_COUNT, 0);
+ mInfoProviderName = contact.optString(CallerMetaData.INFO_PROVIDER, null);
+ mSuccinctLocation = contact.optString(CallerMetaData.SUCCINCT_LOCATION, null);
+ mPhotoUrl = contact.optString(CallerMetaData.PHOTO_URL, null);
+ }
+
+ }
+ catch(JSONException e) {
+ Log.e(TAG, "Error parsing encoded fragment of uri " + encodedContactUri, e);
+ throw e;
+ }
+ }
+
+ public void setSpamCount(int spamCount) {
+ mSpamCount= spamCount;
+ }
+
+ public int getSpamCount() {
+ return mSpamCount;
+ }
+
+ public void setInfoProviderName(String infoProviderName) {
+ mInfoProviderName = infoProviderName;
+ }
+
+ public String getInfoProviderName() {
+ return mInfoProviderName;
+ }
+
+ public void setSuccinctLocation(String location) {
+ mSuccinctLocation = location;
+ }
+
+ public String getSuccinctLocation() {
+ return mSuccinctLocation;
+ }
+
+ public void addAddress(Address address) {
+ if (DEBUG) Log.d(TAG, "Adding address");
+ if (address != null) {
+ mAddresses.add(address);
+ }
+ }
+
+ public Address[] getAddresses() {
+ return mAddresses.toArray(new Address[mAddresses.size()]);
+ }
+
+ public void addPhoneNumber(PhoneNumber phoneNumber) {
+ if (DEBUG) Log.d(TAG, "Adding phone number");
+ if (phoneNumber != null) {
+ mPhoneNumbers.add(phoneNumber);
+ }
+ }
+
+ public PhoneNumber[] getPhoneNumbers() {
+ return mPhoneNumbers.toArray(
+ new PhoneNumber[mPhoneNumbers.size()]);
+ }
+
+ public void addWebsite(WebsiteUrl website) {
+ if (DEBUG) Log.d(TAG, "Adding website");
+ if (website != null) {
+ mWebsites.add(website);
+ }
+ }
+
+ public WebsiteUrl[] getWebsites() {
+ return mWebsites.toArray(new WebsiteUrl[mWebsites.size()]);
+ }
+
+ public void setName(Name name) {
+ if (DEBUG) Log.d(TAG, "Setting name");
+ if (name != null) {
+ mName = name;
+ }
+ }
+
+ public Name getName() {
+ return mName;
+ }
+
+ public void setPhotoUri(String photoUri) {
+ if (photoUri != null) {
+ setPhotoUri(Uri.parse(photoUri));
+ }
+ }
+
+ public void setPhotoUri(Uri photoUri) {
+ if (DEBUG) Log.d(TAG, "Setting photo URI");
+ mPhotoUri = photoUri;
+ }
+
+ public Uri getPhotoUri() {
+ return mPhotoUri;
+ }
+
+ public void setPhotoUrl(String url) {
+ if (!TextUtils.isEmpty(url)) {
+ mPhotoUrl = url;
+ }
+ }
+
+ public String getPhotoUrl() {
+ return mPhotoUrl;
+ }
+
+ public long getDirectoryId() {
+ return mDirectoryId;
+ }
+
+ public void setIsBusiness(boolean isBusiness) {
+ if (DEBUG) Log.d(TAG, "Setting isBusiness to " + isBusiness);
+ mIsBusiness = isBusiness;
+ }
+
+ public boolean isBusiness() {
+ return mIsBusiness;
+ }
+
+ public Contact build() {
+ if (mName == null) {
+ throw new IllegalStateException("Name has not been set");
+ }
+ Uri lookupUri = null;
+
+ if (mDirectoryType != FORWARD_LOOKUP
+ && mDirectoryType != PEOPLE_LOOKUP
+ && mDirectoryType != REVERSE_LOOKUP) {
+ throw new IllegalStateException("Invalid directory type");
+ }
+
+ final ContentValues values = new ContentValues();
+ values.put(ContactsContract.Data._ID, -1);
+ values.put(ContactsContract.Data.CONTACT_ID, -1);
+ final RawContact rawContact = new RawContact(values);
+
+ final ContentValues numberValues = new ContentValues();
+ numberValues.put(ContactsContract.Data._ID, -1);
+ numberValues.put(ContactsContract.Data.CONTACT_ID, -1);
+ numberValues.put(ContactsContract.Data.MIMETYPE, Phone.CONTENT_ITEM_TYPE);
+
+ // Use the incoming call's phone number if no other phone number
+ // is specified. The reverse lookup source could present the phone
+ // number differently (eg. without the area code).
+ if (mPhoneNumbers.size() == 0) {
+ PhoneNumber pn = new PhoneNumber();
+ // Use the formatted number where possible
+ pn.number = mFormattedNumber != null
+ ? mFormattedNumber : mNormalizedNumber;
+ pn.type = Phone.TYPE_MAIN;
+ numberValues.put(ContactsContract.Data.DATA1, pn.number);
+ addPhoneNumber(pn);
+ }
+
+ try {
+ JSONObject contact = new JSONObject();
+
+ // Insert the name
+ contact.put(StructuredName.CONTENT_ITEM_TYPE,
+ mName.getJsonObject());
+
+ final String dataKeyPrefix = "data";
+
+ // Insert phone numbers
+ JSONArray phoneNumbers = new JSONArray();
+ for (int i = 0; i < mPhoneNumbers.size(); i++) {
+ phoneNumbers.put(mPhoneNumbers.get(i).getJsonObject());
+ if (i+1 < 15) { // data14 is max
+ numberValues.put(dataKeyPrefix + (i + 1), mPhoneNumbers.get(i).number);
+ }
+ }
+ rawContact.addDataItemValues(numberValues);
+ contact.put(Phone.CONTENT_ITEM_TYPE, phoneNumbers);
+
+ final ContentValues addressValues = new ContentValues();
+ addressValues.put(ContactsContract.Data._ID, -1);
+ addressValues.put(ContactsContract.Data.CONTACT_ID, -1);
+ addressValues.put(ContactsContract.Data.MIMETYPE, StructuredPostal.CONTENT_ITEM_TYPE);
+
+ // Insert addresses if there are any
+ if (mAddresses.size() > 0) {
+ JSONArray addresses = new JSONArray();
+ for (int i = 0; i < mAddresses.size(); i++) {
+ addresses.put(mAddresses.get(i).getJsonObject());
+ if (i+1 < 15) { // data14 is max
+ addressValues.put(dataKeyPrefix + (i + 1), mAddresses.get(i).formattedAddress);
+ }
+ }
+ contact.put(StructuredPostal.CONTENT_ITEM_TYPE, addresses);
+ rawContact.addDataItemValues(addressValues);
+ }
+
+ // Insert websites if there are any
+ if (mWebsites.size() > 0) {
+ JSONArray websites = new JSONArray();
+ for (int i = 0; i < mWebsites.size(); i++) {
+ websites.put(mWebsites.get(i).getJsonObject());
+ }
+ contact.put(Website.CONTENT_ITEM_TYPE, websites);
+ }
+
+ // add spam count and attribution
+ contact.put(CallerMetaData.SPAM_COUNT, getSpamCount());
+ contact.put(CallerMetaData.INFO_PROVIDER, getInfoProviderName());
+ contact.put(CallerMetaData.SUCCINCT_LOCATION, getSuccinctLocation());
+ contact.put(CallerMetaData.PHOTO_URL, getPhotoUrl());
+ mPhotoUrl = getPhotoUrl();
+
+ String json = new JSONObject()
+ .put(Contacts.DISPLAY_NAME, mName.displayName)
+ .put(Contacts.DISPLAY_NAME_SOURCE, mDisplayNameSource)
+ .put(Directory.EXPORT_SUPPORT,
+ Directory.EXPORT_SUPPORT_ANY_ACCOUNT)
+ .put(Contacts.CONTENT_ITEM_TYPE, contact)
+ .toString();
+
+ if (json != null) {
+ long directoryId = -1;
+ switch (mDirectoryType) {
+ case FORWARD_LOOKUP:
+ directoryId = DirectoryId.NEARBY;
+ break;
+ case PEOPLE_LOOKUP:
+ directoryId = DirectoryId.PEOPLE;
+ break;
+ case REVERSE_LOOKUP:
+ // use null directory to be backwards compatible with old code
+ directoryId = DirectoryId.NULL;
+ break;
+ }
+
+ lookupUri = Contacts.CONTENT_LOOKUP_URI
+ .buildUpon()
+ .appendPath(Constants.LOOKUP_URI_ENCODED)
+ .appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
+ String.valueOf(directoryId))
+ .encodedFragment(json)
+ .build();
+ }
+
+ final Contact contactToReturn = new Contact(
+ lookupUri, lookupUri,
+ lookupUri,
+ mDirectoryId,
+ null /* lookupKey */,
+ -1 /* id */,
+ -1 /* nameRawContactId */,
+ mDisplayNameSource,
+ 0 /* photoId */,
+ mPhotoUrl,
+ mName.displayName,
+ mName.givenName,
+ mName.phoneticGivenName /* phoneticName */,
+ false /* starred */,
+ null /* presence */,
+ false /* sendToVoicemail */,
+ null /* customRingtone */,
+ false /* isUserProfile */);
+
+ contactToReturn.setStatuses(new ImmutableMap.Builder<Long, DataStatus>().build());
+ final String accountName = contact.optString(ContactsContract.RawContacts.ACCOUNT_NAME,
+ null);
+ final String directoryName = lookupUri.getQueryParameter(Directory.DISPLAY_NAME);
+ if (accountName != null) {
+ final String accountType =
+ contact.getString(ContactsContract.RawContacts.ACCOUNT_TYPE);
+ contactToReturn.setDirectoryMetaData(directoryName, null, accountName, accountType,
+ contact.optInt(Directory.EXPORT_SUPPORT,
+ Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY));
+ } else {
+ contactToReturn.setDirectoryMetaData(directoryName, null, null, null,
+ contact.optInt(Directory.EXPORT_SUPPORT,
+ Directory.EXPORT_SUPPORT_ANY_ACCOUNT));
+ }
+
+ final ContentValues nameValues = new ContentValues();
+ nameValues.put(ContactsContract.Data._ID, -1);
+ nameValues.put(ContactsContract.Data.CONTACT_ID, -1);
+ nameValues.put(ContactsContract.Data.DATA1, mName.displayName);
+ nameValues.put(ContactsContract.Data.MIMETYPE, StructuredName.CONTENT_ITEM_TYPE);
+ rawContact.addDataItemValues(nameValues);
+
+ contactToReturn.setRawContacts(new ImmutableList.Builder<RawContact>()
+ .add(rawContact)
+ .build());
+
+ contactToReturn.setSpamCount(getSpamCount());
+ contactToReturn.setProviderName(getInfoProviderName());
+
+ return contactToReturn;
+ } catch (JSONException e) {
+ Log.e(TAG, "Failed to build contactToReturn", e);
+ return null;
+ }
+ }
+
+ // android.provider.ContactsContract.CommonDataKinds.StructuredPostal
+ public static class Address {
+ public String formattedAddress;
+ public int type;
+ public String label;
+ public String street;
+ public String poBox;
+ public String neighborhood;
+ public String city;
+ public String region;
+ public String postCode;
+ public String country;
+
+ public static Address createFormattedHome(String address) {
+ Address a = new Address();
+ a.formattedAddress = address;
+ a.type = StructuredPostal.TYPE_HOME;
+ return a;
+ }
+
+ public JSONObject getJsonObject() throws JSONException {
+ JSONObject json = new JSONObject();
+ json.putOpt(StructuredPostal.FORMATTED_ADDRESS,
+ formattedAddress);
+ json.put(StructuredPostal.TYPE, type);
+ json.putOpt(StructuredPostal.LABEL, label);
+ json.putOpt(StructuredPostal.STREET, street);
+ json.putOpt(StructuredPostal.POBOX, poBox);
+ json.putOpt(StructuredPostal.NEIGHBORHOOD, neighborhood);
+ json.putOpt(StructuredPostal.CITY, city);
+ json.putOpt(StructuredPostal.REGION, region);
+ json.putOpt(StructuredPostal.POSTCODE, postCode);
+ json.putOpt(StructuredPostal.COUNTRY, country);
+ return json;
+ }
+
+ public Address() {}
+
+ public Address(JSONObject json) throws JSONException {
+ if (json.has(StructuredPostal.FORMATTED_ADDRESS)) {
+ formattedAddress = json.getString(StructuredPostal.FORMATTED_ADDRESS);
+ }
+ }
+
+ public String toString() {
+ return "formattedAddress: " + formattedAddress + "; " +
+ "type: " + type + "; " +
+ "label: " + label + "; " +
+ "street: " + street + "; " +
+ "poBox: " + poBox + "; " +
+ "neighborhood: " + neighborhood + "; " +
+ "city: " + city + "; " +
+ "region: " + region + "; " +
+ "postCode: " + postCode + "; " +
+ "country: " + country;
+ }
+ }
+
+ // android.provider.ContactsContract.CommonDataKinds.StructuredName
+ public static class Name {
+ public String displayName;
+ public String givenName;
+ public String familyName;
+ public String prefix;
+ public String middleName;
+ public String suffix;
+ public String phoneticGivenName;
+ public String phoneticMiddleName;
+ public String phoneticFamilyName;
+
+ public static Name createDisplayName(String displayName) {
+ Name name = new Name();
+ name.displayName = displayName;
+ return name;
+ }
+
+ public JSONObject getJsonObject() throws JSONException {
+ JSONObject json = new JSONObject();
+ json.putOpt(StructuredName.DISPLAY_NAME, displayName);
+ json.putOpt(StructuredName.GIVEN_NAME, givenName);
+ json.putOpt(StructuredName.FAMILY_NAME, familyName);
+ json.putOpt(StructuredName.PREFIX, prefix);
+ json.putOpt(StructuredName.MIDDLE_NAME, middleName);
+ json.putOpt(StructuredName.SUFFIX, suffix);
+ json.putOpt(StructuredName.PHONETIC_GIVEN_NAME,
+ phoneticGivenName);
+ json.putOpt(StructuredName.PHONETIC_MIDDLE_NAME,
+ phoneticMiddleName);
+ json.putOpt(StructuredName.PHONETIC_FAMILY_NAME,
+ phoneticFamilyName);
+ return json;
+ }
+
+ public Name(JSONObject json) throws JSONException {
+ if (json != null) {
+ displayName = json.optString(StructuredName.DISPLAY_NAME, null);
+ }
+ }
+
+ public Name() {}
+
+ public String toString() {
+ return "displayName: " + displayName + "; " +
+ "givenName: " + givenName + "; " +
+ "familyName: " + familyName + "; " +
+ "prefix: " + prefix + "; " +
+ "middleName: " + middleName + "; " +
+ "suffix: " + suffix + "; " +
+ "phoneticGivenName: " + phoneticGivenName + "; " +
+ "phoneticMiddleName: " + phoneticMiddleName + "; " +
+ "phoneticFamilyName: " + phoneticFamilyName;
+ }
+ }
+
+ // android.provider.ContactsContract.CommonDataKinds.Phone
+ public static class PhoneNumber {
+ public String number;
+ public int type;
+ public String label;
+
+ public static PhoneNumber createMainNumber(String number) {
+ PhoneNumber n = new PhoneNumber();
+ n.number = number;
+ n.type = Phone.TYPE_MAIN;
+ return n;
+ }
+
+ public JSONObject getJsonObject() throws JSONException {
+ JSONObject json = new JSONObject();
+ json.put(Phone.NUMBER, number);
+ json.put(Phone.TYPE, type);
+ json.putOpt(Phone.LABEL, label);
+ return json;
+ }
+
+ public PhoneNumber(JSONObject json) throws JSONException {
+ number = json.getString(Phone.NUMBER);
+ type = json.getInt(Phone.TYPE);
+ if (json.has(Phone.LABEL)) {
+ label = json.getString(Phone.LABEL);
+ }
+ }
+
+ public PhoneNumber() {}
+
+ public String toString() {
+ return "number: " + number + "; " +
+ "type: " + type + "; " +
+ "label: " + label;
+ }
+ }
+
+ // android.provider.ContactsContract.CommonDataKinds.Website
+ public static class WebsiteUrl {
+ public String url;
+ public int type;
+ public String label;
+
+ public static WebsiteUrl createProfile(String url) {
+ WebsiteUrl u = new WebsiteUrl();
+ u.url = url;
+ u.type = Website.TYPE_PROFILE;
+ return u;
+ }
+
+ public JSONObject getJsonObject() throws JSONException {
+ JSONObject json = new JSONObject();
+ json.put(Website.URL, url);
+ json.put(Website.TYPE, type);
+ json.putOpt(Website.LABEL, label);
+ return json;
+ }
+
+ public WebsiteUrl() {}
+
+ public WebsiteUrl(JSONObject json) throws JSONException {
+ if (json.has(Website.URL)) {
+ url = json.getString(Website.URL);
+ }
+ if (json.has(Website.TYPE)) {
+ type = json.getInt(Website.TYPE);
+ }
+ if (json.has(Website.LABEL)) {
+ label = json.getString(Website.LABEL);
+ }
+ }
+
+ public String toString() {
+ return "url: " + url + "; " +
+ "type: " + type + "; " +
+ "label: " + label;
+ }
+ }
+}
diff --git a/src/com/android/contacts/common/model/ContactLoader.java b/src/com/android/contacts/common/model/ContactLoader.java
index 59ab292e..1078e836 100644
--- a/src/com/android/contacts/common/model/ContactLoader.java
+++ b/src/com/android/contacts/common/model/ContactLoader.java
@@ -42,13 +42,15 @@ import com.android.contacts.common.GeoUtil;
import com.android.contacts.common.GroupMetaData;
import com.android.contacts.common.model.account.AccountType;
import com.android.contacts.common.model.account.AccountTypeWithDataSet;
+
+import com.android.contacts.common.model.dataitem.DataItem;
+import com.android.contacts.common.model.dataitem.PhoneDataItem;
+import com.android.contacts.common.model.dataitem.PhotoDataItem;
import com.android.contacts.common.util.Constants;
import com.android.contacts.common.util.ContactLoaderUtils;
import com.android.contacts.common.util.DataStatus;
import com.android.contacts.common.util.UriUtils;
-import com.android.contacts.common.model.dataitem.DataItem;
-import com.android.contacts.common.model.dataitem.PhoneDataItem;
-import com.android.contacts.common.model.dataitem.PhotoDataItem;
+
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Maps;
@@ -92,6 +94,14 @@ public class ContactLoader extends AsyncTaskLoader<Contact> {
private ForceLoadContentObserver mObserver;
private final Set<Long> mNotifiedRawContactIds = Sets.newHashSet();
+ /**
+ * If loading from an encoded contact entity, there are two
+ * possible schemas that can be used.
+ */
+ public enum EncodedContactEntitySchemaVersion {
+ ORIGINAL, ENHANCED_CALLER_META_DATA
+ }
+
public ContactLoader(Context context, Uri lookupUri, boolean postViewNotification) {
this(context, lookupUri, false, false, postViewNotification, false);
}
@@ -318,7 +328,8 @@ public class ContactLoader extends AsyncTaskLoader<Contact> {
resultIsCached = true;
} else {
if (uriCurrentFormat.getLastPathSegment().equals(Constants.LOOKUP_URI_ENCODED)) {
- result = loadEncodedContactEntity(uriCurrentFormat, mLookupUri);
+ result = loadEncodedContactEntity(uriCurrentFormat,
+ mLookupUri, EncodedContactEntitySchemaVersion.ORIGINAL);
} else {
result = loadContactEntity(resolver, uriCurrentFormat);
}
@@ -360,84 +371,104 @@ public class ContactLoader extends AsyncTaskLoader<Contact> {
*/
public static Contact parseEncodedContactEntity(Uri lookupUri) {
try {
- return loadEncodedContactEntity(lookupUri, lookupUri);
+ return loadEncodedContactEntity(lookupUri, lookupUri, null);
} catch (JSONException je) {
return null;
}
}
- private static Contact loadEncodedContactEntity(Uri uri, Uri lookupUri) throws JSONException {
- final String jsonString = uri.getEncodedFragment();
- final JSONObject json = new JSONObject(jsonString);
-
- final long directoryId =
- Long.valueOf(uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY));
-
- final String displayName = json.optString(Contacts.DISPLAY_NAME);
- final String altDisplayName = json.optString(
- Contacts.DISPLAY_NAME_ALTERNATIVE, displayName);
- final int displayNameSource = json.getInt(Contacts.DISPLAY_NAME_SOURCE);
- final String photoUri = json.optString(Contacts.PHOTO_URI, null);
- final Contact contact = new Contact(
- uri, uri,
- lookupUri,
- directoryId,
- null /* lookupKey */,
- -1 /* id */,
- -1 /* nameRawContactId */,
- displayNameSource,
- 0 /* photoId */,
- photoUri,
- displayName,
- altDisplayName,
- null /* phoneticName */,
- false /* starred */,
- null /* presence */,
- false /* sendToVoicemail */,
- null /* customRingtone */,
- false /* isUserProfile */);
-
- contact.setStatuses(new ImmutableMap.Builder<Long, DataStatus>().build());
-
- final String accountName = json.optString(RawContacts.ACCOUNT_NAME, null);
- final String directoryName = uri.getQueryParameter(Directory.DISPLAY_NAME);
- if (accountName != null) {
- final String accountType = json.getString(RawContacts.ACCOUNT_TYPE);
- contact.setDirectoryMetaData(directoryName, null, accountName, accountType,
- json.optInt(Directory.EXPORT_SUPPORT,
- Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY));
- } else {
- contact.setDirectoryMetaData(directoryName, null, null, null,
- json.optInt(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_ANY_ACCOUNT));
+
+ public static Contact parseEncodedContactEntity(Uri lookupUri,
+ EncodedContactEntitySchemaVersion schemaVersion) {
+ try {
+ return loadEncodedContactEntity(lookupUri, lookupUri, schemaVersion);
+ } catch (JSONException je) {
+ return null;
}
+ }
- final ContentValues values = new ContentValues();
- values.put(Data._ID, -1);
- values.put(Data.CONTACT_ID, -1);
- final RawContact rawContact = new RawContact(values);
-
- final JSONObject items = json.getJSONObject(Contacts.CONTENT_ITEM_TYPE);
- final Iterator keys = items.keys();
- while (keys.hasNext()) {
- final String mimetype = (String) keys.next();
-
- // Could be single object or array.
- final JSONObject obj = items.optJSONObject(mimetype);
- if (obj == null) {
- final JSONArray array = items.getJSONArray(mimetype);
- for (int i = 0; i < array.length(); i++) {
- final JSONObject item = array.getJSONObject(i);
- processOneRecord(rawContact, item, mimetype);
- }
+ private static Contact loadEncodedContactEntity(Uri uri,
+ Uri lookupUri, EncodedContactEntitySchemaVersion schemaVersion) throws JSONException {
+ if (schemaVersion == EncodedContactEntitySchemaVersion.ENHANCED_CALLER_META_DATA) {
+ return new ContactBuilder(uri).build();
+ } else {
+ final String jsonString = uri.getEncodedFragment();
+ final JSONObject json = new JSONObject(jsonString);
+
+ final long directoryId =
+ Long.valueOf(uri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY));
+
+ final String displayName = json.optString(Contacts.DISPLAY_NAME);
+ final String altDisplayName = json.optString(
+ Contacts.DISPLAY_NAME_ALTERNATIVE, displayName);
+ final int displayNameSource = json.getInt(Contacts.DISPLAY_NAME_SOURCE);
+ final String photoUri = json.optString(Contacts.PHOTO_URI, null);
+ final Contact contact = new Contact(
+ uri, uri,
+ lookupUri,
+ directoryId,
+ null /* lookupKey */,
+ -1 /* id */,
+ -1 /* nameRawContactId */,
+ displayNameSource,
+ 0 /* photoId */,
+ photoUri,
+ displayName,
+ altDisplayName,
+ null /* phoneticName */,
+ false /* starred */,
+ null /* presence */,
+ false /* sendToVoicemail */,
+ null /* customRingtone */,
+ false /* isUserProfile */);
+
+ contact.setStatuses(new ImmutableMap.Builder<Long, DataStatus>().build());
+
+ final String accountName = json.optString(RawContacts.ACCOUNT_NAME, null);
+ final String directoryName = uri.getQueryParameter(Directory.DISPLAY_NAME);
+ if (accountName != null) {
+ final String accountType = json.getString(RawContacts.ACCOUNT_TYPE);
+ contact.setDirectoryMetaData(directoryName, null, accountName, accountType,
+ json.optInt(Directory.EXPORT_SUPPORT,
+ Directory.EXPORT_SUPPORT_SAME_ACCOUNT_ONLY));
} else {
- processOneRecord(rawContact, obj, mimetype);
+ contact.setDirectoryMetaData(directoryName, null, null, null,
+ json.optInt(Directory.EXPORT_SUPPORT, Directory.EXPORT_SUPPORT_ANY_ACCOUNT));
}
- }
- contact.setRawContacts(new ImmutableList.Builder<RawContact>()
- .add(rawContact)
- .build());
- return contact;
+ final ContentValues values = new ContentValues();
+ values.put(Data._ID, -1);
+ values.put(Data.CONTACT_ID, -1);
+ final RawContact rawContact = new RawContact(values);
+
+ final JSONObject items = json.getJSONObject(Contacts.CONTENT_ITEM_TYPE);
+ final Iterator keys = items.keys();
+ while (keys.hasNext()) {
+ final String mimetype = (String) keys.next();
+
+ // Could be single object, int, or array.
+ JSONObject obj = items.optJSONObject(mimetype);
+ final int num = items.optInt(mimetype, -1);
+ if (obj == null && num != -1) {
+ final JSONArray array = items.getJSONArray(mimetype);
+ for (int i = 0; i < array.length(); i++) {
+ final JSONObject item = array.getJSONObject(i);
+ processOneRecord(rawContact, item, mimetype);
+ }
+ } else if (num != -1 && obj != null) {
+ obj = new JSONObject();
+ obj.put(mimetype, num);
+ processOneRecord(rawContact, obj, mimetype);
+ } else {
+ processOneRecord(rawContact, obj, mimetype);
+ }
+ }
+
+ contact.setRawContacts(new ImmutableList.Builder<RawContact>()
+ .add(rawContact)
+ .build());
+ return contact;
+ }
}
private static void processOneRecord(RawContact rawContact, JSONObject item, String mimetype)
diff --git a/src/com/android/contacts/common/model/DirectoryId.java b/src/com/android/contacts/common/model/DirectoryId.java
new file mode 100644
index 00000000..c705c925
--- /dev/null
+++ b/src/com/android/contacts/common/model/DirectoryId.java
@@ -0,0 +1,35 @@
+package com.android.contacts.common.model;
+
+import android.net.Uri;
+import android.provider.ContactsContract;
+
+public class DirectoryId {
+
+ // default contacts directory
+ public static final long DEFAULT = ContactsContract.Directory.DEFAULT;
+
+ // id for a non existant directory
+ public static final long NULL = Long.MAX_VALUE;
+
+ // id for nearby forward lookup results (not a real directory)
+ public static final long NEARBY = NULL - 1;
+
+ // id for people forward lookup results (not a real directory)
+ public static final long PEOPLE = NULL - 2;
+
+ public static boolean isFakeDirectory(long directory) {
+ return directory == NULL || directory == NEARBY || directory == PEOPLE;
+ }
+
+ public static long fromUri(Uri lookupUri) {
+ long directory = DirectoryId.DEFAULT;
+ if (lookupUri != null) {
+ String dqp =
+ lookupUri.getQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY);
+ if (dqp != null) {
+ directory = Long.valueOf(dqp);
+ }
+ }
+ return directory;
+ }
+}
diff --git a/src/com/android/contacts/common/util/CallerMetaData.java b/src/com/android/contacts/common/util/CallerMetaData.java
new file mode 100644
index 00000000..55b58480
--- /dev/null
+++ b/src/com/android/contacts/common/util/CallerMetaData.java
@@ -0,0 +1,17 @@
+package com.android.contacts.common.util;
+
+/**
+ * Common strings used in conjunction with a CallerInfoService
+ */
+public class CallerMetaData {
+
+ public static final String SPAM_COUNT = "CALLER_META_DATA_SPAM_COUNT";
+ // tag for storing concise location of a caller (eg: [<city>, <Country>])
+ public static final String SUCCINCT_LOCATION = "CALLER_META_DATA_SUCCINCT_LOCATION";
+ public static final String INFO_PROVIDER = "CALLER_META_DATA_INFO_PROVIDER";
+ public static final String PHOTO_URL = "CALLER_META_DATA_PHOTO_URL";
+
+ // mimetype for the name of service that helped identify the caller
+ public static final String MIMETYPE_SERVICE_IDENTIFIER =
+ "com.cyanogen/callerinfoservice/identifier";
+}