diff options
Diffstat (limited to 'InCallUI/src/com/android/incallui/ContactInfoCache.java')
-rw-r--r-- | InCallUI/src/com/android/incallui/ContactInfoCache.java | 699 |
1 files changed, 0 insertions, 699 deletions
diff --git a/InCallUI/src/com/android/incallui/ContactInfoCache.java b/InCallUI/src/com/android/incallui/ContactInfoCache.java deleted file mode 100644 index 9d6fc4627..000000000 --- a/InCallUI/src/com/android/incallui/ContactInfoCache.java +++ /dev/null @@ -1,699 +0,0 @@ -/* - * Copyright (C) 2013 The Android Open Source Project - * - * 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.incallui; - -import com.google.common.base.MoreObjects; -import com.google.common.base.Preconditions; -import com.google.common.collect.Maps; -import com.google.common.collect.Sets; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.drawable.BitmapDrawable; -import android.graphics.drawable.Drawable; -import android.location.Address; -import android.media.RingtoneManager; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Looper; -import android.provider.ContactsContract; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.Contacts; -import android.provider.ContactsContract.DisplayNameSources; -import android.telecom.TelecomManager; -import android.text.TextUtils; -import android.util.Pair; - -import com.android.contacts.common.ContactsUtils; -import com.android.contacts.common.util.PhoneNumberHelper; -import com.android.dialer.R; -import com.android.dialer.calllog.ContactInfo; -import com.android.dialer.service.CachedNumberLookupService; -import com.android.dialer.service.CachedNumberLookupService.CachedContactInfo; -import com.android.dialer.util.MoreStrings; -import com.android.incallui.Call.LogState; -import com.android.incallui.service.PhoneNumberService; -import com.android.incalluibind.ObjectFactory; - -import org.json.JSONException; -import org.json.JSONObject; - -import java.util.Calendar; -import java.util.HashMap; -import java.util.List; -import java.util.Set; - -/** - * Class responsible for querying Contact Information for Call objects. Can perform asynchronous - * requests to the Contact Provider for information as well as respond synchronously for any data - * that it currently has cached from previous queries. This class always gets called from the UI - * thread so it does not need thread protection. - */ -public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadCompleteListener { - - private static final String TAG = ContactInfoCache.class.getSimpleName(); - private static final int TOKEN_UPDATE_PHOTO_FOR_CALL_STATE = 0; - - private final Context mContext; - private final PhoneNumberService mPhoneNumberService; - private final CachedNumberLookupService mCachedNumberLookupService; - private final HashMap<String, ContactCacheEntry> mInfoMap = Maps.newHashMap(); - private final HashMap<String, Set<ContactInfoCacheCallback>> mCallBacks = Maps.newHashMap(); - - private static ContactInfoCache sCache = null; - - private Drawable mDefaultContactPhotoDrawable; - private Drawable mConferencePhotoDrawable; - private ContactUtils mContactUtils; - - public static synchronized ContactInfoCache getInstance(Context mContext) { - if (sCache == null) { - sCache = new ContactInfoCache(mContext.getApplicationContext()); - } - return sCache; - } - - private ContactInfoCache(Context context) { - mContext = context; - mPhoneNumberService = ObjectFactory.newPhoneNumberService(context); - mCachedNumberLookupService = - com.android.dialerbind.ObjectFactory.newCachedNumberLookupService(); - mContactUtils = ObjectFactory.getContactUtilsInstance(context); - - } - - public ContactCacheEntry getInfo(String callId) { - return mInfoMap.get(callId); - } - - public static ContactCacheEntry buildCacheEntryFromCall(Context context, Call call, - boolean isIncoming) { - final ContactCacheEntry entry = new ContactCacheEntry(); - - // TODO: get rid of caller info. - final CallerInfo info = CallerInfoUtils.buildCallerInfo(context, call); - ContactInfoCache.populateCacheEntry(context, info, entry, call.getNumberPresentation(), - isIncoming); - return entry; - } - - public void maybeInsertCnapInformationIntoCache(Context context, final Call call, - final CallerInfo info) { - if (mCachedNumberLookupService == null || TextUtils.isEmpty(info.cnapName) - || mInfoMap.get(call.getId()) != null) { - return; - } - final Context applicationContext = context.getApplicationContext(); - Log.i(TAG, "Found contact with CNAP name - inserting into cache"); - new AsyncTask<Void, Void, Void>() { - @Override - protected Void doInBackground(Void... params) { - ContactInfo contactInfo = new ContactInfo(); - CachedContactInfo cacheInfo = mCachedNumberLookupService.buildCachedContactInfo( - contactInfo); - cacheInfo.setSource(CachedContactInfo.SOURCE_TYPE_CNAP, "CNAP", 0); - contactInfo.name = info.cnapName; - contactInfo.number = call.getNumber(); - contactInfo.type = ContactsContract.CommonDataKinds.Phone.TYPE_MAIN; - try { - final JSONObject contactRows = new JSONObject().put(Phone.CONTENT_ITEM_TYPE, - new JSONObject() - .put(Phone.NUMBER, contactInfo.number) - .put(Phone.TYPE, Phone.TYPE_MAIN)); - final String jsonString = new JSONObject() - .put(Contacts.DISPLAY_NAME, contactInfo.name) - .put(Contacts.DISPLAY_NAME_SOURCE, DisplayNameSources.STRUCTURED_NAME) - .put(Contacts.CONTENT_ITEM_TYPE, contactRows).toString(); - cacheInfo.setLookupKey(jsonString); - } catch (JSONException e) { - Log.w(TAG, "Creation of lookup key failed when caching CNAP information"); - } - mCachedNumberLookupService.addContact(applicationContext, cacheInfo); - return null; - } - }.execute(); - } - - private class FindInfoCallback implements CallerInfoAsyncQuery.OnQueryCompleteListener { - private final boolean mIsIncoming; - - public FindInfoCallback(boolean isIncoming) { - mIsIncoming = isIncoming; - } - - @Override - public void onQueryComplete(int token, Object cookie, CallerInfo callerInfo) { - findInfoQueryComplete((Call) cookie, callerInfo, mIsIncoming, true); - } - } - - /** - * Requests contact data for the Call object passed in. - * Returns the data through callback. If callback is null, no response is made, however the - * query is still performed and cached. - * - * @param callback The function to call back when the call is found. Can be null. - */ - public void findInfo(final Call call, final boolean isIncoming, - ContactInfoCacheCallback callback) { - Preconditions.checkState(Looper.getMainLooper().getThread() == Thread.currentThread()); - Preconditions.checkNotNull(callback); - - final String callId = call.getId(); - final ContactCacheEntry cacheEntry = mInfoMap.get(callId); - Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId); - - // If we have a previously obtained intermediate result return that now - if (cacheEntry != null) { - Log.d(TAG, "Contact lookup. In memory cache hit; lookup " - + (callBacks == null ? "complete" : "still running")); - callback.onContactInfoComplete(callId, cacheEntry); - // If no other callbacks are in flight, we're done. - if (callBacks == null) { - return; - } - } - - // If the entry already exists, add callback - if (callBacks != null) { - callBacks.add(callback); - return; - } - Log.d(TAG, "Contact lookup. In memory cache miss; searching provider."); - // New lookup - callBacks = Sets.newHashSet(); - callBacks.add(callback); - mCallBacks.put(callId, callBacks); - - /** - * Performs a query for caller information. - * Save any immediate data we get from the query. An asynchronous query may also be made - * for any data that we do not already have. Some queries, such as those for voicemail and - * emergency call information, will not perform an additional asynchronous query. - */ - final CallerInfo callerInfo = CallerInfoUtils.getCallerInfoForCall( - mContext, call, new FindInfoCallback(isIncoming)); - - findInfoQueryComplete(call, callerInfo, isIncoming, false); - } - - private void findInfoQueryComplete(Call call, CallerInfo callerInfo, boolean isIncoming, - boolean didLocalLookup) { - final String callId = call.getId(); - int presentationMode = call.getNumberPresentation(); - if (callerInfo.contactExists || callerInfo.isEmergencyNumber() || - callerInfo.isVoiceMailNumber()) { - presentationMode = TelecomManager.PRESENTATION_ALLOWED; - } - - ContactCacheEntry cacheEntry = mInfoMap.get(callId); - // Ensure we always have a cacheEntry. Replace the existing entry if - // it has no name or if we found a local contact. - if (cacheEntry == null || TextUtils.isEmpty(cacheEntry.namePrimary) || - callerInfo.contactExists) { - cacheEntry = buildEntry(mContext, callId, callerInfo, presentationMode, isIncoming); - mInfoMap.put(callId, cacheEntry); - } - - sendInfoNotifications(callId, cacheEntry); - - if (didLocalLookup) { - // Before issuing a request for more data from other services, we only check that the - // contact wasn't found in the local DB. We don't check the if the cache entry already - // has a name because we allow overriding cnap data with data from other services. - if (!callerInfo.contactExists && mPhoneNumberService != null) { - Log.d(TAG, "Contact lookup. Local contacts miss, checking remote"); - final PhoneNumberServiceListener listener = new PhoneNumberServiceListener(callId); - mPhoneNumberService.getPhoneNumberInfo(cacheEntry.number, listener, listener, - isIncoming); - } else if (cacheEntry.displayPhotoUri != null) { - Log.d(TAG, "Contact lookup. Local contact found, starting image load"); - // Load the image with a callback to update the image state. - // When the load is finished, onImageLoadComplete() will be called. - cacheEntry.isLoadingPhoto = true; - ContactsAsyncHelper.startObtainPhotoAsync(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE, - mContext, cacheEntry.displayPhotoUri, ContactInfoCache.this, callId); - } else { - if (callerInfo.contactExists) { - Log.d(TAG, "Contact lookup done. Local contact found, no image."); - } else { - Log.d(TAG, "Contact lookup done. Local contact not found and" - + " no remote lookup service available."); - } - clearCallbacks(callId); - } - } - } - - class PhoneNumberServiceListener implements PhoneNumberService.NumberLookupListener, - PhoneNumberService.ImageLookupListener, ContactUtils.Listener { - private final String mCallId; - - PhoneNumberServiceListener(String callId) { - mCallId = callId; - } - - @Override - public void onPhoneNumberInfoComplete( - final PhoneNumberService.PhoneNumberInfo info) { - // If we got a miss, this is the end of the lookup pipeline, - // so clear the callbacks and return. - if (info == null) { - Log.d(TAG, "Contact lookup done. Remote contact not found."); - clearCallbacks(mCallId); - return; - } - - ContactCacheEntry entry = new ContactCacheEntry(); - entry.namePrimary = info.getDisplayName(); - entry.number = info.getNumber(); - entry.contactLookupResult = info.getLookupSource(); - final int type = info.getPhoneType(); - final String label = info.getPhoneLabel(); - if (type == Phone.TYPE_CUSTOM) { - entry.label = label; - } else { - final CharSequence typeStr = Phone.getTypeLabel( - mContext.getResources(), type, label); - entry.label = typeStr == null ? null : typeStr.toString(); - } - final ContactCacheEntry oldEntry = mInfoMap.get(mCallId); - if (oldEntry != null) { - // Location is only obtained from local lookup so persist - // the value for remote lookups. Once we have a name this - // field is no longer used; it is persisted here in case - // the UI is ever changed to use it. - entry.location = oldEntry.location; - // Contact specific ringtone is obtained from local lookup. - entry.contactRingtoneUri = oldEntry.contactRingtoneUri; - } - - // If no image and it's a business, switch to using the default business avatar. - if (info.getImageUrl() == null && info.isBusiness()) { - Log.d(TAG, "Business has no image. Using default."); - entry.photo = mContext.getResources().getDrawable(R.drawable.img_business); - } - - mInfoMap.put(mCallId, entry); - sendInfoNotifications(mCallId, entry); - - if (mContactUtils != null) { - // This method will callback "onContactInteractionsFound". - entry.isLoadingContactInteractions = - mContactUtils.retrieveContactInteractionsFromLookupKey( - info.getLookupKey(), this); - } - - entry.isLoadingPhoto = info.getImageUrl() != null; - - // If there is no image or contact interactions then we should not expect another - // callback. - if (!entry.isLoadingPhoto && !entry.isLoadingContactInteractions) { - // We're done, so clear callbacks - clearCallbacks(mCallId); - } - } - - @Override - public void onImageFetchComplete(Bitmap bitmap) { - onImageLoadComplete(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE, null, bitmap, mCallId); - } - - @Override - public void onContactInteractionsFound(Address address, - List<Pair<Calendar, Calendar>> openingHours) { - final ContactCacheEntry entry = mInfoMap.get(mCallId); - if (entry == null) { - Log.e(this, "Contact context received for empty search entry."); - clearCallbacks(mCallId); - return; - } - - entry.isLoadingContactInteractions = false; - - Log.v(ContactInfoCache.this, "Setting contact interactions for entry: ", entry); - - entry.locationAddress = address; - entry.openingHours = openingHours; - sendContactInteractionsNotifications(mCallId, entry); - - if (!entry.isLoadingPhoto) { - clearCallbacks(mCallId); - } - } - } - - /** - * Implemented for ContactsAsyncHelper.OnImageLoadCompleteListener interface. - * make sure that the call state is reflected after the image is loaded. - */ - @Override - public void onImageLoadComplete(int token, Drawable photo, Bitmap photoIcon, Object cookie) { - Log.d(this, "Image load complete with context: ", mContext); - // TODO: may be nice to update the image view again once the newer one - // is available on contacts database. - - final String callId = (String) cookie; - final ContactCacheEntry entry = mInfoMap.get(callId); - - if (entry == null) { - Log.e(this, "Image Load received for empty search entry."); - clearCallbacks(callId); - return; - } - - entry.isLoadingPhoto = false; - - Log.d(this, "setting photo for entry: ", entry); - - // Conference call icons are being handled in CallCardPresenter. - if (photo != null) { - Log.v(this, "direct drawable: ", photo); - entry.photo = photo; - } else if (photoIcon != null) { - Log.v(this, "photo icon: ", photoIcon); - entry.photo = new BitmapDrawable(mContext.getResources(), photoIcon); - } else { - Log.v(this, "unknown photo"); - entry.photo = null; - } - - sendImageNotifications(callId, entry); - - if (!entry.isLoadingContactInteractions) { - clearCallbacks(callId); - } - } - - /** - * Blows away the stored cache values. - */ - public void clearCache() { - mInfoMap.clear(); - mCallBacks.clear(); - } - - private ContactCacheEntry buildEntry(Context context, String callId, - CallerInfo info, int presentation, boolean isIncoming) { - // The actual strings we're going to display onscreen: - Drawable photo = null; - - final ContactCacheEntry cce = new ContactCacheEntry(); - populateCacheEntry(context, info, cce, presentation, isIncoming); - - // This will only be true for emergency numbers - if (info.photoResource != 0) { - photo = context.getResources().getDrawable(info.photoResource); - } else if (info.isCachedPhotoCurrent) { - if (info.cachedPhoto != null) { - photo = info.cachedPhoto; - } else { - photo = getDefaultContactPhotoDrawable(); - } - } else if (info.contactDisplayPhotoUri == null) { - photo = getDefaultContactPhotoDrawable(); - } else { - cce.displayPhotoUri = info.contactDisplayPhotoUri; - } - - // Support any contact id in N because QuickContacts in N starts supporting enterprise - // contact id - if (info.lookupKeyOrNull != null - && (ContactsUtils.FLAG_N_FEATURE || info.contactIdOrZero != 0)) { - cce.lookupUri = Contacts.getLookupUri(info.contactIdOrZero, info.lookupKeyOrNull); - } else { - Log.v(TAG, "lookup key is null or contact ID is 0 on M. Don't create a lookup uri."); - cce.lookupUri = null; - } - - cce.photo = photo; - cce.lookupKey = info.lookupKeyOrNull; - cce.contactRingtoneUri = info.contactRingtoneUri; - if (cce.contactRingtoneUri == null || cce.contactRingtoneUri == Uri.EMPTY) { - cce.contactRingtoneUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_RINGTONE); - } - - return cce; - } - - /** - * Populate a cache entry from a call (which got converted into a caller info). - */ - public static void populateCacheEntry(Context context, CallerInfo info, ContactCacheEntry cce, - int presentation, boolean isIncoming) { - Preconditions.checkNotNull(info); - String displayName = null; - String displayNumber = null; - String displayLocation = null; - String label = null; - boolean isSipCall = false; - - // It appears that there is a small change in behaviour with the - // PhoneUtils' startGetCallerInfo whereby if we query with an - // empty number, we will get a valid CallerInfo object, but with - // fields that are all null, and the isTemporary boolean input - // parameter as true. - - // In the past, we would see a NULL callerinfo object, but this - // ends up causing null pointer exceptions elsewhere down the - // line in other cases, so we need to make this fix instead. It - // appears that this was the ONLY call to PhoneUtils - // .getCallerInfo() that relied on a NULL CallerInfo to indicate - // an unknown contact. - - // Currently, infi.phoneNumber may actually be a SIP address, and - // if so, it might sometimes include the "sip:" prefix. That - // prefix isn't really useful to the user, though, so strip it off - // if present. (For any other URI scheme, though, leave the - // prefix alone.) - // TODO: It would be cleaner for CallerInfo to explicitly support - // SIP addresses instead of overloading the "phoneNumber" field. - // Then we could remove this hack, and instead ask the CallerInfo - // for a "user visible" form of the SIP address. - String number = info.phoneNumber; - - if (!TextUtils.isEmpty(number)) { - isSipCall = PhoneNumberHelper.isUriNumber(number); - if (number.startsWith("sip:")) { - number = number.substring(4); - } - } - - if (TextUtils.isEmpty(info.name)) { - // No valid "name" in the CallerInfo, so fall back to - // something else. - // (Typically, we promote the phone number up to the "name" slot - // onscreen, and possibly display a descriptive string in the - // "number" slot.) - if (TextUtils.isEmpty(number)) { - // No name *or* number! Display a generic "unknown" string - // (or potentially some other default based on the presentation.) - displayName = getPresentationString(context, presentation, info.callSubject); - Log.d(TAG, " ==> no name *or* number! displayName = " + displayName); - } else if (presentation != TelecomManager.PRESENTATION_ALLOWED) { - // This case should never happen since the network should never send a phone # - // AND a restricted presentation. However we leave it here in case of weird - // network behavior - displayName = getPresentationString(context, presentation, info.callSubject); - Log.d(TAG, " ==> presentation not allowed! displayName = " + displayName); - } else if (!TextUtils.isEmpty(info.cnapName)) { - // No name, but we do have a valid CNAP name, so use that. - displayName = info.cnapName; - info.name = info.cnapName; - displayNumber = number; - Log.d(TAG, " ==> cnapName available: displayName '" + displayName + - "', displayNumber '" + displayNumber + "'"); - } else { - // No name; all we have is a number. This is the typical - // case when an incoming call doesn't match any contact, - // or if you manually dial an outgoing number using the - // dialpad. - displayNumber = number; - - // Display a geographical description string if available - // (but only for incoming calls.) - if (isIncoming) { - // TODO (CallerInfoAsyncQuery cleanup): Fix the CallerInfo - // query to only do the geoDescription lookup in the first - // place for incoming calls. - displayLocation = info.geoDescription; // may be null - Log.d(TAG, "Geodescrption: " + info.geoDescription); - } - - Log.d(TAG, " ==> no name; falling back to number:" - + " displayNumber '" + Log.pii(displayNumber) - + "', displayLocation '" + displayLocation + "'"); - } - } else { - // We do have a valid "name" in the CallerInfo. Display that - // in the "name" slot, and the phone number in the "number" slot. - if (presentation != TelecomManager.PRESENTATION_ALLOWED) { - // This case should never happen since the network should never send a name - // AND a restricted presentation. However we leave it here in case of weird - // network behavior - displayName = getPresentationString(context, presentation, info.callSubject); - Log.d(TAG, " ==> valid name, but presentation not allowed!" + - " displayName = " + displayName); - } else { - // Causes cce.namePrimary to be set as info.name below. CallCardPresenter will - // later determine whether to use the name or nameAlternative when presenting - displayName = info.name; - cce.nameAlternative = info.nameAlternative; - displayNumber = number; - label = info.phoneLabel; - Log.d(TAG, " ==> name is present in CallerInfo: displayName '" + displayName - + "', displayNumber '" + displayNumber + "'"); - } - } - - cce.namePrimary = displayName; - cce.number = displayNumber; - cce.location = displayLocation; - cce.label = label; - cce.isSipCall = isSipCall; - cce.userType = info.userType; - - if (info.contactExists) { - cce.contactLookupResult = LogState.LOOKUP_LOCAL_CONTACT; - } - } - - /** - * Sends the updated information to call the callbacks for the entry. - */ - private void sendInfoNotifications(String callId, ContactCacheEntry entry) { - final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId); - if (callBacks != null) { - for (ContactInfoCacheCallback callBack : callBacks) { - callBack.onContactInfoComplete(callId, entry); - } - } - } - - private void sendImageNotifications(String callId, ContactCacheEntry entry) { - final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId); - if (callBacks != null && entry.photo != null) { - for (ContactInfoCacheCallback callBack : callBacks) { - callBack.onImageLoadComplete(callId, entry); - } - } - } - - private void sendContactInteractionsNotifications(String callId, ContactCacheEntry entry) { - final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId); - if (callBacks != null) { - for (ContactInfoCacheCallback callBack : callBacks) { - callBack.onContactInteractionsInfoComplete(callId, entry); - } - } - } - - private void clearCallbacks(String callId) { - mCallBacks.remove(callId); - } - - /** - * Gets name strings based on some special presentation modes and the associated custom label. - */ - private static String getPresentationString(Context context, int presentation, - String customLabel) { - String name = context.getString(R.string.unknown); - if (!TextUtils.isEmpty(customLabel) && - ((presentation == TelecomManager.PRESENTATION_UNKNOWN) || - (presentation == TelecomManager.PRESENTATION_RESTRICTED))) { - name = customLabel; - return name; - } else { - if (presentation == TelecomManager.PRESENTATION_RESTRICTED) { - name = context.getString(R.string.private_num); - } else if (presentation == TelecomManager.PRESENTATION_PAYPHONE) { - name = context.getString(R.string.payphone); - } - } - return name; - } - - public Drawable getDefaultContactPhotoDrawable() { - if (mDefaultContactPhotoDrawable == null) { - mDefaultContactPhotoDrawable = - mContext.getResources().getDrawable(R.drawable.img_no_image_automirrored); - } - return mDefaultContactPhotoDrawable; - } - - public Drawable getConferenceDrawable() { - if (mConferencePhotoDrawable == null) { - mConferencePhotoDrawable = - mContext.getResources().getDrawable(R.drawable.img_conference_automirrored); - } - return mConferencePhotoDrawable; - } - - /** - * Callback interface for the contact query. - */ - public interface ContactInfoCacheCallback { - public void onContactInfoComplete(String callId, ContactCacheEntry entry); - public void onImageLoadComplete(String callId, ContactCacheEntry entry); - public void onContactInteractionsInfoComplete(String callId, ContactCacheEntry entry); - } - - public static class ContactCacheEntry { - public String namePrimary; - public String nameAlternative; - public String number; - public String location; - public String label; - public Drawable photo; - public boolean isSipCall; - // Note in cache entry whether this is a pending async loading action to know whether to - // wait for its callback or not. - public boolean isLoadingPhoto; - public boolean isLoadingContactInteractions; - /** This will be used for the "view" notification. */ - public Uri contactUri; - /** Either a display photo or a thumbnail URI. */ - public Uri displayPhotoUri; - public Uri lookupUri; // Sent to NotificationMananger - public String lookupKey; - public Address locationAddress; - public List<Pair<Calendar, Calendar>> openingHours; - public int contactLookupResult = LogState.LOOKUP_NOT_FOUND; - public long userType = ContactsUtils.USER_TYPE_CURRENT; - public Uri contactRingtoneUri; - - @Override - public String toString() { - return MoreObjects.toStringHelper(this) - .add("name", MoreStrings.toSafeString(namePrimary)) - .add("nameAlternative", MoreStrings.toSafeString(nameAlternative)) - .add("number", MoreStrings.toSafeString(number)) - .add("location", MoreStrings.toSafeString(location)) - .add("label", label) - .add("photo", photo) - .add("isSipCall", isSipCall) - .add("contactUri", contactUri) - .add("displayPhotoUri", displayPhotoUri) - .add("locationAddress", locationAddress) - .add("openingHours", openingHours) - .add("contactLookupResult", contactLookupResult) - .add("userType", userType) - .add("contactRingtoneUri", contactRingtoneUri) - .toString(); - } - } -} |