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