summaryrefslogtreecommitdiff
path: root/InCallUI
diff options
context:
space:
mode:
authorSantos Cordon <santoscordon@google.com>2013-08-05 09:42:04 -0700
committerSantos Cordon <santoscordon@google.com>2013-08-05 15:36:15 -0700
commit1b60e8c70bdcbf07c3efab310790134ecf81d4fd (patch)
tree53a677be4d17034d2907f06fc4b26571cdb947f9 /InCallUI
parent258eb86d9f64ebebee4ec4e39af385364ca0c071 (diff)
Notifications in Phone (part 1)
- Added StatusBarNotification class to handle notifications - StatusBarNotification has tons of copied comments from NotificationMgr.java. Also has minimal code to make the notification pop up (without call-specific data). - Moved the UI start-up code from InCallPresenter to StatusBarNotification. Incoming phone calls need to display an alert above immersive (fullscreen) foreground activities and the way to do that is through notifications. The old phone app did the same thing...finally bringing over that functionality. Change-Id: I3324503e0d7bb1de00d8e7fab72d4b40519491dd
Diffstat (limited to 'InCallUI')
-rw-r--r--InCallUI/res/drawable-hdpi/stat_sys_phone_call.pngbin0 -> 1095 bytes
-rw-r--r--InCallUI/res/drawable-ldrtl-hdpi/stat_sys_phone_call.pngbin0 -> 4993 bytes
-rw-r--r--InCallUI/res/drawable-ldrtl-mdpi/stat_sys_phone_call.pngbin0 -> 4578 bytes
-rw-r--r--InCallUI/res/drawable-ldrtl-xhdpi/stat_sys_phone_call.pngbin0 -> 5428 bytes
-rw-r--r--InCallUI/res/drawable-mdpi/stat_sys_phone_call.pngbin0 -> 796 bytes
-rw-r--r--InCallUI/res/drawable-xhdpi/stat_sys_phone_call.pngbin0 -> 1430 bytes
-rw-r--r--InCallUI/src/com/android/incallui/CallButtonPresenter.java8
-rw-r--r--InCallUI/src/com/android/incallui/CallCardPresenter.java7
-rw-r--r--InCallUI/src/com/android/incallui/CallHandlerService.java4
-rw-r--r--InCallUI/src/com/android/incallui/CallList.java36
-rw-r--r--InCallUI/src/com/android/incallui/InCallActivity.java10
-rw-r--r--InCallUI/src/com/android/incallui/InCallPresenter.java108
-rw-r--r--InCallUI/src/com/android/incallui/StatusBarNotifier.java267
13 files changed, 393 insertions, 47 deletions
diff --git a/InCallUI/res/drawable-hdpi/stat_sys_phone_call.png b/InCallUI/res/drawable-hdpi/stat_sys_phone_call.png
new file mode 100644
index 000000000..7eda84ca5
--- /dev/null
+++ b/InCallUI/res/drawable-hdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/InCallUI/res/drawable-ldrtl-hdpi/stat_sys_phone_call.png b/InCallUI/res/drawable-ldrtl-hdpi/stat_sys_phone_call.png
new file mode 100644
index 000000000..e0f33f88c
--- /dev/null
+++ b/InCallUI/res/drawable-ldrtl-hdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/InCallUI/res/drawable-ldrtl-mdpi/stat_sys_phone_call.png b/InCallUI/res/drawable-ldrtl-mdpi/stat_sys_phone_call.png
new file mode 100644
index 000000000..d771d87bf
--- /dev/null
+++ b/InCallUI/res/drawable-ldrtl-mdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/InCallUI/res/drawable-ldrtl-xhdpi/stat_sys_phone_call.png b/InCallUI/res/drawable-ldrtl-xhdpi/stat_sys_phone_call.png
new file mode 100644
index 000000000..86af9c202
--- /dev/null
+++ b/InCallUI/res/drawable-ldrtl-xhdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/InCallUI/res/drawable-mdpi/stat_sys_phone_call.png b/InCallUI/res/drawable-mdpi/stat_sys_phone_call.png
new file mode 100644
index 000000000..70a4bbe71
--- /dev/null
+++ b/InCallUI/res/drawable-mdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/InCallUI/res/drawable-xhdpi/stat_sys_phone_call.png b/InCallUI/res/drawable-xhdpi/stat_sys_phone_call.png
new file mode 100644
index 000000000..1bb434076
--- /dev/null
+++ b/InCallUI/res/drawable-xhdpi/stat_sys_phone_call.png
Binary files differ
diff --git a/InCallUI/src/com/android/incallui/CallButtonPresenter.java b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
index 014f8f8ae..60ab7bbf4 100644
--- a/InCallUI/src/com/android/incallui/CallButtonPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallButtonPresenter.java
@@ -49,7 +49,7 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
getUi().setVisible(state == InCallState.INCALL);
if (state == InCallState.INCALL) {
- mCall = callList.getActiveCall();
+ mCall = callList.getActiveOrBackgroundCall();
} else {
mCall = null;
}
@@ -75,11 +75,15 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
}
public void muteClicked(boolean checked) {
+ Logger.d(this, "turning on mute: " + checked);
+
CallCommandClient.getInstance().mute(checked);
getUi().setMute(checked);
}
public void speakerClicked(boolean checked) {
+ Logger.d(this, "turning on speaker: " + checked);
+
CallCommandClient.getInstance().turnSpeakerOn(checked);
getUi().setSpeaker(checked);
}
@@ -87,6 +91,8 @@ public class CallButtonPresenter extends Presenter<CallButtonPresenter.CallButto
public void holdClicked(boolean checked) {
Preconditions.checkNotNull(mCall);
+ Logger.d(this, "holding: " + mCall.getCallId());
+
// TODO(klp): use appropriate hold callId.
CallCommandClient.getInstance().hold(mCall.getCallId(), checked);
getUi().setHold(checked);
diff --git a/InCallUI/src/com/android/incallui/CallCardPresenter.java b/InCallUI/src/com/android/incallui/CallCardPresenter.java
index 5f1f3b747..04a43eae1 100644
--- a/InCallUI/src/com/android/incallui/CallCardPresenter.java
+++ b/InCallUI/src/com/android/incallui/CallCardPresenter.java
@@ -43,7 +43,12 @@ public class CallCardPresenter extends Presenter<CallCardPresenter.CallCardUi>
primary = callList.getIncomingCall();
} else if (state == InCallState.INCALL) {
primary = callList.getActiveCall();
- secondary = callList.getBackgroundCall();
+ if (primary != null) {
+ secondary = callList.getBackgroundCall();
+ } else {
+ primary = callList.getBackgroundCall();
+ secondary = callList.getSecondBackgroundCall();
+ }
}
Logger.d(this, "Primary call: " + primary);
diff --git a/InCallUI/src/com/android/incallui/CallHandlerService.java b/InCallUI/src/com/android/incallui/CallHandlerService.java
index ad4772976..dcba7d6f9 100644
--- a/InCallUI/src/com/android/incallui/CallHandlerService.java
+++ b/InCallUI/src/com/android/incallui/CallHandlerService.java
@@ -46,9 +46,7 @@ public class CallHandlerService extends Service {
mCallList = CallList.getInstance();
mMainHandler = new MainHandler();
- mInCallPresenter = InCallPresenter.getInstance();
-
- mInCallPresenter.init(this);
+ mInCallPresenter = InCallPresenter.init(this);
}
@Override
diff --git a/InCallUI/src/com/android/incallui/CallList.java b/InCallUI/src/com/android/incallui/CallList.java
index 17e719e7c..c16c53df0 100644
--- a/InCallUI/src/com/android/incallui/CallList.java
+++ b/InCallUI/src/com/android/incallui/CallList.java
@@ -128,8 +128,25 @@ public class CallList {
return getFirstCallWithState(Call.State.ONHOLD);
}
+ public Call getSecondBackgroundCall() {
+ return getCallWithState(Call.State.ONHOLD, 1);
+ }
+
+ public Call getActiveOrBackgroundCall() {
+ Call call = getActiveCall();
+ if (call == null) {
+ call = getBackgroundCall();
+ }
+ return call;
+ }
+
public Call getIncomingCall() {
- return getFirstCallWithState(Call.State.INCOMING);
+ Call call = getFirstCallWithState(Call.State.INCOMING);
+ if (call == null) {
+ call = getFirstCallWithState(Call.State.CALL_WAITING);
+ }
+
+ return call;
}
public boolean existsLiveCall() {
@@ -145,11 +162,24 @@ public class CallList {
* Returns first call found in the call map with the specified state.
*/
public Call getFirstCallWithState(int state) {
+ return getCallWithState(state, 0);
+ }
+
+ /**
+ * Returns the [position]th call found in the call map with the specified state.
+ * TODO(klp): Improve this logic to sort by call time.
+ */
+ public Call getCallWithState(int state, int positionToFind) {
Call retval = null;
+ int position = 0;
for (Call call : mCallMap.values()) {
if (call.getState() == state) {
- retval = call;
- break;
+ if (position >= positionToFind) {
+ retval = call;
+ break;
+ } else {
+ position++;
+ }
}
}
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index 89450c534..9eb54ee5e 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -101,6 +101,8 @@ public class InCallActivity extends Activity {
@Override
public void finish() {
Logger.d(this, "finish()...");
+ tearDownPresenters();
+
super.finish();
// TODO(klp): Actually finish the activity for now. Revisit performance implications of
@@ -220,6 +222,14 @@ public class InCallActivity extends Activity {
mainPresenter.setActivity(this);
}
+ private void tearDownPresenters() {
+ InCallPresenter mainPresenter = InCallPresenter.getInstance();
+
+ mainPresenter.removeListener(mCallButtonFragment.getPresenter());
+ mainPresenter.removeListener(mCallCardFragment.getPresenter());
+ mainPresenter.removeListener(mAnswerFragment.getPresenter());
+ }
+
private void toast(String text) {
final Toast toast = Toast.makeText(this, text, Toast.LENGTH_SHORT);
diff --git a/InCallUI/src/com/android/incallui/InCallPresenter.java b/InCallUI/src/com/android/incallui/InCallPresenter.java
index 118c030c2..47d2d69c8 100644
--- a/InCallUI/src/com/android/incallui/InCallPresenter.java
+++ b/InCallUI/src/com/android/incallui/InCallPresenter.java
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2006 The Android Open Source Project
+ * 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.
@@ -22,6 +22,8 @@ import com.google.common.base.Preconditions;
import android.content.Context;
import android.content.Intent;
+import com.android.services.telephony.common.Call;
+
import java.util.Set;
/**
@@ -31,35 +33,35 @@ import java.util.Set;
* are disconnected.
* Creates and manages the in-call state and provides a listener pattern for the presenters
* that want to listen in on the in-call state changes.
+ * TODO(klp): This class has become more of a state machine at this point. Consider renaming.
*/
public class InCallPresenter implements CallList.Listener {
private static InCallPresenter sInCallPresenter;
- private Context mContext;
+ private final StatusBarNotifier mStatusBarNotifier;
+ private final Set<InCallStateListener> mListeners = Sets.newHashSet();
+
private InCallState mInCallState = InCallState.HIDDEN;
private InCallActivity mInCallActivity;
- private final Set<InCallStateListener> mListeners = Sets.newHashSet();
- public static synchronized InCallPresenter getInstance() {
- if (sInCallPresenter == null) {
- sInCallPresenter = new InCallPresenter();
- }
+ public static InCallPresenter getInstance() {
+ Preconditions.checkNotNull(sInCallPresenter);
return sInCallPresenter;
}
- public void init(Context context) {
- Logger.i(this, "InCallPresenter initialized with context " + context);
- Preconditions.checkState(mContext == null);
-
- mContext = context;
- CallList.getInstance().addListener(this);
+ public static synchronized InCallPresenter init(Context context) {
+ Preconditions.checkState(sInCallPresenter == null);
+ sInCallPresenter = new InCallPresenter(context);
+ return sInCallPresenter;
}
public void setActivity(InCallActivity inCallActivity) {
mInCallActivity = inCallActivity;
mInCallState = InCallState.STARTED;
+ Logger.d(this, "UI Initialized");
+
// Since the UI just came up, imitate an update from the call list
// to set the proper UI state.
onCallListChange(CallList.getInstance());
@@ -75,21 +77,14 @@ public class InCallPresenter implements CallList.Listener {
public void onCallListChange(CallList callList) {
// fast fail if we are still starting up
if (mInCallState == InCallState.STARTING_UP) {
+ Logger.d(this, "Already on STARTING_UP, ignoring until ready");
return;
}
- InCallState newState = mInCallState;
- if (callList.getIncomingCall() != null) {
- newState = InCallState.INCOMING;
- } else if (callList.getActiveCall() != null) {
- newState = InCallState.INCALL;
- } else {
- newState = InCallState.HIDDEN;
- }
-
+ InCallState newState = getPotentialStateFromCallList(callList);
newState = startOrFinishUi(newState);
- // finally set the new state before announcing it to the world
+ // Set the new state before announcing it to the world
mInCallState = newState;
// notify listeners of new state
@@ -99,6 +94,22 @@ public class InCallPresenter implements CallList.Listener {
}
}
+ /**
+ * Given the call list, return the state in which the in-call screen should be.
+ */
+ public InCallState getPotentialStateFromCallList(CallList callList) {
+ InCallState newState = InCallState.HIDDEN;
+
+ if (callList.getIncomingCall() != null) {
+ newState = InCallState.INCOMING;
+ } else if (callList.getActiveCall() != null ||
+ callList.getBackgroundCall() != null) {
+ newState = InCallState.INCALL;
+ }
+
+ return newState;
+ }
+
public void addListener(InCallStateListener listener) {
Preconditions.checkNotNull(listener);
mListeners.add(listener);
@@ -115,26 +126,23 @@ public class InCallPresenter implements CallList.Listener {
* It returns a potential new middle state (STARTING_UP) if appropriate.
*/
private InCallState startOrFinishUi(InCallState newState) {
+ Logger.d(this, "startOrFInishUi: " + newState.toString());
+
// TODO(klp): Consider a proper state machine implementation
// if we need to show something, we need to start the Ui...
- if (newState != InCallState.HIDDEN) {
-
- // ...only if the UI is currently hidden
- if (mInCallState == InCallState.HIDDEN) {
- // TODO(klp): Update the flags to match the PhoneApp activity
- final Intent intent = new Intent(mContext, InCallActivity.class);
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
- mContext.startActivity(intent);
-
+ if (!newState.isHidden()) {
+
+ // When we attempt to go to any state from HIDDEN, it means that we need to create the
+ // entire UI. However, the StatusBarNotifier is in charge of starting up the Ui because
+ // it has special behavior in case we have to deal with an immersive foreground app.
+ // We set the STARTING_UP state to let StatusBarNotifier know it needs to start the
+ // the Ui.
+ if (mInCallState.isHidden()) {
return InCallState.STARTING_UP;
}
- // (newState == InCallState.HIDDEN)
- // Else, we need to hide the UI...if it exists
} else if (mInCallActivity != null) {
- mListeners.clear();
-
// Null out reference before we start end sequence
InCallActivity temp = mInCallActivity;
mInCallActivity = null;
@@ -148,7 +156,12 @@ public class InCallPresenter implements CallList.Listener {
/**
* Private constructor. Must use getInstance() to get this singleton.
*/
- private InCallPresenter() {
+ private InCallPresenter(Context context) {
+ Preconditions.checkNotNull(context);
+
+ mStatusBarNotifier = new StatusBarNotifier(context);
+ addListener(mStatusBarNotifier);
+
CallList.getInstance().addListener(this);
}
@@ -156,12 +169,29 @@ public class InCallPresenter implements CallList.Listener {
* All the main states of InCallActivity.
*/
public enum InCallState {
+ // InCall Screen is off and there are no calls
HIDDEN,
+
+ // In call is in the process of starting up
STARTING_UP,
+
+ // In call has started but is not displaying any information
STARTED,
+
+ // Incoming-call screen is up
INCOMING,
- INCALL
- };
+
+ // In-call experience is showing
+ INCALL;
+
+ public boolean isIncoming() {
+ return (this == INCOMING);
+ }
+
+ public boolean isHidden() {
+ return (this == HIDDEN);
+ }
+ }
/**
* Interface implemented by classes that need to know about the InCall State.
diff --git a/InCallUI/src/com/android/incallui/StatusBarNotifier.java b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
new file mode 100644
index 000000000..617be2fac
--- /dev/null
+++ b/InCallUI/src/com/android/incallui/StatusBarNotifier.java
@@ -0,0 +1,267 @@
+/*
+ * 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.Preconditions;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.Context;
+import android.content.Intent;
+
+import com.android.incallui.InCallPresenter.InCallState;
+import com.android.services.telephony.common.Call;
+
+/**
+ * This class adds Notifications to the status bar for the in-call experience.
+ */
+public class StatusBarNotifier implements InCallPresenter.InCallStateListener {
+ // notification types
+ private static final int IN_CALL_NOTIFICATION = 1;
+
+ private final Context mContext;
+ private final NotificationManager mNotificationManager;
+
+ public StatusBarNotifier(Context context) {
+ Preconditions.checkNotNull(context);
+
+ mContext = context;
+ mNotificationManager =
+ (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
+ }
+
+ /**
+ * Creates notifications according to the state we receive from {@link InCallPresenter}.
+ */
+ @Override
+ public void onStateChange(InCallState state, CallList callList) {
+
+ if (InCallState.STARTING_UP == state) {
+ // TODO: This method needs to move somewhere more appropriate for sharing
+ InCallState newState = InCallPresenter.getInstance()
+ .getPotentialStateFromCallList(callList);
+
+ updateNotificationAndLaunchIncomingCallUi(newState);
+ } else {
+ updateInCallNotification(state);
+ }
+ }
+
+ /**
+ * Updates the phone app's status bar notification based on the
+ * current telephony state, or cancels the notification if the phone
+ * is totally idle.
+ *
+ * This method will never actually launch the incoming-call UI.
+ * (Use updateNotificationAndLaunchIncomingCallUi() for that.)
+ */
+ private void updateInCallNotification(InCallState state) {
+ // allowFullScreenIntent=false means *don't* allow the incoming
+ // call UI to be launched.
+ updateInCallNotification(false, state);
+ }
+
+ /**
+ * Updates the phone app's status bar notification *and* launches the
+ * incoming call UI in response to a new incoming call.
+ *
+ * This is just like updateInCallNotification(), with one exception:
+ * If an incoming call is ringing (or call-waiting), the notification
+ * will also include a "fullScreenIntent" that will cause the
+ * InCallScreen to be launched immediately, unless the current
+ * foreground activity is marked as "immersive".
+ *
+ * (This is the mechanism that actually brings up the incoming call UI
+ * when we receive a "new ringing connection" event from the telephony
+ * layer.)
+ *
+ * Watch out: this method should ONLY be called directly from the code
+ * path in CallNotifier that handles the "new ringing connection"
+ * event from the telephony layer. All other places that update the
+ * in-call notification (like for phone state changes) should call
+ * updateInCallNotification() instead. (This ensures that we don't
+ * end up launching the InCallScreen multiple times for a single
+ * incoming call, which could cause slow responsiveness and/or visible
+ * glitches.)
+ *
+ * Also note that this method is safe to call even if the phone isn't
+ * actually ringing (or, more likely, if an incoming call *was*
+ * ringing briefly but then disconnected). In that case, we'll simply
+ * update or cancel the in-call notification based on the current
+ * phone state.
+ *
+ * @see #updateInCallNotification(boolean)
+ */
+ private void updateNotificationAndLaunchIncomingCallUi(InCallState state) {
+ // Set allowFullScreenIntent=true to indicate that we *should*
+ // launch the incoming call UI if necessary.
+ updateInCallNotification(true, state);
+ }
+
+
+ /**
+ * Take down the in-call notification.
+ * @see updateInCallNotification()
+ */
+ private void cancelInCall() {
+ Logger.d(this, "cancelInCall()...");
+ mNotificationManager.cancel(IN_CALL_NOTIFICATION);
+ }
+
+ /**
+ * Helper method for updateInCallNotification() and
+ * updateNotificationAndLaunchIncomingCallUi(): Update the phone app's
+ * status bar notification based on the current telephony state, or
+ * cancels the notification if the phone is totally idle.
+ *
+ * @param allowFullScreenIntent If true, *and* an incoming call is
+ * ringing, the notification will include a "fullScreenIntent"
+ * pointing at the InCallActivity (which will cause the InCallActivity
+ * to be launched.)
+ * Watch out: This should be set to true *only* when directly
+ * handling a new incoming call for the first time.
+ */
+ private void updateInCallNotification(boolean allowFullScreenIntent, InCallState state) {
+ int resId;
+ Logger.d(this, "updateInCallNotification(allowFullScreenIntent = "
+ + allowFullScreenIntent + ")...");
+
+ if (InCallState.INCALL != state && InCallState.INCOMING != state) {
+ cancelInCall();
+ return;
+ }
+
+ final PendingIntent inCallPendingIntent = getLaunchPendingIntent();
+ final Notification.Builder builder = getNotificationBuilder();
+
+ /*
+ * Set up the Intents that will get fires when the user interacts with the notificaiton.
+ */
+ builder.setContentIntent(inCallPendingIntent);
+ if (InCallState.INCOMING == state) {
+ if (allowFullScreenIntent) {
+ configureFullScreenIntent(builder, inCallPendingIntent);
+ }
+ } else if (InCallState.INCALL == state) {
+ addActiveCallIntents(builder);
+ }
+
+
+ /*
+ * Set up notification Ui.
+ */
+ setUpNotificationUi(builder);
+
+
+ /*
+ * Fire off the notification
+ */
+ Notification notification = builder.build();
+ Logger.d(this, "Notifying IN_CALL_NOTIFICATION: " + notification);
+ mNotificationManager.notify(IN_CALL_NOTIFICATION, notification);
+ }
+
+ /**
+ * Sets up the main Ui for the notification
+ */
+ private void setUpNotificationUi(Notification.Builder builder) {
+ // set the content
+ builder.setContentText(mContext.getString(R.string.notification_ongoing_call));
+ builder.setSmallIcon(R.drawable.stat_sys_phone_call);
+ }
+
+ private void addActiveCallIntents(Notification.Builder builder) {
+ Logger.i(this, "Will show \"hang-up\" action in the ongoing active call Notification");
+ // TODO: use better asset.
+ // TODO(klp): uncomment this for "hang-up" capability
+ //builder.addAction(R.drawable.stat_sys_phone_call_end,
+ // mContext.getText(R.string.notification_action_end_call),
+ // PhoneGlobals.createHangUpOngoingCallPendingIntent(mContext));
+ }
+
+ /**
+ * Adds fullscreen intent to the builder.
+ */
+ private void configureFullScreenIntent(Notification.Builder builder, PendingIntent intent) {
+ // Ok, we actually want to launch the incoming call
+ // UI at this point (in addition to simply posting a notification
+ // to the status bar). Setting fullScreenIntent will cause
+ // the InCallScreen to be launched immediately *unless* the
+ // current foreground activity is marked as "immersive".
+ Logger.d(this, "- Setting fullScreenIntent: " + intent);
+ builder.setFullScreenIntent(intent, true);
+
+ // Ugly hack alert:
+ //
+ // The NotificationManager has the (undocumented) behavior
+ // that it will *ignore* the fullScreenIntent field if you
+ // post a new Notification that matches the ID of one that's
+ // already active. Unfortunately this is exactly what happens
+ // when you get an incoming call-waiting call: the
+ // "ongoing call" notification is already visible, so the
+ // InCallScreen won't get launched in this case!
+ // (The result: if you bail out of the in-call UI while on a
+ // call and then get a call-waiting call, the incoming call UI
+ // won't come up automatically.)
+ //
+ // The workaround is to just notice this exact case (this is a
+ // call-waiting call *and* the InCallScreen is not in the
+ // foreground) and manually cancel the in-call notification
+ // before (re)posting it.
+ //
+ // TODO: there should be a cleaner way of avoiding this
+ // problem (see discussion in bug 3184149.)
+
+ // TODO(klp): reenable this for klp
+ /*if (incomingCall.getState() == Call.State.CALL_WAITING) {
+ Logger.i(this, "updateInCallNotification: call-waiting! force relaunch...");
+ // Cancel the IN_CALL_NOTIFICATION immediately before
+ // (re)posting it; this seems to force the
+ // NotificationManager to launch the fullScreenIntent.
+ mNotificationManager.cancel(IN_CALL_NOTIFICATION);
+ }*/
+ }
+
+ private Notification.Builder getNotificationBuilder() {
+ final Notification.Builder builder = new Notification.Builder(mContext);
+ builder.setOngoing(true);
+
+ // Make the notification prioritized over the other normal notifications.
+ builder.setPriority(Notification.PRIORITY_HIGH);
+
+ return builder;
+ }
+
+ private PendingIntent getLaunchPendingIntent() {
+
+ 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);
+
+ // PendingIntent that can be used to launch the InCallActivity. The
+ // system fires off this intent if the user pulls down the windowshade
+ // and clicks the notification's expanded view. It's also used to
+ // launch the InCallActivity immediately when when there's an incoming
+ // call (see the "fullScreenIntent" field below).
+ PendingIntent inCallPendingIntent = PendingIntent.getActivity(mContext, 0, intent, 0);
+
+ return inCallPendingIntent;
+ }
+}