blob: 01dc1bb3619f19bb055eacbf9ad0eac0f2604f5e [file] [log] [blame]
Eric Erfanianccca3152017-02-22 16:32:36 -08001/*
2 * Copyright (C) 2016 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.incallui;
18
19import android.app.ActivityManager;
20import android.app.ActivityManager.AppTask;
21import android.app.ActivityManager.TaskDescription;
22import android.app.AlertDialog;
23import android.app.Dialog;
Eric Erfanianccca3152017-02-22 16:32:36 -080024import android.content.Context;
25import android.content.DialogInterface;
26import android.content.DialogInterface.OnCancelListener;
27import android.content.DialogInterface.OnDismissListener;
28import android.content.Intent;
29import android.content.res.Configuration;
30import android.content.res.Resources;
31import android.os.Bundle;
32import android.support.annotation.IntDef;
33import android.support.annotation.NonNull;
34import android.support.annotation.Nullable;
35import android.support.v4.app.Fragment;
36import android.support.v4.app.FragmentManager;
37import android.support.v4.app.FragmentTransaction;
38import android.support.v4.content.res.ResourcesCompat;
39import android.telecom.DisconnectCause;
40import android.telecom.PhoneAccountHandle;
41import android.text.TextUtils;
42import android.util.Pair;
43import android.view.KeyEvent;
44import android.view.View;
45import android.view.Window;
46import android.view.WindowManager;
47import android.view.animation.Animation;
48import android.view.animation.AnimationUtils;
49import android.widget.CheckBox;
50import android.widget.Toast;
51import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
52import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
53import com.android.dialer.animation.AnimUtils;
54import com.android.dialer.animation.AnimationListenerAdapter;
55import com.android.dialer.common.LogUtil;
56import com.android.dialer.compat.CompatUtils;
57import com.android.dialer.logging.Logger;
Eric Erfanian8369df02017-05-03 10:27:13 -070058import com.android.dialer.logging.ScreenEvent;
Eric Erfanianccca3152017-02-22 16:32:36 -080059import com.android.dialer.util.ViewUtil;
Eric Erfanian8369df02017-05-03 10:27:13 -070060import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080061import com.android.incallui.call.CallList;
62import com.android.incallui.call.DialerCall;
63import com.android.incallui.call.DialerCall.State;
64import com.android.incallui.call.TelecomAdapter;
65import com.android.incallui.wifi.EnableWifiCallingPrompt;
66import java.lang.annotation.Retention;
67import java.lang.annotation.RetentionPolicy;
68import java.util.ArrayList;
69import java.util.List;
70
71/** Shared functionality between the new and old in call activity. */
72public class InCallActivityCommon {
73
74 private static final String INTENT_EXTRA_SHOW_DIALPAD = "InCallActivity.show_dialpad";
75 private static final String INTENT_EXTRA_NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
76 private static final String INTENT_EXTRA_FOR_FULL_SCREEN =
77 "InCallActivity.for_full_screen_intent";
78
79 private static final String DIALPAD_TEXT_KEY = "InCallActivity.dialpad_text";
80
81 private static final String TAG_SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment";
82 private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment";
83
84 @Retention(RetentionPolicy.SOURCE)
85 @IntDef({
86 DIALPAD_REQUEST_NONE,
87 DIALPAD_REQUEST_SHOW,
88 DIALPAD_REQUEST_HIDE,
89 })
90 @interface DialpadRequestType {}
91
92 private static final int DIALPAD_REQUEST_NONE = 1;
93 private static final int DIALPAD_REQUEST_SHOW = 2;
94 private static final int DIALPAD_REQUEST_HIDE = 3;
95
96 private final InCallActivity inCallActivity;
97 private boolean dismissKeyguard;
98 private boolean showPostCharWaitDialogOnResume;
99 private String showPostCharWaitDialogCallId;
100 private String showPostCharWaitDialogChars;
101 private Dialog dialog;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700102 private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800103 private InCallOrientationEventListener inCallOrientationEventListener;
104 private Animation dialpadSlideInAnimation;
105 private Animation dialpadSlideOutAnimation;
106 private boolean animateDialpadOnShow;
107 private String dtmfTextToPreopulate;
108 @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
109
110 private SelectPhoneAccountListener selectAccountListener =
111 new SelectPhoneAccountListener() {
112 @Override
113 public void onPhoneAccountSelected(
114 PhoneAccountHandle selectedAccountHandle, boolean setDefault, String callId) {
115 DialerCall call = CallList.getInstance().getCallById(callId);
116 LogUtil.i(
117 "InCallActivityCommon.SelectPhoneAccountListener.onPhoneAccountSelected",
118 "call: " + call);
119 if (call != null) {
120 call.phoneAccountSelected(selectedAccountHandle, setDefault);
121 }
122 }
123
124 @Override
125 public void onDialogDismissed(String callId) {
126 DialerCall call = CallList.getInstance().getCallById(callId);
127 LogUtil.i(
128 "InCallActivityCommon.SelectPhoneAccountListener.onDialogDismissed",
129 "disconnecting call: " + call);
130 if (call != null) {
131 call.disconnect();
132 }
133 }
134 };
135
136 public static void setIntentExtras(
137 Intent intent, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
138 if (showDialpad) {
139 intent.putExtra(INTENT_EXTRA_SHOW_DIALPAD, true);
140 }
141 intent.putExtra(INTENT_EXTRA_NEW_OUTGOING_CALL, newOutgoingCall);
142 intent.putExtra(INTENT_EXTRA_FOR_FULL_SCREEN, isForFullScreen);
143 }
144
145 public InCallActivityCommon(InCallActivity inCallActivity) {
146 this.inCallActivity = inCallActivity;
147 }
148
149 public void onCreate(Bundle icicle) {
150 // set this flag so this activity will stay in front of the keyguard
151 // Have the WindowManager filter out touch events that are "too fat".
152 int flags =
153 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
154 | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
155 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
156
157 inCallActivity.getWindow().addFlags(flags);
158
159 inCallActivity.setContentView(R.layout.incall_screen);
160
161 internalResolveIntent(inCallActivity.getIntent());
162
163 boolean isLandscape =
164 inCallActivity.getResources().getConfiguration().orientation
165 == Configuration.ORIENTATION_LANDSCAPE;
166 boolean isRtl = ViewUtil.isRtl();
167
168 if (isLandscape) {
169 dialpadSlideInAnimation =
170 AnimationUtils.loadAnimation(
171 inCallActivity, isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
172 dialpadSlideOutAnimation =
173 AnimationUtils.loadAnimation(
174 inCallActivity,
175 isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
176 } else {
177 dialpadSlideInAnimation =
178 AnimationUtils.loadAnimation(inCallActivity, R.anim.dialpad_slide_in_bottom);
179 dialpadSlideOutAnimation =
180 AnimationUtils.loadAnimation(inCallActivity, R.anim.dialpad_slide_out_bottom);
181 }
182
183 dialpadSlideInAnimation.setInterpolator(AnimUtils.EASE_IN);
184 dialpadSlideOutAnimation.setInterpolator(AnimUtils.EASE_OUT);
185
186 dialpadSlideOutAnimation.setAnimationListener(
187 new AnimationListenerAdapter() {
188 @Override
189 public void onAnimationEnd(Animation animation) {
190 performHideDialpadFragment();
191 }
192 });
193
194 if (icicle != null) {
195 // If the dialpad was shown before, set variables indicating it should be shown and
196 // populated with the previous DTMF text. The dialpad is actually shown and populated
197 // in onResume() to ensure the hosting fragment has been inflated and is ready to receive it.
198 if (icicle.containsKey(INTENT_EXTRA_SHOW_DIALPAD)) {
199 boolean showDialpad = icicle.getBoolean(INTENT_EXTRA_SHOW_DIALPAD);
200 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
201 animateDialpadOnShow = false;
202 }
203 dtmfTextToPreopulate = icicle.getString(DIALPAD_TEXT_KEY);
204
205 SelectPhoneAccountDialogFragment dialogFragment =
206 (SelectPhoneAccountDialogFragment)
207 inCallActivity.getFragmentManager().findFragmentByTag(TAG_SELECT_ACCOUNT_FRAGMENT);
208 if (dialogFragment != null) {
209 dialogFragment.setListener(selectAccountListener);
210 }
211 }
212
213 inCallOrientationEventListener = new InCallOrientationEventListener(inCallActivity);
214 }
215
216 public void onSaveInstanceState(Bundle out) {
217 // TODO: The dialpad fragment should handle this as part of its own state
218 out.putBoolean(INTENT_EXTRA_SHOW_DIALPAD, isDialpadVisible());
219 DialpadFragment dialpadFragment = getDialpadFragment();
220 if (dialpadFragment != null) {
221 out.putString(DIALPAD_TEXT_KEY, dialpadFragment.getDtmfText());
222 }
223 }
224
225 public void onStart() {
226 // setting activity should be last thing in setup process
227 InCallPresenter.getInstance().setActivity(inCallActivity);
228 enableInCallOrientationEventListener(
229 inCallActivity.getRequestedOrientation()
230 == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
231
232 InCallPresenter.getInstance().onActivityStarted();
233 }
234
235 public void onResume() {
236 if (InCallPresenter.getInstance().isReadyForTearDown()) {
237 LogUtil.i(
238 "InCallActivityCommon.onResume",
239 "InCallPresenter is ready for tear down, not sending updates");
240 } else {
241 updateTaskDescription();
242 InCallPresenter.getInstance().onUiShowing(true);
243 }
244
245 // If there is a pending request to show or hide the dialpad, handle that now.
246 if (showDialpadRequest != DIALPAD_REQUEST_NONE) {
247 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
248 // Exit fullscreen so that the user has access to the dialpad hide/show button and
249 // can hide the dialpad. Important when showing the dialpad from within dialer.
250 InCallPresenter.getInstance().setFullScreen(false, true /* force */);
251
252 inCallActivity.showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
253 animateDialpadOnShow = false;
254
255 DialpadFragment dialpadFragment = getDialpadFragment();
256 if (dialpadFragment != null) {
257 dialpadFragment.setDtmfText(dtmfTextToPreopulate);
258 dtmfTextToPreopulate = null;
259 }
260 } else {
261 LogUtil.i("InCallActivityCommon.onResume", "force hide dialpad");
262 if (getDialpadFragment() != null) {
263 inCallActivity.showDialpadFragment(false /* show */, false /* animate */);
264 }
265 }
266 showDialpadRequest = DIALPAD_REQUEST_NONE;
267 }
268
269 if (showPostCharWaitDialogOnResume) {
270 showPostCharWaitDialog(showPostCharWaitDialogCallId, showPostCharWaitDialogChars);
271 }
272
273 CallList.getInstance()
274 .onInCallUiShown(
275 inCallActivity.getIntent().getBooleanExtra(INTENT_EXTRA_FOR_FULL_SCREEN, false));
276 }
277
278 // onPause is guaranteed to be called when the InCallActivity goes
279 // in the background.
280 public void onPause() {
281 DialpadFragment dialpadFragment = getDialpadFragment();
282 if (dialpadFragment != null) {
283 dialpadFragment.onDialerKeyUp(null);
284 }
285
286 InCallPresenter.getInstance().onUiShowing(false);
287 if (inCallActivity.isFinishing()) {
288 InCallPresenter.getInstance().unsetActivity(inCallActivity);
289 }
290 }
291
292 public void onStop() {
293 enableInCallOrientationEventListener(false);
294 InCallPresenter.getInstance().updateIsChangingConfigurations();
295 InCallPresenter.getInstance().onActivityStopped();
296 }
297
298 public void onDestroy() {
299 InCallPresenter.getInstance().unsetActivity(inCallActivity);
300 InCallPresenter.getInstance().updateIsChangingConfigurations();
301 }
302
303 public void onNewIntent(Intent intent) {
304 LogUtil.i("InCallActivityCommon.onNewIntent", "");
305
306 // We're being re-launched with a new Intent. Since it's possible for a
307 // single InCallActivity instance to persist indefinitely (even if we
308 // finish() ourselves), this sequence can potentially happen any time
309 // the InCallActivity needs to be displayed.
310
311 // Stash away the new intent so that we can get it in the future
312 // by calling getIntent(). (Otherwise getIntent() will return the
313 // original Intent from when we first got created!)
314 inCallActivity.setIntent(intent);
315
316 // Activities are always paused before receiving a new intent, so
317 // we can count on our onResume() method being called next.
318
319 // Just like in onCreate(), handle the intent.
320 internalResolveIntent(intent);
321 }
322
323 public boolean onBackPressed(boolean isInCallScreenVisible) {
324 LogUtil.i("InCallActivityCommon.onBackPressed", "");
325
326 // BACK is also used to exit out of any "special modes" of the
327 // in-call UI:
328 if (!inCallActivity.isVisible()) {
329 return true;
330 }
331
332 if (!isInCallScreenVisible) {
333 return true;
334 }
335
336 DialpadFragment dialpadFragment = getDialpadFragment();
337 if (dialpadFragment != null && dialpadFragment.isVisible()) {
338 inCallActivity.showDialpadFragment(false /* show */, true /* animate */);
339 return true;
340 }
341
342 // Always disable the Back key while an incoming call is ringing
343 DialerCall call = CallList.getInstance().getIncomingCall();
344 if (call != null) {
345 LogUtil.i("InCallActivityCommon.onBackPressed", "consume Back press for an incoming call");
346 return true;
347 }
348
349 // Nothing special to do. Fall back to the default behavior.
350 return false;
351 }
352
353 public boolean onKeyUp(int keyCode, KeyEvent event) {
354 DialpadFragment dialpadFragment = getDialpadFragment();
355 // push input to the dialer.
356 if (dialpadFragment != null
357 && (dialpadFragment.isVisible())
358 && (dialpadFragment.onDialerKeyUp(event))) {
359 return true;
360 } else if (keyCode == KeyEvent.KEYCODE_CALL) {
361 // Always consume CALL to be sure the PhoneWindow won't do anything with it
362 return true;
363 }
364 return false;
365 }
366
367 public boolean onKeyDown(int keyCode, KeyEvent event) {
368 switch (keyCode) {
369 case KeyEvent.KEYCODE_CALL:
370 boolean handled = InCallPresenter.getInstance().handleCallKey();
371 if (!handled) {
372 LogUtil.e(
373 "InCallActivityCommon.onKeyDown",
374 "InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
375 }
376 // Always consume CALL to be sure the PhoneWindow won't do anything with it
377 return true;
378
379 // Note there's no KeyEvent.KEYCODE_ENDCALL case here.
380 // The standard system-wide handling of the ENDCALL key
381 // (see PhoneWindowManager's handling of KEYCODE_ENDCALL)
382 // already implements exactly what the UI spec wants,
383 // namely (1) "hang up" if there's a current active call,
384 // or (2) "don't answer" if there's a current ringing call.
385
386 case KeyEvent.KEYCODE_CAMERA:
387 // Disable the CAMERA button while in-call since it's too
388 // easy to press accidentally.
389 return true;
390
391 case KeyEvent.KEYCODE_VOLUME_UP:
392 case KeyEvent.KEYCODE_VOLUME_DOWN:
393 case KeyEvent.KEYCODE_VOLUME_MUTE:
394 // Ringer silencing handled by PhoneWindowManager.
395 break;
396
397 case KeyEvent.KEYCODE_MUTE:
398 TelecomAdapter.getInstance()
399 .mute(!AudioModeProvider.getInstance().getAudioState().isMuted());
400 return true;
401
402 // Various testing/debugging features, enabled ONLY when VERBOSE == true.
403 case KeyEvent.KEYCODE_SLASH:
404 if (LogUtil.isVerboseEnabled()) {
405 LogUtil.v(
406 "InCallActivityCommon.onKeyDown",
407 "----------- InCallActivity View dump --------------");
408 // Dump starting from the top-level view of the entire activity:
409 Window w = inCallActivity.getWindow();
410 View decorView = w.getDecorView();
411 LogUtil.v("InCallActivityCommon.onKeyDown", "View dump:" + decorView);
412 return true;
413 }
414 break;
415 case KeyEvent.KEYCODE_EQUALS:
416 break;
417 }
418
419 return event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event);
420 }
421
422 private boolean handleDialerKeyDown(int keyCode, KeyEvent event) {
423 LogUtil.v("InCallActivityCommon.handleDialerKeyDown", "keyCode %d, event: %s", keyCode, event);
424
425 // As soon as the user starts typing valid dialable keys on the
426 // keyboard (presumably to type DTMF tones) we start passing the
427 // key events to the DTMFDialer's onDialerKeyDown.
428 DialpadFragment dialpadFragment = getDialpadFragment();
429 if (dialpadFragment != null && dialpadFragment.isVisible()) {
430 return dialpadFragment.onDialerKeyDown(event);
431 }
432
433 return false;
434 }
435
436 public void dismissKeyguard(boolean dismiss) {
437 if (dismissKeyguard == dismiss) {
438 return;
439 }
440 dismissKeyguard = dismiss;
441 if (dismiss) {
442 inCallActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
443 } else {
444 inCallActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
445 }
446 }
447
448 public void showPostCharWaitDialog(String callId, String chars) {
449 if (inCallActivity.isVisible()) {
450 PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
451 fragment.show(inCallActivity.getSupportFragmentManager(), "postCharWait");
452
453 showPostCharWaitDialogOnResume = false;
454 showPostCharWaitDialogCallId = null;
455 showPostCharWaitDialogChars = null;
456 } else {
457 showPostCharWaitDialogOnResume = true;
458 showPostCharWaitDialogCallId = callId;
459 showPostCharWaitDialogChars = chars;
460 }
461 }
462
463 public void maybeShowErrorDialogOnDisconnect(DisconnectCause cause) {
464 LogUtil.i(
465 "InCallActivityCommon.maybeShowErrorDialogOnDisconnect", "disconnect cause: %s", cause);
466
467 if (!inCallActivity.isFinishing()) {
468 if (EnableWifiCallingPrompt.shouldShowPrompt(cause)) {
469 Pair<Dialog, CharSequence> pair =
470 EnableWifiCallingPrompt.createDialog(inCallActivity, cause);
471 showErrorDialog(pair.first, pair.second);
472 } else if (shouldShowDisconnectErrorDialog(cause)) {
473 Pair<Dialog, CharSequence> pair = getDisconnectErrorDialog(inCallActivity, cause);
474 showErrorDialog(pair.first, pair.second);
475 }
476 }
477 }
478
479 /**
480 * When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad should
481 * be shown on launch.
482 *
483 * @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and {@code
484 * false} to indicate no change should be made to the dialpad visibility.
485 */
486 private void relaunchedFromDialer(boolean showDialpad) {
487 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
488 animateDialpadOnShow = true;
489
490 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
491 // If there's only one line in use, AND it's on hold, then we're sure the user
492 // wants to use the dialpad toward the exact line, so un-hold the holding line.
493 DialerCall call = CallList.getInstance().getActiveOrBackgroundCall();
494 if (call != null && call.getState() == State.ONHOLD) {
495 call.unhold();
496 }
497 }
498 }
499
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700500 void dismissPendingDialogs() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800501 if (dialog != null) {
502 dialog.dismiss();
503 dialog = null;
504 }
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700505 if (selectPhoneAccountDialogFragment != null) {
506 selectPhoneAccountDialogFragment.dismiss();
507 selectPhoneAccountDialogFragment = null;
508 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800509 }
510
511 private static boolean shouldShowDisconnectErrorDialog(@NonNull DisconnectCause cause) {
512 return !TextUtils.isEmpty(cause.getDescription())
513 && (cause.getCode() == DisconnectCause.ERROR
514 || cause.getCode() == DisconnectCause.RESTRICTED);
515 }
516
517 private static Pair<Dialog, CharSequence> getDisconnectErrorDialog(
518 @NonNull Context context, @NonNull DisconnectCause cause) {
519 CharSequence message = cause.getDescription();
520 Dialog dialog =
521 new AlertDialog.Builder(context)
522 .setMessage(message)
523 .setPositiveButton(android.R.string.ok, null)
524 .create();
525 return new Pair<>(dialog, message);
526 }
527
528 private void showErrorDialog(Dialog dialog, CharSequence message) {
529 LogUtil.i("InCallActivityCommon.showErrorDialog", "message: %s", message);
530 inCallActivity.dismissPendingDialogs();
531
532 // Show toast if apps is in background when dialog won't be visible.
533 if (!inCallActivity.isVisible()) {
534 Toast.makeText(inCallActivity.getApplicationContext(), message, Toast.LENGTH_LONG).show();
535 return;
536 }
537
538 this.dialog = dialog;
539 dialog.setOnDismissListener(
540 new OnDismissListener() {
541 @Override
542 public void onDismiss(DialogInterface dialog) {
543 LogUtil.i("InCallActivityCommon.showErrorDialog", "dialog dismissed");
544 onDialogDismissed();
545 }
546 });
547 dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
548 dialog.show();
549 }
550
551 private void onDialogDismissed() {
552 dialog = null;
553 CallList.getInstance().onErrorDialogDismissed();
554 InCallPresenter.getInstance().onDismissDialog();
555 }
556
557 public void enableInCallOrientationEventListener(boolean enable) {
558 if (enable) {
559 inCallOrientationEventListener.enable(true);
560 } else {
561 inCallOrientationEventListener.disable();
562 }
563 }
564
565 public void setExcludeFromRecents(boolean exclude) {
566 List<AppTask> tasks = inCallActivity.getSystemService(ActivityManager.class).getAppTasks();
567 int taskId = inCallActivity.getTaskId();
568 for (int i = 0; i < tasks.size(); i++) {
569 ActivityManager.AppTask task = tasks.get(i);
570 try {
571 if (task.getTaskInfo().id == taskId) {
572 task.setExcludeFromRecents(exclude);
573 }
574 } catch (RuntimeException e) {
575 LogUtil.e(
576 "InCallActivityCommon.setExcludeFromRecents",
577 "RuntimeException when excluding task from recents.",
578 e);
579 }
580 }
581 }
582
583 public void showWifiToLteHandoverToast(DialerCall call) {
584 if (call.hasShownWiFiToLteHandoverToast()) {
585 return;
586 }
587 Toast.makeText(
588 inCallActivity, R.string.video_call_wifi_to_lte_handover_toast, Toast.LENGTH_LONG)
589 .show();
590 call.setHasShownWiFiToLteHandoverToast();
591 }
592
593 public void showWifiFailedDialog(final DialerCall call) {
594 if (call.showWifiHandoverAlertAsToast()) {
595 LogUtil.i("InCallActivityCommon.showWifiFailedDialog", "as toast");
596 Toast.makeText(
597 inCallActivity, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
598 .show();
599 return;
600 }
601
602 dismissPendingDialogs();
603
604 AlertDialog.Builder builder =
605 new AlertDialog.Builder(inCallActivity)
606 .setTitle(R.string.video_call_lte_to_wifi_failed_title);
607
608 // This allows us to use the theme of the dialog instead of the activity
609 View dialogCheckBoxView =
610 View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null);
611 final CheckBox wifiHandoverFailureCheckbox =
612 (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
613 wifiHandoverFailureCheckbox.setChecked(false);
614
615 dialog =
616 builder
617 .setView(dialogCheckBoxView)
618 .setMessage(R.string.video_call_lte_to_wifi_failed_message)
619 .setOnCancelListener(
620 new OnCancelListener() {
621 @Override
622 public void onCancel(DialogInterface dialog) {
623 onDialogDismissed();
624 }
625 })
626 .setPositiveButton(
627 android.R.string.ok,
628 new DialogInterface.OnClickListener() {
629 @Override
630 public void onClick(DialogInterface dialog, int id) {
631 call.setDoNotShowDialogForHandoffToWifiFailure(
632 wifiHandoverFailureCheckbox.isChecked());
633 dialog.cancel();
634 onDialogDismissed();
635 }
636 })
637 .create();
638
639 LogUtil.i("InCallActivityCommon.showWifiFailedDialog", "as dialog");
640 dialog.show();
641 }
642
643 public boolean showDialpadFragment(boolean show, boolean animate) {
644 // If the dialpad is already visible, don't animate in. If it's gone, don't animate out.
645 boolean isDialpadVisible = isDialpadVisible();
646 LogUtil.i(
647 "InCallActivityCommon.showDialpadFragment",
648 "show: %b, animate: %b, " + "isDialpadVisible: %b",
649 show,
650 animate,
651 isDialpadVisible);
652 if (show == isDialpadVisible) {
653 return false;
654 }
655
656 FragmentManager dialpadFragmentManager = inCallActivity.getDialpadFragmentManager();
657 if (dialpadFragmentManager == null) {
658 LogUtil.i(
659 "InCallActivityCommon.showDialpadFragment", "unable to show or hide dialpad fragment");
660 return false;
661 }
662
663 // We don't do a FragmentTransaction on the hide case because it will be dealt with when
664 // the listener is fired after an animation finishes.
665 if (!animate) {
666 if (show) {
667 performShowDialpadFragment(dialpadFragmentManager);
668 } else {
669 performHideDialpadFragment();
670 }
671 } else {
672 if (show) {
673 performShowDialpadFragment(dialpadFragmentManager);
674 getDialpadFragment().animateShowDialpad();
675 }
676 getDialpadFragment()
677 .getView()
678 .startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
679 }
680
681 ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
682 if (sensor != null) {
683 sensor.onDialpadVisible(show);
684 }
685 showDialpadRequest = DIALPAD_REQUEST_NONE;
686 return true;
687 }
688
689 private void performShowDialpadFragment(@NonNull FragmentManager dialpadFragmentManager) {
690 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
691 DialpadFragment dialpadFragment = getDialpadFragment();
692 if (dialpadFragment == null) {
693 transaction.add(
694 inCallActivity.getDialpadContainerId(), new DialpadFragment(), TAG_DIALPAD_FRAGMENT);
695 } else {
696 transaction.show(dialpadFragment);
697 }
698
699 transaction.commitAllowingStateLoss();
700 dialpadFragmentManager.executePendingTransactions();
701
702 Logger.get(inCallActivity).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, inCallActivity);
703 }
704
705 private void performHideDialpadFragment() {
706 FragmentManager fragmentManager = inCallActivity.getDialpadFragmentManager();
707 if (fragmentManager == null) {
708 LogUtil.e(
709 "InCallActivityCommon.performHideDialpadFragment", "child fragment manager is null");
710 return;
711 }
712
713 Fragment fragment = fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
714 if (fragment != null) {
715 FragmentTransaction transaction = fragmentManager.beginTransaction();
716 transaction.hide(fragment);
717 transaction.commitAllowingStateLoss();
718 fragmentManager.executePendingTransactions();
719 }
720 }
721
722 public boolean isDialpadVisible() {
723 DialpadFragment dialpadFragment = getDialpadFragment();
724 return dialpadFragment != null && dialpadFragment.isVisible();
725 }
726
727 /** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
728 @Nullable
729 private DialpadFragment getDialpadFragment() {
730 FragmentManager fragmentManager = inCallActivity.getDialpadFragmentManager();
731 if (fragmentManager == null) {
732 return null;
733 }
734 return (DialpadFragment) fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
735 }
736
737 public void updateTaskDescription() {
738 Resources resources = inCallActivity.getResources();
739 int color;
740 if (resources.getBoolean(R.bool.is_layout_landscape)) {
741 color =
742 ResourcesCompat.getColor(
743 resources, R.color.statusbar_background_color, inCallActivity.getTheme());
744 } else {
745 color = InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
746 }
747
748 TaskDescription td =
749 new TaskDescription(resources.getString(R.string.notification_ongoing_call), null, color);
750 inCallActivity.setTaskDescription(td);
751 }
752
753 public boolean hasPendingDialogs() {
754 return dialog != null;
755 }
756
757 private void internalResolveIntent(Intent intent) {
758 if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
759 return;
760 }
761
762 if (intent.hasExtra(INTENT_EXTRA_SHOW_DIALPAD)) {
763 // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF
764 // dialpad should be initially visible. If the extra isn't
765 // present at all, we just leave the dialpad in its previous state.
766 boolean showDialpad = intent.getBooleanExtra(INTENT_EXTRA_SHOW_DIALPAD, false);
767 LogUtil.i("InCallActivityCommon.internalResolveIntent", "SHOW_DIALPAD_EXTRA: " + showDialpad);
768
769 relaunchedFromDialer(showDialpad);
770 }
771
772 DialerCall outgoingCall = CallList.getInstance().getOutgoingCall();
773 if (outgoingCall == null) {
774 outgoingCall = CallList.getInstance().getPendingOutgoingCall();
775 }
776
Eric Erfanianccca3152017-02-22 16:32:36 -0800777 if (intent.getBooleanExtra(INTENT_EXTRA_NEW_OUTGOING_CALL, false)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800778 intent.removeExtra(INTENT_EXTRA_NEW_OUTGOING_CALL);
779
780 // InCallActivity is responsible for disconnecting a new outgoing call if there
781 // is no way of making it (i.e. no valid call capable accounts).
782 // If the version is not MSIM compatible, then ignore this code.
783 if (CompatUtils.isMSIMCompatible()
784 && InCallPresenter.isCallWithNoValidAccounts(outgoingCall)) {
785 LogUtil.i(
786 "InCallActivityCommon.internalResolveIntent",
787 "call with no valid accounts, disconnecting");
788 outgoingCall.disconnect();
789 }
790
791 dismissKeyguard(true);
792 }
793
794 boolean didShowAccountSelectionDialog = maybeShowAccountSelectionDialog();
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700795 if (didShowAccountSelectionDialog) {
796 inCallActivity.hideMainInCallFragment();
797 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800798 }
799
800 private boolean maybeShowAccountSelectionDialog() {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700801 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
802 if (waitingForAccountCall == null) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800803 return false;
804 }
805
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700806 Bundle extras = waitingForAccountCall.getIntentExtras();
Eric Erfanianccca3152017-02-22 16:32:36 -0800807 List<PhoneAccountHandle> phoneAccountHandles;
808 if (extras != null) {
809 phoneAccountHandles =
810 extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
811 } else {
812 phoneAccountHandles = new ArrayList<>();
813 }
814
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700815 selectPhoneAccountDialogFragment =
Eric Erfanianccca3152017-02-22 16:32:36 -0800816 SelectPhoneAccountDialogFragment.newInstance(
817 R.string.select_phone_account_for_calls,
818 true,
819 phoneAccountHandles,
820 selectAccountListener,
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700821 waitingForAccountCall.getId());
822 selectPhoneAccountDialogFragment.show(
823 inCallActivity.getFragmentManager(), TAG_SELECT_ACCOUNT_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800824 return true;
825 }
826}