StatusBarNotifier.java revision ccca31529c07970e89419fb85a9e8153a5396838
1ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/*
2ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Copyright (C) 2013 The Android Open Source Project
3ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
4ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Licensed under the Apache License, Version 2.0 (the "License");
5ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * you may not use this file except in compliance with the License.
6ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * You may obtain a copy of the License at
7ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
8ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *      http://www.apache.org/licenses/LICENSE-2.0
9ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian *
10ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * Unless required by applicable law or agreed to in writing, software
11ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * distributed under the License is distributed on an "AS IS" BASIS,
12ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * See the License for the specific language governing permissions and
14ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian * limitations under the License.
15ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian */
16ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
17ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpackage com.android.incallui;
18ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
19ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport static com.android.contacts.common.compat.CallCompat.Details.PROPERTY_ENTERPRISE_CALL;
20ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport static com.android.incallui.NotificationBroadcastReceiver.ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST;
21ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport static com.android.incallui.NotificationBroadcastReceiver.ACTION_ANSWER_VIDEO_INCOMING_CALL;
22ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport static com.android.incallui.NotificationBroadcastReceiver.ACTION_ANSWER_VOICE_INCOMING_CALL;
23ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport static com.android.incallui.NotificationBroadcastReceiver.ACTION_DECLINE_INCOMING_CALL;
24ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport static com.android.incallui.NotificationBroadcastReceiver.ACTION_DECLINE_VIDEO_UPGRADE_REQUEST;
25ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport static com.android.incallui.NotificationBroadcastReceiver.ACTION_HANG_UP_ONGOING_CALL;
26ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
27ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.app.ActivityManager;
28ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.app.Notification;
29ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.app.NotificationManager;
30ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.app.PendingIntent;
31ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.Context;
32ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.content.Intent;
33ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.Bitmap;
34ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.BitmapFactory;
35ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.drawable.BitmapDrawable;
36ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.graphics.drawable.Drawable;
37ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.media.AudioAttributes;
38ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.net.Uri;
39ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Build.VERSION;
40ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.os.Build.VERSION_CODES;
41ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.ColorRes;
42ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.NonNull;
43ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.Nullable;
44ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.StringRes;
45ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.support.annotation.VisibleForTesting;
46ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.telecom.Call.Details;
47ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.telecom.PhoneAccount;
48ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.telecom.TelecomManager;
49ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.text.BidiFormatter;
50ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.text.Spannable;
51ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.text.SpannableString;
52ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.text.TextDirectionHeuristics;
53ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.text.TextUtils;
54ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport android.text.style.ForegroundColorSpan;
55ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.ContactsUtils;
56ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.ContactsUtils.UserType;
57ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.preference.ContactsPreferences;
58ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.util.BitmapUtil;
59ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.contacts.common.util.ContactDisplayUtils;
60ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.common.LogUtil;
61ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.dialer.util.DrawableConverter;
62ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.ContactInfoCache.ContactCacheEntry;
63ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.ContactInfoCache.ContactInfoCacheCallback;
64ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.InCallPresenter.InCallState;
65ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.async.PausableExecutorImpl;
66ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.call.CallList;
67ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.call.DialerCall;
68ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.call.DialerCall.SessionModificationState;
69ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.call.DialerCallListener;
70ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.ringtone.DialerRingtoneManager;
71ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.ringtone.InCallTonePlayer;
72ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport com.android.incallui.ringtone.ToneGeneratorFactory;
73ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianimport java.util.Objects;
74ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
75ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian/** This class adds Notifications to the status bar for the in-call experience. */
76ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanianpublic class StatusBarNotifier implements InCallPresenter.InCallStateListener {
77ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
78ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // Notification types
79ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // Indicates that no notification is currently showing.
80ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int NOTIFICATION_NONE = 0;
81ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // Notification for an active call. This is non-interruptive, but cannot be dismissed.
82ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int NOTIFICATION_IN_CALL = 1;
83ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  // Notification for incoming calls. This is interruptive and will show up as a HUN.
84ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int NOTIFICATION_INCOMING_CALL = 2;
85ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
86ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN = 0;
87ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1;
88ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
89ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static final long[] VIBRATE_PATTERN = new long[] {0, 1000, 1000};
90ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
91ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final Context mContext;
92ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final ContactInfoCache mContactInfoCache;
93ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final NotificationManager mNotificationManager;
94ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private final DialerRingtoneManager mDialerRingtoneManager;
95ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Nullable private ContactsPreferences mContactsPreferences;
96ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private int mCurrentNotification = NOTIFICATION_NONE;
97ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private int mCallState = DialerCall.State.INVALID;
98ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private int mSavedIcon = 0;
99ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private String mSavedContent = null;
100ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Bitmap mSavedLargeIcon;
101ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private String mSavedContentTitle;
102ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Uri mRingtone;
103ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private StatusBarCallListener mStatusBarCallListener;
104ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
105ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public StatusBarNotifier(@NonNull Context context, @NonNull ContactInfoCache contactInfoCache) {
106ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Objects.requireNonNull(context);
107ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContext = context;
108ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContactsPreferences = ContactsPreferencesFactory.newContactsPreferences(mContext);
109ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContactInfoCache = contactInfoCache;
110ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mNotificationManager = context.getSystemService(NotificationManager.class);
111ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mDialerRingtoneManager =
112ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new DialerRingtoneManager(
113ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            new InCallTonePlayer(new ToneGeneratorFactory(), new PausableExecutorImpl()),
114ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            CallList.getInstance());
115ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mCurrentNotification = NOTIFICATION_NONE;
116ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
117ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
118ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
119ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Should only be called from a irrecoverable state where it is necessary to dismiss all
120ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * notifications.
121ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
122ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  static void clearAllCallNotifications(Context backupContext) {
123ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.i(
124ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        StatusBarNotifier.class.getSimpleName(),
125ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        "Something terrible happened. Clear all InCall notifications");
126ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
127ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    NotificationManager notificationManager =
128ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        backupContext.getSystemService(NotificationManager.class);
129ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    notificationManager.cancel(NOTIFICATION_IN_CALL);
130ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    notificationManager.cancel(NOTIFICATION_INCOMING_CALL);
131ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
132ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
133ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static int getWorkStringFromPersonalString(int resId) {
134ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (resId == R.string.notification_ongoing_call) {
135ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return R.string.notification_ongoing_work_call;
136ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (resId == R.string.notification_ongoing_call_wifi) {
137ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return R.string.notification_ongoing_work_call_wifi;
138ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (resId == R.string.notification_incoming_call_wifi) {
139ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return R.string.notification_incoming_work_call_wifi;
140ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (resId == R.string.notification_incoming_call) {
141ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return R.string.notification_incoming_work_call;
142ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
143ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return resId;
144ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
145ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
146ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
147ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
148ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns PendingIntent for answering a phone call. This will typically be used from Notification
149ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * context.
150ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
151ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private static PendingIntent createNotificationPendingIntent(Context context, String action) {
152ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Intent intent = new Intent(action, null, context, NotificationBroadcastReceiver.class);
153ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return PendingIntent.getBroadcast(context, 0, intent, 0);
154ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
155ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
156ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Creates notifications according to the state we receive from {@link InCallPresenter}. */
157ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Override
158ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void onStateChange(InCallState oldState, InCallState newState, CallList callList) {
159ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.d(this, "onStateChange");
160ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    updateNotification(callList);
161ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
162ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
163ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
164ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Updates the phone app's status bar notification *and* launches the incoming call UI in response
165ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * to a new incoming call.
166ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
167ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>If an incoming call is ringing (or call-waiting), the notification will also include a
168ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * "fullScreenIntent" that will cause the InCallScreen to be launched, unless the current
169ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * foreground activity is marked as "immersive".
170ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
171ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>(This is the mechanism that actually brings up the incoming call UI when we receive a "new
172ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * ringing connection" event from the telephony layer.)
173ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
174ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * <p>Also note that this method is safe to call even if the phone isn't actually ringing (or,
175ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * more likely, if an incoming call *was* ringing briefly but then disconnected). In that case,
176ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * we'll simply update or cancel the in-call notification based on the current phone state.
177ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
178ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @see #updateInCallNotification(CallList)
179ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
180ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  public void updateNotification(CallList callList) {
181ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    updateInCallNotification(callList);
182ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
183ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
184ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
185ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Take down the in-call notification.
186ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   *
187ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * @see #updateInCallNotification(CallList)
188ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
189ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void cancelNotification() {
190ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mStatusBarCallListener != null) {
191ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      setStatusBarCallListener(null);
192ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
193ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mCurrentNotification != NOTIFICATION_NONE) {
194ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Log.d(this, "cancelInCall()...");
195ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mNotificationManager.cancel(mCurrentNotification);
196ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
197ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mCurrentNotification = NOTIFICATION_NONE;
198ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
199ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
200ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
201ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Helper method for updateInCallNotification() and updateNotification(): Update the phone app's
202ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * status bar notification based on the current telephony state, or cancels the notification if
203ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * the phone is totally idle.
204ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
205ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void updateInCallNotification(CallList callList) {
206ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.d(this, "updateInCallNotification...");
207ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
208ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final DialerCall call = getCallToShow(callList);
209ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
210ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call != null) {
211ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      showNotification(callList, call);
212ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
213ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      cancelNotification();
214ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
215ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
216ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
217ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void showNotification(final CallList callList, final DialerCall call) {
218ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final boolean isIncoming =
219ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        (call.getState() == DialerCall.State.INCOMING
220ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || call.getState() == DialerCall.State.CALL_WAITING);
221ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    setStatusBarCallListener(new StatusBarCallListener(call));
222ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
223ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // we make a call to the contact info cache to query for supplemental data to what the
224ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // call provides.  This includes the contact name and photo.
225ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // This callback will always get called immediately and synchronously with whatever data
226ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // it has available, and may make a subsequent call later (same thread) if it had to
227ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // call into the contacts provider for more data.
228ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mContactInfoCache.findInfo(
229ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        call,
230ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        isIncoming,
231ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        new ContactInfoCacheCallback() {
232ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          @Override
233ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          public void onContactInfoComplete(String callId, ContactCacheEntry entry) {
234ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            DialerCall call = callList.getCallById(callId);
235ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            if (call != null) {
236ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              call.getLogState().contactLookupResult = entry.contactLookupResult;
237ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              buildAndSendNotification(callList, call, entry);
238ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
239ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
240ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
241ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          @Override
242ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          public void onImageLoadComplete(String callId, ContactCacheEntry entry) {
243ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            DialerCall call = callList.getCallById(callId);
244ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            if (call != null) {
245ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              buildAndSendNotification(callList, call, entry);
246ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            }
247ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          }
248ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        });
249ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
250ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
251ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Sets up the main Ui for the notification */
252ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void buildAndSendNotification(
253ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      CallList callList, DialerCall originalCall, ContactCacheEntry contactInfo) {
254ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // This can get called to update an existing notification after contact information has come
255ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // back. However, it can happen much later. Before we continue, we need to make sure that
256ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // the call being passed in is still the one we want to show in the notification.
257ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final DialerCall call = getCallToShow(callList);
258ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call == null || !call.getId().equals(originalCall.getId())) {
259ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return;
260ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
261ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
262ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int callState = call.getState();
263ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
264ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Check if data has changed; if nothing is different, don't issue another notification.
265ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int iconResId = getIconToDisplay(call);
266ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Bitmap largeIcon = getLargeIconToDisplay(contactInfo, call);
267ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final String content = getContentString(call, contactInfo.userType);
268ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final String contentTitle = getContentTitle(contactInfo, call);
269ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
270ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final boolean isVideoUpgradeRequest =
271ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        call.getSessionModificationState()
272ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST;
273ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int notificationType;
274ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (callState == DialerCall.State.INCOMING
275ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        || callState == DialerCall.State.CALL_WAITING
276ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        || isVideoUpgradeRequest) {
277ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      notificationType = NOTIFICATION_INCOMING_CALL;
278ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
279ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      notificationType = NOTIFICATION_IN_CALL;
280ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
281ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
282ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (!checkForChangeAndSaveData(
283ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        iconResId,
284ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        content,
285ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        largeIcon,
286ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        contentTitle,
287ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        callState,
288ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        notificationType,
289ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        contactInfo.contactRingtoneUri)) {
290ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return;
291ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
292ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
293ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (largeIcon != null) {
294ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      largeIcon = getRoundedIcon(largeIcon);
295ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
296ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
297ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // This builder is used for the notification shown when the device is locked and the user
298ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // has set their notification settings to 'hide sensitive content'
299ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // {@see Notification.Builder#setPublicVersion}.
300ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Notification.Builder publicBuilder = new Notification.Builder(mContext);
301ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    publicBuilder
302ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        .setSmallIcon(iconResId)
303ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        .setColor(mContext.getResources().getColor(R.color.dialer_theme_color))
304ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // Hide work call state for the lock screen notification
305ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        .setContentTitle(getContentString(call, ContactsUtils.USER_TYPE_CURRENT));
306ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    setNotificationWhen(call, callState, publicBuilder);
307ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
308ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Builder for the notification shown when the device is unlocked or the user has set their
309ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // notification settings to 'show all notification content'.
310ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Notification.Builder builder = getNotificationBuilder();
311ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setPublicVersion(publicBuilder.build());
312ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
313ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Set up the main intent to send the user to the in-call screen
314ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setContentIntent(
315ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        createLaunchPendingIntent(false /* isFullScreen */, call.isVideoCall()));
316ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
317ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Set the intent as a full screen intent as well if a call is incoming
318ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (notificationType == NOTIFICATION_INCOMING_CALL) {
319ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (!InCallPresenter.getInstance().isActivityStarted()) {
320ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        configureFullScreenIntent(
321ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            builder,
322ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            createLaunchPendingIntent(true /* isFullScreen */, call.isVideoCall()),
323ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            callList,
324ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            call);
325ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } else {
326ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // If the incall screen is already up, we don't want to show HUN but regular notification
327ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // should still be shown. In order to do that the previous one with full screen intent
328ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        // needs to be cancelled.
329ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        LogUtil.d(
330ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            "StatusBarNotifier.buildAndSendNotification",
331ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            "cancel previous incoming call notification");
332ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mNotificationManager.cancel(NOTIFICATION_INCOMING_CALL);
333ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
334ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Set the notification category for incoming calls
335ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      builder.setCategory(Notification.CATEGORY_CALL);
336ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
337ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
338ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Set the content
339ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setContentText(content);
340ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setSmallIcon(iconResId);
341ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setContentTitle(contentTitle);
342ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setLargeIcon(largeIcon);
343ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setColor(mContext.getResources().getColor(R.color.dialer_theme_color));
344ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
345ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (isVideoUpgradeRequest) {
346ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      builder.setUsesChronometer(false);
347ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      addDismissUpgradeRequestAction(builder);
348ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      addAcceptUpgradeRequestAction(builder);
349ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
350ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      createIncomingCallNotification(call, callState, builder);
351ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
352ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
353ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    addPersonReference(builder, contactInfo, call);
354ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
355ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Fire off the notification
356ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Notification notification = builder.build();
357ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
358ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mDialerRingtoneManager.shouldPlayRingtone(callState, contactInfo.contactRingtoneUri)) {
359ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      notification.flags |= Notification.FLAG_INSISTENT;
360ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      notification.sound = contactInfo.contactRingtoneUri;
361ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      AudioAttributes.Builder audioAttributes = new AudioAttributes.Builder();
362ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      audioAttributes.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC);
363ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      audioAttributes.setUsage(AudioAttributes.USAGE_NOTIFICATION_RINGTONE);
364ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      notification.audioAttributes = audioAttributes.build();
365ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (mDialerRingtoneManager.shouldVibrate(mContext.getContentResolver())) {
366ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        notification.vibrate = VIBRATE_PATTERN;
367ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
368ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
369ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mDialerRingtoneManager.shouldPlayCallWaitingTone(callState)) {
370ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Log.v(this, "Playing call waiting tone");
371ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mDialerRingtoneManager.playCallWaitingTone();
372ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
373ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mCurrentNotification != notificationType && mCurrentNotification != NOTIFICATION_NONE) {
374ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Log.i(this, "Previous notification already showing - cancelling " + mCurrentNotification);
375ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mNotificationManager.cancel(mCurrentNotification);
376ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
377ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
378ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.i(this, "Displaying notification for " + notificationType);
379ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    try {
380ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mNotificationManager.notify(notificationType, notification);
381ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } catch (RuntimeException e) {
382ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // TODO(b/34744003): Move the memory stats into silent feedback PSD.
383ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      ActivityManager activityManager = mContext.getSystemService(ActivityManager.class);
384ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      ActivityManager.MemoryInfo memoryInfo = new ActivityManager.MemoryInfo();
385ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      activityManager.getMemoryInfo(memoryInfo);
386ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      throw new RuntimeException(
387ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          String.format(
388ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              "Error displaying notification with photo type: %d (low memory? %b, availMem: %d)",
389ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              contactInfo.photoType, memoryInfo.lowMemory, memoryInfo.availMem),
390ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          e);
391ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
392ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    call.getLatencyReport().onNotificationShown();
393ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mCurrentNotification = notificationType;
394ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
395ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
396ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void createIncomingCallNotification(
397ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      DialerCall call, int state, Notification.Builder builder) {
398ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    setNotificationWhen(call, state, builder);
399ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
400ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Add hang up option for any active calls (active | onhold), outgoing calls (dialing).
401ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (state == DialerCall.State.ACTIVE
402ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        || state == DialerCall.State.ONHOLD
403ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        || DialerCall.State.isDialing(state)) {
404ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      addHangupAction(builder);
405ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (state == DialerCall.State.INCOMING || state == DialerCall.State.CALL_WAITING) {
406ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      addDismissAction(builder);
407ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (call.isVideoCall()) {
408ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        addVideoCallAction(builder);
409ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } else {
410ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        addAnswerAction(builder);
411ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
412ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
413ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
414ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
415ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
416ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Sets the notification's when section as needed. For active calls, this is explicitly set as the
417ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * duration of the call. For all other states, the notification will automatically show the time
418ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * at which the notification was created.
419ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
420ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void setNotificationWhen(DialerCall call, int state, Notification.Builder builder) {
421ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (state == DialerCall.State.ACTIVE) {
422ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      builder.setUsesChronometer(true);
423ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      builder.setWhen(call.getConnectTimeMillis());
424ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else {
425ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      builder.setUsesChronometer(false);
426ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
427ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
428ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
429ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
430ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Checks the new notification data and compares it against any notification that we are already
431ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * displaying. If the data is exactly the same, we return false so that we do not issue a new
432ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * notification for the exact same data.
433ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
434ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private boolean checkForChangeAndSaveData(
435ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      int icon,
436ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      String content,
437ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Bitmap largeIcon,
438ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      String contentTitle,
439ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      int state,
440ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      int notificationType,
441ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Uri ringtone) {
442ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
443ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // The two are different:
444ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // if new title is not null, it should be different from saved version OR
445ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // if new title is null, the saved version should not be null
446ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final boolean contentTitleChanged =
447ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        (contentTitle != null && !contentTitle.equals(mSavedContentTitle))
448ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || (contentTitle == null && mSavedContentTitle != null);
449ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
450ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // any change means we are definitely updating
451ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    boolean retval =
452ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        (mSavedIcon != icon)
453ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || !Objects.equals(mSavedContent, content)
454ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || (mCallState != state)
455ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || (mSavedLargeIcon != largeIcon)
456ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || contentTitleChanged
457ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || !Objects.equals(mRingtone, ringtone);
458ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
459ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // If we aren't showing a notification right now or the notification type is changing,
460ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // definitely do an update.
461ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mCurrentNotification != notificationType) {
462ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (mCurrentNotification == NOTIFICATION_NONE) {
463ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        Log.d(this, "Showing notification for first time.");
464ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
465ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      retval = true;
466ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
467ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
468ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mSavedIcon = icon;
469ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mSavedContent = content;
470ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mCallState = state;
471ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mSavedLargeIcon = largeIcon;
472ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mSavedContentTitle = contentTitle;
473ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mRingtone = ringtone;
474ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
475ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (retval) {
476ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Log.d(this, "Data changed.  Showing notification");
477ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
478ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
479ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return retval;
480ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
481ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
482ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Returns the main string to use in the notification. */
483ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @VisibleForTesting
484ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  @Nullable
485ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  String getContentTitle(ContactCacheEntry contactInfo, DialerCall call) {
486ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.isConferenceCall() && !call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
487ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return mContext.getResources().getString(R.string.conference_call_name);
488ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
489ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
490ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    String preferredName =
491ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        ContactDisplayUtils.getPreferredDisplayName(
492ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            contactInfo.namePrimary, contactInfo.nameAlternative, mContactsPreferences);
493ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (TextUtils.isEmpty(preferredName)) {
494ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return TextUtils.isEmpty(contactInfo.number)
495ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          ? null
496ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          : BidiFormatter.getInstance()
497ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian              .unicodeWrap(contactInfo.number, TextDirectionHeuristics.LTR);
498ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
499ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return preferredName;
500ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
501ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
502ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void addPersonReference(
503ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Notification.Builder builder, ContactCacheEntry contactInfo, DialerCall call) {
504ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Query {@link Contacts#CONTENT_LOOKUP_URI} directly with work lookup key is not allowed.
505ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // So, do not pass {@link Contacts#CONTENT_LOOKUP_URI} to NotificationManager to avoid
506ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // NotificationManager using it.
507ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (contactInfo.lookupUri != null && contactInfo.userType != ContactsUtils.USER_TYPE_WORK) {
508ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      builder.addPerson(contactInfo.lookupUri.toString());
509ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (!TextUtils.isEmpty(call.getNumber())) {
510ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      builder.addPerson(Uri.fromParts(PhoneAccount.SCHEME_TEL, call.getNumber(), null).toString());
511ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
512ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
513ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
514ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Gets a large icon from the contact info object to display in the notification. */
515ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Bitmap getLargeIconToDisplay(ContactCacheEntry contactInfo, DialerCall call) {
516ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Bitmap largeIcon = null;
517ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.isConferenceCall() && !call.hasProperty(Details.PROPERTY_GENERIC_CONFERENCE)) {
518ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      largeIcon = BitmapFactory.decodeResource(mContext.getResources(), R.drawable.img_conference);
519ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
520ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (contactInfo.photo != null && (contactInfo.photo instanceof BitmapDrawable)) {
521ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      largeIcon = ((BitmapDrawable) contactInfo.photo).getBitmap();
522ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
523ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.isSpam()) {
524ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Drawable drawable = mContext.getResources().getDrawable(R.drawable.blocked_contact);
525ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      largeIcon = DrawableConverter.drawableToBitmap(drawable);
526ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
527ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return largeIcon;
528ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
529ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
530ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Bitmap getRoundedIcon(Bitmap bitmap) {
531ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (bitmap == null) {
532ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return null;
533ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
534ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int height =
535ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        (int) mContext.getResources().getDimension(android.R.dimen.notification_large_icon_height);
536ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final int width =
537ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        (int) mContext.getResources().getDimension(android.R.dimen.notification_large_icon_width);
538ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return BitmapUtil.getRoundedBitmap(bitmap, width, height);
539ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
540ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
541ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /**
542ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * Returns the appropriate icon res Id to display based on the call for which we want to display
543ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   * information.
544ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian   */
545ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private int getIconToDisplay(DialerCall call) {
546ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Even if both lines are in use, we only show a single item in
547ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // the expanded Notifications UI.  It's labeled "Ongoing call"
548ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // (or "On hold" if there's only one call, and it's on hold.)
549ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Also, we don't have room to display caller-id info from two
550ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // different calls.  So if both lines are in use, display info
551ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // from the foreground call.  And if there's a ringing call,
552ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // display that regardless of the state of the other calls.
553ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.getState() == DialerCall.State.ONHOLD) {
554ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return R.drawable.ic_phone_paused_white_24dp;
555ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (call.getSessionModificationState()
556ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
557ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return R.drawable.ic_videocam;
558ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
559ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return R.anim.on_going_call;
560ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
561ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
562ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Returns the message to use with the notification. */
563ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private String getContentString(DialerCall call, @UserType long userType) {
564ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    boolean isIncomingOrWaiting =
565ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        call.getState() == DialerCall.State.INCOMING
566ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || call.getState() == DialerCall.State.CALL_WAITING;
567ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
568ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (isIncomingOrWaiting
569ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        && call.getNumberPresentation() == TelecomManager.PRESENTATION_ALLOWED) {
570ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
571ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (!TextUtils.isEmpty(call.getChildNumber())) {
572ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        return mContext.getString(R.string.child_number, call.getChildNumber());
573ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } else if (!TextUtils.isEmpty(call.getCallSubject()) && call.isCallSubjectSupported()) {
574ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        return call.getCallSubject();
575ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
576ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
577ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
578ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    int resId = R.string.notification_ongoing_call;
579ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call.hasProperty(Details.PROPERTY_WIFI)) {
580ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      resId = R.string.notification_ongoing_call_wifi;
581ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
582ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
583ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (isIncomingOrWaiting) {
584ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (call.hasProperty(Details.PROPERTY_WIFI)) {
585ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        resId = R.string.notification_incoming_call_wifi;
586ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      } else {
587ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        if (call.isSpam()) {
588ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          resId = R.string.notification_incoming_spam_call;
589ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        } else {
590ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          resId = R.string.notification_incoming_call;
591ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        }
592ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
593ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (call.getState() == DialerCall.State.ONHOLD) {
594ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      resId = R.string.notification_on_hold;
595ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (DialerCall.State.isDialing(call.getState())) {
596ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      resId = R.string.notification_dialing;
597ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    } else if (call.getSessionModificationState()
598ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        == DialerCall.SESSION_MODIFICATION_STATE_RECEIVED_UPGRADE_TO_VIDEO_REQUEST) {
599ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      resId = R.string.notification_requesting_video_call;
600ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
601ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
602ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Is the call placed through work connection service.
603ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    boolean isWorkCall = call.hasProperty(PROPERTY_ENTERPRISE_CALL);
604ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (userType == ContactsUtils.USER_TYPE_WORK || isWorkCall) {
605ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      resId = getWorkStringFromPersonalString(resId);
606ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
607ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
608ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return mContext.getString(resId);
609ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
610ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
611ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Gets the most relevant call to display in the notification. */
612ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private DialerCall getCallToShow(CallList callList) {
613ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (callList == null) {
614ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      return null;
615ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
616ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    DialerCall call = callList.getIncomingCall();
617ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call == null) {
618ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call = callList.getOutgoingCall();
619ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
620ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call == null) {
621ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call = callList.getVideoUpgradeRequestCall();
622ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
623ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (call == null) {
624ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      call = callList.getActiveOrBackgroundCall();
625ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
626ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return call;
627ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
628ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
629ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Spannable getActionText(@StringRes int stringRes, @ColorRes int colorRes) {
630ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Spannable spannable = new SpannableString(mContext.getText(stringRes));
631ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (VERSION.SDK_INT >= VERSION_CODES.N_MR1) {
632ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // This will only work for cases where the Notification.Builder has a fullscreen intent set
633ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Notification.Builder that does not have a full screen intent will take the color of the
634ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // app and the following leads to a no-op.
635ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      spannable.setSpan(
636ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian          new ForegroundColorSpan(mContext.getColor(colorRes)), 0, spannable.length(), 0);
637ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
638ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return spannable;
639ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
640ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
641ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void addAnswerAction(Notification.Builder builder) {
642ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.d(this, "Will show \"answer\" action in the incoming call Notification");
643ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    PendingIntent answerVoicePendingIntent =
644ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        createNotificationPendingIntent(mContext, ACTION_ANSWER_VOICE_INCOMING_CALL);
645ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.addAction(
646ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        R.anim.on_going_call,
647ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getActionText(R.string.notification_action_answer, R.color.notification_action_accept),
648ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        answerVoicePendingIntent);
649ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
650ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
651ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void addDismissAction(Notification.Builder builder) {
652ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.d(this, "Will show \"decline\" action in the incoming call Notification");
653ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    PendingIntent declinePendingIntent =
654ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        createNotificationPendingIntent(mContext, ACTION_DECLINE_INCOMING_CALL);
655ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.addAction(
656ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        R.drawable.ic_close_dk,
657ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getActionText(R.string.notification_action_dismiss, R.color.notification_action_dismiss),
658ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        declinePendingIntent);
659ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
660ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
661ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void addHangupAction(Notification.Builder builder) {
662ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.d(this, "Will show \"hang-up\" action in the ongoing active call Notification");
663ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    PendingIntent hangupPendingIntent =
664ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        createNotificationPendingIntent(mContext, ACTION_HANG_UP_ONGOING_CALL);
665ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.addAction(
666ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        R.drawable.ic_call_end_white_24dp,
667ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getActionText(R.string.notification_action_end_call, R.color.notification_action_end_call),
668ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        hangupPendingIntent);
669ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
670ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
671ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void addVideoCallAction(Notification.Builder builder) {
672ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.i(this, "Will show \"video\" action in the incoming call Notification");
673ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    PendingIntent answerVideoPendingIntent =
674ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        createNotificationPendingIntent(mContext, ACTION_ANSWER_VIDEO_INCOMING_CALL);
675ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.addAction(
676ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        R.drawable.ic_videocam,
677ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getActionText(
678ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            R.string.notification_action_answer_video, R.color.notification_action_answer_video),
679ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        answerVideoPendingIntent);
680ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
681ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
682ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void addAcceptUpgradeRequestAction(Notification.Builder builder) {
683ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.i(this, "Will show \"accept upgrade\" action in the incoming call Notification");
684ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    PendingIntent acceptVideoPendingIntent =
685ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        createNotificationPendingIntent(mContext, ACTION_ACCEPT_VIDEO_UPGRADE_REQUEST);
686ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.addAction(
687ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        R.drawable.ic_videocam,
688ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getActionText(R.string.notification_action_accept, R.color.notification_action_accept),
689ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        acceptVideoPendingIntent);
690ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
691ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
692ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void addDismissUpgradeRequestAction(Notification.Builder builder) {
693ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.i(this, "Will show \"dismiss upgrade\" action in the incoming call Notification");
694ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    PendingIntent declineVideoPendingIntent =
695ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        createNotificationPendingIntent(mContext, ACTION_DECLINE_VIDEO_UPGRADE_REQUEST);
696ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.addAction(
697ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        R.drawable.ic_videocam,
698ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        getActionText(R.string.notification_action_dismiss, R.color.notification_action_dismiss),
699ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        declineVideoPendingIntent);
700ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
701ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
702ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  /** Adds fullscreen intent to the builder. */
703ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void configureFullScreenIntent(
704ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Notification.Builder builder, PendingIntent intent, CallList callList, DialerCall call) {
705ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Ok, we actually want to launch the incoming call
706ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // UI at this point (in addition to simply posting a notification
707ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // to the status bar).  Setting fullScreenIntent will cause
708ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // the InCallScreen to be launched immediately *unless* the
709ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // current foreground activity is marked as "immersive".
710ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Log.d(this, "- Setting fullScreenIntent: " + intent);
711ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setFullScreenIntent(intent, true);
712ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
713ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Ugly hack alert:
714ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    //
715ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // The NotificationManager has the (undocumented) behavior
716ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // that it will *ignore* the fullScreenIntent field if you
717ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // post a new Notification that matches the ID of one that's
718ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // already active.  Unfortunately this is exactly what happens
719ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // when you get an incoming call-waiting call:  the
720ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // "ongoing call" notification is already visible, so the
721ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // InCallScreen won't get launched in this case!
722ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // (The result: if you bail out of the in-call UI while on a
723ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // call and then get a call-waiting call, the incoming call UI
724ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // won't come up automatically.)
725ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    //
726ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // The workaround is to just notice this exact case (this is a
727ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // call-waiting call *and* the InCallScreen is not in the
728ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // foreground) and manually cancel the in-call notification
729ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // before (re)posting it.
730ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    //
731ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // TODO: there should be a cleaner way of avoiding this
732ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // problem (see discussion in bug 3184149.)
733ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
734ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // If a call is onhold during an incoming call, the call actually comes in as
735ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // INCOMING.  For that case *and* traditional call-waiting, we want to
736ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // cancel the notification.
737ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    boolean isCallWaiting =
738ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        (call.getState() == DialerCall.State.CALL_WAITING
739ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            || (call.getState() == DialerCall.State.INCOMING
740ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian                && callList.getBackgroundCall() != null));
741ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
742ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (isCallWaiting) {
743ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      Log.i(this, "updateInCallNotification: call-waiting! force relaunch...");
744ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Cancel the IN_CALL_NOTIFICATION immediately before
745ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // (re)posting it; this seems to force the
746ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // NotificationManager to launch the fullScreenIntent.
747ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mNotificationManager.cancel(NOTIFICATION_IN_CALL);
748ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
749ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
750ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
751ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private Notification.Builder getNotificationBuilder() {
752ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    final Notification.Builder builder = new Notification.Builder(mContext);
753ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setOngoing(true);
754ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
755ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // Make the notification prioritized over the other normal notifications.
756ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    builder.setPriority(Notification.PRIORITY_HIGH);
757ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
758ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return builder;
759ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
760ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
761ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private PendingIntent createLaunchPendingIntent(boolean isFullScreen, boolean isVideoCall) {
762ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    Intent intent =
763ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        InCallActivity.getIntent(
764ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            mContext,
765ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            false /* showDialpad */,
766ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            false /* newOutgoingCall */,
767ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            isVideoCall,
768ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian            isFullScreen);
769ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
770ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    int requestCode = PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN;
771ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (isFullScreen) {
772ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // Use a unique request code so that the pending intent isn't clobbered by the
773ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      // non-full screen pending intent.
774ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      requestCode = PENDING_INTENT_REQUEST_CODE_FULL_SCREEN;
775ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
776ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
777ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // PendingIntent that can be used to launch the InCallActivity.  The
778ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // system fires off this intent if the user pulls down the windowshade
779ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // and clicks the notification's expanded view.  It's also used to
780ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // launch the InCallActivity immediately when when there's an incoming
781ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    // call (see the "fullScreenIntent" field below).
782ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    return PendingIntent.getActivity(mContext, requestCode, intent, 0);
783ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
784ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
785ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private void setStatusBarCallListener(StatusBarCallListener listener) {
786ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    if (mStatusBarCallListener != null) {
787ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mStatusBarCallListener.cleanup();
788ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
789ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    mStatusBarCallListener = listener;
790ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
791ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
792ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  private class StatusBarCallListener implements DialerCallListener {
793ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
794ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    private DialerCall mDialerCall;
795ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
796ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    StatusBarCallListener(DialerCall dialerCall) {
797ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mDialerCall = dialerCall;
798ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mDialerCall.addListener(this);
799ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
800ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
801ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    void cleanup() {
802ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      mDialerCall.removeListener(this);
803ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
804ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
805ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
806ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallDisconnect() {}
807ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
808ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
809ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallUpdate() {
810ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (CallList.getInstance().getIncomingCall() == null) {
811ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        mDialerRingtoneManager.stopCallWaitingTone();
812ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
813ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
814ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
815ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
816ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallChildNumberChange() {}
817ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
818ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
819ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallLastForwardedNumberChange() {}
820ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
821ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
822ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallUpgradeToVideo() {}
823ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
824ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
825ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onWiFiToLteHandover() {}
826ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
827ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
828ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onHandoverToWifiFailure() {}
829ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian
830ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    /**
831ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * Responds to changes in the session modification state for the call by dismissing the status
832ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     * bar notification as required.
833ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian     */
834ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    @Override
835ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    public void onDialerCallSessionModificationStateChange(@SessionModificationState int state) {
836ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      if (state == DialerCall.SESSION_MODIFICATION_STATE_NO_REQUEST) {
837ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        cleanup();
838ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian        updateNotification(CallList.getInstance());
839ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian      }
840ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian    }
841ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian  }
842ccca31529c07970e89419fb85a9e8153a5396838Eric Erfanian}
843