summaryrefslogtreecommitdiff
path: root/InCallUI/src/com/android/incallui/CallCardPresenter.java
diff options
context:
space:
mode:
authorSantos Cordon <santoscordon@google.com>2013-08-12 16:31:35 -0700
committerSantos Cordon <santoscordon@google.com>2013-08-13 10:09:20 -0700
commitd2bd93fe3929403f84367f826f07cbadc0c6366b (patch)
treefda1b15279840e8f1665b5e12ad219ea1d3b8b99 /InCallUI/src/com/android/incallui/CallCardPresenter.java
parentb8eedb3b9796873eaa22847205239a51092f1a43 (diff)
Add Simplifying layer between Contact Info search and CallCardProvider.
I will be getting contact info on notifications for the next CL so this creates a simpler layer for interacting with the contact data async requester. CHANGES: - Moved the code which does the requesting from CallCardPresenter to ContactInfoCache. - ContactInfo Cache defines new listening interface and new simpler object for transmitting the return data: ContactInfoEntry. - Updated CallCardPresenter to use the new simpler interface. - Updated some logging entries. - Simplified Ui interface inside for CallCardPresenter. Change-Id: Ic802c4e53cdf17fcd37c70deb6da61a78b9d8993
Diffstat (limited to 'InCallUI/src/com/android/incallui/CallCardPresenter.java')
-rw-r--r--InCallUI/src/com/android/incallui/CallCardPresenter.java358
1 files changed, 81 insertions, 277 deletions
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index d4a39eaab..105e3482a 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -16,15 +16,12 @@
package com.android.incallui;
-import android.content.ContentUris;
import android.content.Context;
-import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
-import android.net.Uri;
-import android.provider.ContactsContract.Contacts;
-import android.text.TextUtils;
import com.android.incallui.AudioModeProvider.AudioModeListener;
+import com.android.incallui.ContactInfoCache.ContactCacheEntry;
+import com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
import com.android.incallui.InCallPresenter.InCallState;
import com.android.incallui.InCallPresenter.InCallStateListener;
@@ -35,27 +32,18 @@ import com.android.services.telephony.common.Call;
* Presenter for the Call Card Fragment.
* This class listens for changes to InCallState and passes it along to the fragment.
*/
-public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> implements
- InCallStateListener, CallerInfoAsyncQuery.OnQueryCompleteListener,
- ContactsAsyncHelper.OnImageLoadCompleteListener, AudioModeListener {
-
- private static final int TOKEN_UPDATE_PHOTO_FOR_CALL_STATE = 0;
+public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
+ implements InCallStateListener, AudioModeListener, ContactInfoCacheCallback {
private Context mContext;
private AudioModeProvider mAudioModeProvider;
+ private ContactInfoCache mContactInfoCache;
private Call mPrimary;
-
- /**
- * Uri being used to load contact photo for mPhoto. Will be null when nothing is being loaded,
- * or a photo is already loaded.
- */
- private Uri mLoadingPersonUri;
-
- // Track the state for the photo.
- private ContactsAsyncHelper.ImageTracker mPhotoTracker;
+ private Call mSecondary;
+ private ContactCacheEntry mPrimaryContactInfo;
+ private ContactCacheEntry mSecondaryContactInfo;
public CallCardPresenter() {
- mPhotoTracker = new ContactsAsyncHelper.ImageTracker();
}
@Override
@@ -75,10 +63,14 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> i
mAudioModeProvider.removeListener(this);
}
mPrimary = null;
+ mPrimaryContactInfo = null;
+ mSecondaryContactInfo = null;
}
public void setContext(Context context) {
mContext = context;
+ mContactInfoCache = new ContactInfoCache(mContext);
+ startContactInfoSearch();
}
@Override
@@ -107,32 +99,23 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> i
Logger.d(this, "Primary call: " + primary);
Logger.d(this, "Secondary call: " + secondary);
+ mPrimary = primary;
+ mSecondary = secondary;
- if (primary != null) {
- // Set primary call data
- final CallerInfo primaryCallInfo = CallerInfoUtils.getCallerInfoForCall(mContext,
- primary, null, this);
- updateDisplayByCallerInfo(primary, primaryCallInfo, primary.getNumberPresentation(),
- true);
+ // Query for contact data. This will call back on onContactInfoComplete at least once
+ // synchronously, and potentially a second time asynchronously if it needs to make
+ // a full query for the data.
+ // It is in that callback that we set the values into the Ui.
+ startContactInfoSearch();
+ // Set the call state
+ if (mPrimary != null) {
final boolean bluetoothOn = mAudioModeProvider != null &&
mAudioModeProvider.getAudioMode() == AudioMode.BLUETOOTH;
-
- ui.setNumber(primary.getNumber());
- ui.setCallState(primary.getState(), primary.getDisconnectCause(), bluetoothOn);
+ ui.setCallState(mPrimary.getState(), mPrimary.getDisconnectCause(), bluetoothOn);
} else {
- ui.setNumber("");
ui.setCallState(Call.State.INVALID, Call.DisconnectCause.UNKNOWN, false);
}
-
- // Set secondary call data
- if (secondary != null) {
- ui.setSecondaryCallInfo(true, secondary.getNumber());
- } else {
- ui.setSecondaryCallInfo(false, null);
- }
-
- mPrimary = primary;
}
@Override
@@ -149,6 +132,25 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> i
}
/**
+ * Starts a query for more contact data for the save primary and secondary calls.
+ */
+ private void startContactInfoSearch() {
+ if (mPrimary != null && mContactInfoCache != null) {
+ mContactInfoCache.findInfo(mPrimary, this);
+ } else {
+ mPrimaryContactInfo = null;
+ updatePrimaryDisplayInfo();
+ }
+
+ if (mSecondary != null && mContactInfoCache != null) {
+ mContactInfoCache.findInfo(mSecondary, this);
+ } else {
+ mSecondaryContactInfo = null;
+ updateSecondaryDisplayInfo();
+ }
+ }
+
+ /**
* Get the highest priority call to display.
* Goes through the calls and chooses which to return based on priority of which type of call
* to display to the user. Callers can use the "ignore" feature to get the second best call
@@ -183,251 +185,56 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> i
return retval;
}
- public interface CallCardUi extends Ui {
- // TODO(klp): Consider passing in the Call object directly in these methods.
- void setVisible(boolean on);
- void setNumber(String number);
- void setNumberLabel(String label);
- void setName(String name);
- void setName(String name, boolean isNumber);
- void setImage(int resource);
- void setImage(Drawable drawable);
- void setImage(Bitmap bitmap);
- void setSecondaryCallInfo(boolean show, String number);
- void setCallState(int state, Call.DisconnectCause cause, boolean bluetoothOn);
- }
-
- @Override
- public void onQueryComplete(int token, Object cookie, CallerInfo ci) {
- if (cookie instanceof Call) {
- final Call call = (Call) cookie;
- if (ci.contactExists || ci.isEmergencyNumber() || ci.isVoiceMailNumber()) {
- updateDisplayByCallerInfo(call, ci, Call.PRESENTATION_ALLOWED, true);
- } else {
- // If the contact doesn't exist, we can still use information from the
- // returned caller info (geodescription, etc).
- updateDisplayByCallerInfo(call, ci, call.getNumberPresentation(), true);
- }
-
- // Todo (klp): updatePhotoForCallState(call);
- }
- }
-
/**
- * Based on the given caller info, determine a suitable name, phone number and label
- * to be passed to the CallCardUI.
- *
- * If the current call is a conference call, use
- * updateDisplayForConference() instead.
+ * Callback received when Contact info data query completes.
*/
- private void updateDisplayByCallerInfo(Call call, CallerInfo info, int presentation,
- boolean isPrimary) {
-
- // Inform the state machine that we are displaying a photo.
- mPhotoTracker.setPhotoRequest(info);
- mPhotoTracker.setPhotoState(ContactsAsyncHelper.ImageTracker.DISPLAY_IMAGE);
-
- // The actual strings we're going to display onscreen:
- String displayName;
- String displayNumber = null;
- String label = null;
- Uri personUri = null;
-
- // Gather missing info unless the call is generic, in which case we wouldn't use
- // the gathered information anyway.
- if (info != null) {
-
- // 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 ((number != null) && 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(presentation);
- Logger.d(this, " ==> no name *or* number! displayName = " + displayName);
- } else if (presentation != Call.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(presentation);
- Logger.d(this, " ==> 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;
- Logger.d(this, " ==> 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.
-
- // Promote the phone number up to the "name" slot:
- displayName = number;
-
- // ...and use the "number" slot for a geographical description
- // string if available (but only for incoming calls.)
- if ((call != null) && (call.getState() == Call.State.INCOMING)) {
- // TODO (CallerInfoAsyncQuery cleanup): Fix the CallerInfo
- // query to only do the geoDescription lookup in the first
- // place for incoming calls.
- displayNumber = info.geoDescription; // may be null
- Logger.d(this, "Geodescrption: " + info.geoDescription);
- }
-
- Logger.d(this, " ==> no name; falling back to number: displayName '"
- + displayName + "', displayNumber '" + displayNumber + "'");
- }
- } 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 != Call.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(presentation);
- Logger.d(this, " ==> valid name, but presentation not allowed!"
- + " displayName = " + displayName);
- } else {
- displayName = info.name;
- displayNumber = number;
- label = info.phoneLabel;
- Logger.d(this, " ==> name is present in CallerInfo: displayName '"
- + displayName + "', displayNumber '" + displayNumber + "'");
- }
- }
- personUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, info.person_id);
- Logger.d(this, "- got personUri: '" + personUri
- + "', based on info.person_id: " + info.person_id);
- } else {
- displayName = getPresentationString(presentation);
- }
+ @Override
+ public void onContactInfoComplete(int callId, ContactCacheEntry entry) {
+ Logger.d(this, "onContactInfoComplete: ", entry.name);
+ Logger.d(this, "onContactInfoComplete: ", entry.number);
+ Logger.d(this, "onContactInfoComplete: ", entry.label);
+ Logger.d(this, "onContactInfoComplete: ", entry.photo);
- // TODO (klp): Update secondary user call info as well.
- if (isPrimary) {
- updateInfoUiForPrimary(displayName, displayNumber, label);
+ if (mPrimary != null && mPrimary.getCallId() == callId) {
+ mPrimaryContactInfo = entry;
+ updatePrimaryDisplayInfo();
}
+ if (mSecondary != null && mSecondary.getCallId() == callId) {
+ mSecondaryContactInfo = entry;
+ updateSecondaryDisplayInfo();
+ }
+
+ }
- // If the photoResource is filled in for the CallerInfo, (like with the
- // Emergency Number case), then we can just set the photo image without
- // requesting for an image load. Please refer to CallerInfoAsyncQuery.java
- // for cases where CallerInfo.photoResource may be set. We can also avoid
- // the image load step if the image data is cached.
+ private void updatePrimaryDisplayInfo() {
final CallCardUi ui = getUi();
- if (info == null) return;
-
- // This will only be true for emergency numbers
- if (info.photoResource != 0) {
- ui.setImage(info.photoResource);
- } else if (info.isCachedPhotoCurrent) {
- if (info.cachedPhoto != null) {
- ui.setImage(info.cachedPhoto);
- } else {
- ui.setImage(R.drawable.picture_unknown);
- }
- } else {
- if (personUri == null) {
- Logger.v(this, "personUri is null. Just use unknown picture.");
- ui.setImage(R.drawable.picture_unknown);
- } else if (personUri.equals(mLoadingPersonUri)) {
- Logger.v(this, "The requested Uri (" + personUri + ") is being loaded already."
- + " Ignore the duplicate load request.");
- } else {
- // Remember which person's photo is being loaded right now so that we won't issue
- // unnecessary load request multiple times, which will mess up animation around
- // the contact photo.
- mLoadingPersonUri = personUri;
-
- // Load the image with a callback to update the image state.
- // When the load is finished, onImageLoadComplete() will be called.
- ContactsAsyncHelper.startObtainPhotoAsync(TOKEN_UPDATE_PHOTO_FOR_CALL_STATE,
- mContext, personUri, this, call);
-
- // If the image load is too slow, we show a default avatar icon afterward.
- // If it is fast enough, this message will be canceled on onImageLoadComplete().
- // TODO (klp): Figure out if this handler is still needed.
- // mHandler.removeMessages(MESSAGE_SHOW_UNKNOWN_PHOTO);
- // mHandler.sendEmptyMessageDelayed(MESSAGE_SHOW_UNKNOWN_PHOTO, MESSAGE_DELAY);
- }
+ if (ui == null) {
+ return;
}
- // TODO (klp): Update other fields - photo, sip label, etc.
- }
- /**
- * 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) {
- // mHandler.removeMessages(MESSAGE_SHOW_UNKNOWN_PHOTO);
- if (mLoadingPersonUri != null) {
- // Start sending view notification after the current request being done.
- // New image may possibly be available from the next phone calls.
- //
- // TODO: may be nice to update the image view again once the newer one
- // is available on contacts database.
- // TODO (klp): What is this, and why does it need the write_contacts permission?
- // CallerInfoUtils.sendViewNotificationAsync(mContext, mLoadingPersonUri);
+ if (mPrimaryContactInfo != null) {
+ ui.setPrimary(mPrimaryContactInfo.number, mPrimaryContactInfo.name,
+ mPrimaryContactInfo.label, mPrimaryContactInfo.photo);
} else {
- // This should not happen while we need some verbose info if it happens..
- Logger.v(this, "Person Uri isn't available while Image is successfully loaded.");
+ // reset to nothing (like at end of call)
+ ui.setPrimary(null, null, null, null);
}
- mLoadingPersonUri = null;
-
- Call call = (Call) cookie;
- // TODO (klp): Handle conference calls
+ }
+ private void updateSecondaryDisplayInfo() {
final CallCardUi ui = getUi();
- if (photo != null) {
- ui.setImage(photo);
- } else if (photoIcon != null) {
- ui.setImage(photoIcon);
- } else {
- ui.setImage(R.drawable.picture_unknown);
+ if (ui == null) {
+ return;
}
- }
- /**
- * Updates the info portion of the call card with passed in values for the primary user.
- */
- private void updateInfoUiForPrimary(String displayName, String displayNumber, String label) {
- final CallCardUi ui = getUi();
- ui.setName(displayName);
- ui.setNumber(displayNumber);
- ui.setNumberLabel(label);
+ if (mSecondaryContactInfo != null) {
+ ui.setSecondary(true, mSecondaryContactInfo.number, mSecondaryContactInfo.name,
+ mSecondaryContactInfo.label, mSecondaryContactInfo.photo);
+ } else {
+ // reset to nothing so that it starts off blank next time we use it.
+ ui.setSecondary(false, null, null, null, null);
+ }
}
public void setAudioModeProvider(AudioModeProvider audioModeProvider) {
@@ -435,13 +242,10 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi> i
mAudioModeProvider.addListener(this);
}
- public String getPresentationString(int presentation) {
- String name = mContext.getString(R.string.unknown);
- if (presentation == Call.PRESENTATION_RESTRICTED) {
- name = mContext.getString(R.string.private_num);
- } else if (presentation == Call.PRESENTATION_PAYPHONE) {
- name = mContext.getString(R.string.payphone);
- }
- return name;
+ public interface CallCardUi extends Ui {
+ void setVisible(boolean on);
+ void setPrimary(String number, String name, String label, Drawable photo);
+ void setSecondary(boolean show, String number, String name, String label, Drawable photo);
+ void setCallState(int state, Call.DisconnectCause cause, boolean bluetoothOn);
}
}