diff options
author | Matt Garnes <matt@cyngn.com> | 2015-06-09 14:27:31 -0700 |
---|---|---|
committer | Rohit Yengisetty <rohit@cyngn.com> | 2015-11-18 11:08:28 -0800 |
commit | 4ecdcb080a45c6e174b4adf69fc5a44922426b4a (patch) | |
tree | 12c950b27cacd6918693bf20e30121977fcc220e | |
parent | b474ca6dbda4401d6dfb484802984cd76a3c20e6 (diff) | |
download | android_packages_apps_ContactsCommon-4ecdcb080a45c6e174b4adf69fc5a44922426b4a.tar.gz android_packages_apps_ContactsCommon-4ecdcb080a45c6e174b4adf69fc5a44922426b4a.tar.bz2 android_packages_apps_ContactsCommon-4ecdcb080a45c6e174b4adf69fc5a44922426b4a.zip |
ContactsCommon: Add ability to show looked up contacts for quick contacts
MMS-86
Change-Id: I7f10857422908106e2d6cba539acfa3b757568f3
-rw-r--r-- | Android.mk | 11 | ||||
-rw-r--r-- | info_lookup/src/com/cyanogen/lookup/phonenumber/response/LookupResponse.java | 4 | ||||
-rw-r--r-- | libs/picaso.jar | bin | 0 -> 120459 bytes | |||
-rw-r--r-- | proguard.flags | 4 | ||||
-rw-r--r-- | src/com/android/contacts/common/model/Contact.java | 19 | ||||
-rw-r--r-- | src/com/android/contacts/common/model/ContactBuilder.java | 638 | ||||
-rw-r--r-- | src/com/android/contacts/common/model/ContactLoader.java | 175 | ||||
-rw-r--r-- | src/com/android/contacts/common/model/DirectoryId.java | 35 | ||||
-rw-r--r-- | src/com/android/contacts/common/util/CallerMetaData.java | 17 |
9 files changed, 829 insertions, 74 deletions
@@ -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 Binary files differnew file mode 100644 index 00000000..6acbaa14 --- /dev/null +++ b/libs/picaso.jar 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"; +} |