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