summaryrefslogtreecommitdiff
path: root/InCallUI
diff options
context:
space:
mode:
authorSantos Cordon <santoscordon@google.com>2013-09-13 18:56:45 -0700
committerSantos Cordon <santoscordon@google.com>2013-09-13 23:46:31 -0700
commit49ff6571403695e81dbbd83e4f61790ce9c75f6d (patch)
tree6ae7141e11e5866b5dda8ce8cf84b20ea6b7068a /InCallUI
parentb5346c68a29e128fed4d1e72b3501599ee9663bb (diff)
Fix UI lag when calling a contact with a photo.
Related fixes unrelated to latency problem: a. Update StatusBarNotifier with new changes to CallerInfoCache. b. Outgoing JANK fix in notifier needed to know if the activity was previously started, not just if it was finished. (b/10734874) c. Consolidate places where we use the Intent. Several improvements to speed up startUp time from most egregious to least: 1. In InCallPresenter, statusBarNotifier wasn't unlistening to incoming call changes which kept orphaned instances still listening and calling into contactInfoCache. ContactInfoCache was subsequently calling into many notifiers which did heavy image parcelling work. - Clear incomingCallListeners on tear down. 2. StatusBarNotifier was getting called directly from InCallPresenter & onIncomingCall() callbacks. onIncomingCall callback was unnecessary and caused extra work. - Fix is to stop listening to incoming calls. Status bar notifier gets called directly by InCallPresenter in startAndFinish() so listening to incoming was redundant. 3. Make ContactInfoCache listeners list a Set to avoid duplicate entries and reduce callback execution. bug:10712670 bug:10734874 Change-Id: Ic8d50b1d9ce336ffe3a5191abe1c9db32365eee6
Diffstat (limited to 'InCallUI')
-rw-r--r--InCallUI/src/com/android/incallui/CallCardFragment.java10
-rw-r--r--InCallUI/src/com/android/incallui/CallCardPresenter.java16
-rw-r--r--InCallUI/src/com/android/incallui/CallList.java4
-rw-r--r--InCallUI/src/com/android/incallui/ContactInfoCache.java33
-rw-r--r--InCallUI/src/com/android/incallui/InCallPresenter.java30
-rw-r--r--InCallUI/src/com/android/incallui/StatusBarNotifier.java119
6 files changed, 114 insertions, 98 deletions
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 71f577b74..8fb2eb4b4 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -134,9 +134,9 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
}
@Override
- public void setPrimaryImage(Bitmap image) {
+ public void setPrimaryImage(Drawable image) {
if (image != null) {
- setDrawableToImageView(mPhoto, new BitmapDrawable(getResources(), image));
+ setDrawableToImageView(mPhoto, image);
}
}
@@ -213,9 +213,9 @@ public class CallCardFragment extends BaseFragment<CallCardPresenter, CallCardPr
}
@Override
- public void setSecondaryImage(Bitmap bitmap) {
- if (bitmap != null) {
- setDrawableToImageView(mSecondaryPhoto, new BitmapDrawable(getResources(), bitmap));
+ public void setSecondaryImage(Drawable image) {
+ if (image != null) {
+ setDrawableToImageView(mSecondaryPhoto, image);
}
}
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 279925d50..b055a58a1 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -242,14 +242,16 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
}
@Override
- public void onImageLoadComplete(int callId, Bitmap photo) {
+ public void onImageLoadComplete(int callId, ContactCacheEntry entry) {
if (getUi() == null) {
return;
}
- if (mPrimary != null && callId == mPrimary.getCallId()) {
- getUi().setPrimaryImage(photo);
- } else if (mSecondary != null && callId == mSecondary.getCallId()) {
- getUi().setSecondaryImage(photo);
+ if (entry.photo != null) {
+ if (mPrimary != null && callId == mPrimary.getCallId()) {
+ getUi().setPrimaryImage(entry.photo);
+ } else if (mSecondary != null && callId == mSecondary.getCallId()) {
+ getUi().setSecondaryImage(entry.photo);
+ }
}
}
});
@@ -429,12 +431,12 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
Drawable photo, boolean isConference);
void setSecondary(boolean show, String name, boolean nameIsNumber, String label,
Drawable photo, boolean isConference);
- void setSecondaryImage(Bitmap bitmap);
+ void setSecondaryImage(Drawable image);
void setCallState(int state, Call.DisconnectCause cause, boolean bluetoothOn,
String gatewayLabel, String gatewayNumber);
void setPrimaryCallElapsedTime(boolean show, String duration);
void setPrimaryName(String name, boolean nameIsNumber);
- void setPrimaryImage(Bitmap bitmap);
+ void setPrimaryImage(Drawable image);
void setPrimaryPhoneNumber(String phoneNumber);
void setPrimaryLabel(String label);
}
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index 42f4eb974..1b7e10d85 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -236,6 +236,10 @@ public class CallList {
return result;
}
+ public Call getCall(int callId) {
+ return mCallMap.get(callId);
+ }
+
public boolean existsLiveCall() {
for (Call call : mCallMap.values()) {
if (!isCallDead(call)) {
diff --git a/InCallUI/src/com/android/incallui/ContactInfoCache.java b/InCallUI/src/com/android/incallui/ContactInfoCache.java
index 9ad7c5415..788f4e4a9 100644
--- a/InCallUI/src/com/android/incallui/ContactInfoCache.java
+++ b/InCallUI/src/com/android/incallui/ContactInfoCache.java
@@ -34,11 +34,13 @@ import com.android.services.telephony.common.CallIdentification;
import com.android.services.telephony.common.MoreStrings;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
+import com.google.android.collect.Sets;
import com.google.common.base.Objects;
import com.google.common.base.Preconditions;
import java.util.HashMap;
import java.util.List;
+import java.util.Set;
/**
* Class responsible for querying Contact Information for Call objects. Can perform asynchronous
@@ -54,8 +56,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
private final Context mContext;
private final PhoneNumberService mPhoneNumberService;
private final HashMap<Integer, ContactCacheEntry> mInfoMap = Maps.newHashMap();
- private final HashMap<Integer, List<ContactInfoCacheCallback>> mCallBacks =
- Maps.newHashMap();
+ private final HashMap<Integer, Set<ContactInfoCacheCallback>> mCallBacks = Maps.newHashMap();
private static ContactInfoCache sCache = null;
@@ -115,7 +116,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
final int callId = identification.getCallId();
final ContactCacheEntry cacheEntry = mInfoMap.get(callId);
- List<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
+ Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
// If we have a previously obtained intermediate result return that now
if (cacheEntry != null) {
@@ -135,7 +136,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
}
Log.d(TAG, "Contact lookup. In memory cache miss; searching provider.");
// New lookup
- callBacks = Lists.newArrayList();
+ callBacks = Sets.newHashSet();
callBacks.add(callback);
mCallBacks.put(callId, callBacks);
@@ -402,7 +403,8 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
displayName = info.cnapName;
info.name = info.cnapName;
displayNumber = number;
- Log.d(TAG, " ==> cnapName available: displayName '" + displayName + "', displayNumber '" + displayNumber + "'");
+ 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,
@@ -432,8 +434,8 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
// AND a restricted presentation. However we leave it here in case of weird
// network behavior
displayName = getPresentationString(context, presentation);
- Log.d(TAG,
- " ==> valid name, but presentation not allowed!" + " displayName = " + displayName);
+ Log.d(TAG, " ==> valid name, but presentation not allowed!" +
+ " displayName = " + displayName);
} else {
displayName = info.name;
displayNumber = number;
@@ -453,7 +455,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
* Sends the updated information to call the callbacks for the entry.
*/
private void sendInfoNotifications(int callId, ContactCacheEntry entry) {
- final List<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);;
+ final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
if (callBacks != null) {
for (ContactInfoCacheCallback callBack : callBacks) {
callBack.onContactInfoComplete(callId, entry);
@@ -462,21 +464,16 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
}
private void sendImageNotifications(int callId, ContactCacheEntry entry) {
- final List<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);;
- if (callBacks != null) {
+ final Set<ContactInfoCacheCallback> callBacks = mCallBacks.get(callId);
+ if (callBacks != null && entry.photo != null) {
for (ContactInfoCacheCallback callBack : callBacks) {
- if (entry.photo == null) {
- callBack.onImageLoadComplete(callId, null);
- } else {
- callBack.onImageLoadComplete(callId, ((BitmapDrawable) entry.photo)
- .getBitmap());
- }
+ callBack.onImageLoadComplete(callId, entry);
}
}
}
private void clearCallbacks(int callId) {
- mCallBacks.remove(callId);;
+ mCallBacks.remove(callId);
}
/**
@@ -497,7 +494,7 @@ public class ContactInfoCache implements ContactsAsyncHelper.OnImageLoadComplete
*/
public interface ContactInfoCacheCallback {
public void onContactInfoComplete(int callId, ContactCacheEntry entry);
- public void onImageLoadComplete(int callId, Bitmap photo);
+ public void onImageLoadComplete(int callId, ContactCacheEntry entry);
}
public static class ContactCacheEntry {
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 0f8d4071a..47b0871ae 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -50,9 +50,17 @@ public class InCallPresenter implements CallList.Listener {
private Context mContext;
private CallList mCallList;
private InCallActivity mInCallActivity;
- private boolean mServiceConnected = false;
private InCallState mInCallState = InCallState.NO_CALLS;
private ProximitySensor mProximitySensor;
+ private boolean mServiceConnected = false;
+
+ /**
+ * Is true when the activity has been previously started. Some code needs to know not just if
+ * the activity is currently up, but if it had been previously shown in foreground for this
+ * in-call session (e.g., StatusBarNotifier). This gets reset when the session ends in the
+ * tear-down method.
+ */
+ private boolean mActivityPreviouslyStarted = false;
public static synchronized InCallPresenter getInstance() {
if (sInCallPresenter == null) {
@@ -78,7 +86,6 @@ public class InCallPresenter implements CallList.Listener {
mStatusBarNotifier = new StatusBarNotifier(context, mContactInfoCache, mCallList);
addListener(mStatusBarNotifier);
- addIncomingCallListener(mStatusBarNotifier);
mAudioModeProvider = audioModeProvider;
@@ -218,6 +225,11 @@ public class InCallPresenter implements CallList.Listener {
mIncomingCallListeners.add(listener);
}
+ public void removeIncomingCallListener(IncomingCallListener listener) {
+ Preconditions.checkNotNull(listener);
+ mIncomingCallListeners.remove(listener);
+ }
+
public void addListener(InCallStateListener listener) {
Preconditions.checkNotNull(listener);
mListeners.add(listener);
@@ -272,8 +284,12 @@ public class InCallPresenter implements CallList.Listener {
!mInCallActivity.isFinishing());
}
+ public boolean isActivityPreviouslyStarted() {
+ return mActivityPreviouslyStarted;
+ }
+
/**
- * Called when the activity goes out of the foreground.
+ * Called when the activity goes in/out of the foreground.
*/
public void onUiShowing(boolean showing) {
// We need to update the notification bar when we leave the UI because that
@@ -285,6 +301,10 @@ public class InCallPresenter implements CallList.Listener {
if (mProximitySensor != null) {
mProximitySensor.onInCallShowing(showing);
}
+
+ if (showing) {
+ mActivityPreviouslyStarted = true;
+ }
}
/**
@@ -397,6 +417,7 @@ public class InCallPresenter implements CallList.Listener {
Log.i(this, "attemptCleanup? " + shouldCleanup);
if (shouldCleanup) {
+ mActivityPreviouslyStarted = false;
// blow away stale contact info so that we get fresh data on
// the next set of calls
@@ -417,6 +438,7 @@ public class InCallPresenter implements CallList.Listener {
mInCallActivity = null;
mListeners.clear();
+ mIncomingCallListeners.clear();
Log.d(this, "Finished InCallPresenter.CleanUp");
}
@@ -426,7 +448,7 @@ public class InCallPresenter implements CallList.Listener {
mContext.startActivity(getInCallIntent());
}
- private Intent getInCallIntent() {
+ public Intent getInCallIntent() {
final Intent intent = new Intent(Intent.ACTION_MAIN, null);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
index 83dba8c57..2692ab813 100644
--- a/InCallUI/src/com/android/incallui/StatusBarNotifier.java
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -36,11 +36,12 @@ import com.android.incallui.InCallApp.NotificationBroadcastReceiver;
import com.android.incallui.InCallPresenter.InCallState;
import com.android.services.telephony.common.Call;
+import java.util.HashMap;
+
/**
* This class adds Notifications to the status bar for the in-call experience.
*/
-public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
- InCallPresenter.IncomingCallListener {
+public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
// notification types
private static final int IN_CALL_NOTIFICATION = 1;
@@ -49,7 +50,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
private final CallList mCallList;
private final NotificationManager mNotificationManager;
private boolean mIsShowingNotification = false;
- private InCallState mInCallState = InCallState.NO_CALLS;
+ private int mCallState = Call.State.INVALID;
private int mSavedIcon = 0;
private int mSavedContent = 0;
private Bitmap mSavedLargeIcon;
@@ -72,45 +73,8 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
@Override
public void onStateChange(InCallState state, CallList callList) {
Log.d(this, "onStateChange");
- updateNotification(state, callList);
- }
-
- @Override
- public void onIncomingCall(final Call call) {
- final ContactCacheEntry entry = ContactInfoCache.buildCacheEntryFromCall(mContext,
- call.getIdentification(), true);
-
- // Initial update with no contact information.
- buildAndSendNotification(InCallState.INCOMING, call, entry, false);
- // TODO(klp): InCallPresenter already calls updateNofication() when it wants to start
- // the notification. We shouldn't do this twice.
- // TODO(klp): This search doesn't happen for outgoing calls any more. It works because
- // the call card makes a requests that are cached...but eventually this startup process
- // needs to incorporate call searches for all new calls, not just incoming.
-
- // we make a call to the contact info cache to query for supplemental data to what the
- // call provides. This includes the contact name and photo.
- // This callback will always get called immediately and synchronously with whatever data
- // it has available, and may make a subsequent call later (same thread) if it had to
- // call into the contacts provider for more data.
- mContactInfoCache.findInfo(call.getIdentification(), true, new ContactInfoCacheCallback() {
- private ContactCacheEntry mEntry;
-
- @Override
- public void onContactInfoComplete(int callId, ContactCacheEntry entry) {
- mEntry = entry;
- buildAndSendNotification(InCallState.INCOMING, call, entry, false);
- }
-
- @Override
- public void onImageLoadComplete(int callId, Bitmap photo) {
- if (mEntry != null) {
- mEntry.photo = new BitmapDrawable(mContext.getResources(), photo);
- buildAndSendNotification(InCallState.INCOMING, call, mEntry, false);
- }
- }
- });
+ updateNotification(state, callList);
}
/**
@@ -165,7 +129,6 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
updateInCallNotification(true, state, callList);
}
-
/**
* Take down the in-call notification.
* @see updateInCallNotification()
@@ -202,22 +165,51 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
return;
}
- // Contact info should have already been done on incoming calls.
- // TODO(klp): This also needs to be done for outgoing calls.
- ContactCacheEntry entry = mContactInfoCache.getInfo(call.getCallId());
- if (entry == null) {
- entry = ContactInfoCache.buildCacheEntryFromCall(mContext, call.getIdentification(),
- state == InCallState.INCOMING);
- }
- buildAndSendNotification(state, call, entry, allowFullScreenIntent);
+ // we make a call to the contact info cache to query for supplemental data to what the
+ // call provides. This includes the contact name and photo.
+ // This callback will always get called immediately and synchronously with whatever data
+ // it has available, and may make a subsequent call later (same thread) if it had to
+ // call into the contacts provider for more data.
+ mContactInfoCache.findInfo(call.getIdentification(), call.getState() == Call.State.INCOMING,
+ new ContactInfoCacheCallback() {
+ private boolean mAllowFullScreenIntent = allowFullScreenIntent;
+
+ @Override
+ public void onContactInfoComplete(int callId, ContactCacheEntry entry) {
+ Call call = CallList.getInstance().getCall(callId);
+ if (call != null) {
+ buildAndSendNotification(call, entry, mAllowFullScreenIntent);
+ }
+
+ // Full screen intents are what bring up the in call screen. We only want
+ // to do this the first time we are called back.
+ mAllowFullScreenIntent = false;
+ }
+
+ @Override
+ public void onImageLoadComplete(int callId, ContactCacheEntry entry) {
+ Call call = CallList.getInstance().getCall(callId);
+ if (call != null) {
+ buildAndSendNotification(call, entry, mAllowFullScreenIntent);
+ }
+ } });
}
/**
* Sets up the main Ui for the notification
*/
- private void buildAndSendNotification(InCallState state, Call call,
- ContactCacheEntry contactInfo, boolean allowFullScreenIntent) {
+ private void buildAndSendNotification(Call originalCall, ContactCacheEntry contactInfo,
+ boolean allowFullScreenIntent) {
+
+ // This can get called to update an existing notification after contact information has come
+ // back. However, it can happen much later. Before we continue, we need to make sure that
+ // the call being passed in is still the one we want to show in the notification.
+ final Call call = getCallToShow(CallList.getInstance());
+ if (call.getCallId() != originalCall.getCallId()) {
+ return;
+ }
+ final int state = call.getState();
final boolean isConference = call.isConferenceCall();
final int iconResId = getIconToDisplay(call);
final Bitmap largeIcon = getLargeIconToDisplay(contactInfo, isConference);
@@ -250,15 +242,17 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
builder.setContentTitle(contentTitle);
builder.setLargeIcon(largeIcon);
- if (call.getState() == Call.State.ACTIVE) {
+ if (state == Call.State.ACTIVE) {
builder.setUsesChronometer(true);
builder.setWhen(call.getConnectTime());
} else {
builder.setUsesChronometer(false);
}
- // Add special Content for calls that are ongoing
- if (InCallState.INCALL == state || InCallState.OUTGOING == state) {
+ // Add hang up option for any active calls (active | onhold), outgoing calls (dialing).
+ if (state == Call.State.ACTIVE ||
+ state == Call.State.DIALING ||
+ state == Call.State.ONHOLD) {
addHangupAction(builder);
}
@@ -277,7 +271,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
* we do not issue a new notification for the exact same data.
*/
private boolean checkForChangeAndSaveData(int icon, int content, Bitmap largeIcon,
- String contentTitle, InCallState state, boolean showFullScreenIntent) {
+ String contentTitle, int state, boolean showFullScreenIntent) {
// The two are different:
// if new title is not null, it should be different from saved version OR
@@ -288,7 +282,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
// any change means we are definitely updating
boolean retval = (mSavedIcon != icon) || (mSavedContent != content) ||
- (mInCallState != state) || (mSavedLargeIcon != largeIcon) ||
+ (mCallState != state) || (mSavedLargeIcon != largeIcon) ||
contentTitleChanged;
// A full screen intent means that we have been asked to interrupt an activity,
@@ -306,7 +300,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
mSavedIcon = icon;
mSavedContent = content;
- mInCallState = state;
+ mCallState = state;
mSavedLargeIcon = largeIcon;
mSavedContentTitle = contentTitle;
@@ -501,7 +495,8 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
// comes up the user will see it flash on and off on an outgoing call.
// This code ensures that we do not show the notification for outgoing calls before
// the activity has started.
- if (state == InCallState.OUTGOING && !InCallPresenter.getInstance().isActivityStarted()) {
+ if (state == InCallState.OUTGOING &&
+ !InCallPresenter.getInstance().isActivityPreviouslyStarted()) {
Log.v(this, "suppressing: activity not started.");
shouldSuppress = true;
}
@@ -511,11 +506,7 @@ public class StatusBarNotifier implements InCallPresenter.InCallStateListener,
private PendingIntent createLaunchPendingIntent() {
- final Intent intent = new Intent(Intent.ACTION_MAIN, null);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
- | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS
- | Intent.FLAG_ACTIVITY_NO_USER_ACTION);
- intent.setClass(mContext, InCallActivity.class);
+ final Intent intent = InCallPresenter.getInstance().getInCallIntent();
// PendingIntent that can be used to launch the InCallActivity. The
// system fires off this intent if the user pulls down the windowshade