blob: cdab6b4f54b66431ecedcbc0194cf3bf43b4b478 [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.content.Context;
20import android.content.Intent;
21import android.graphics.drawable.GradientDrawable;
22import android.graphics.drawable.GradientDrawable.Orientation;
23import android.os.Bundle;
Eric Erfanian2ca43182017-08-31 06:57:16 -070024import android.os.Trace;
Eric Erfanianccca3152017-02-22 16:32:36 -080025import android.support.annotation.ColorInt;
26import android.support.annotation.FloatRange;
Eric Erfanianc857f902017-05-15 14:05:33 -070027import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080028import android.support.annotation.Nullable;
29import android.support.v4.app.FragmentManager;
30import android.support.v4.app.FragmentTransaction;
31import android.support.v4.graphics.ColorUtils;
Eric Erfanian90508232017-03-24 09:31:16 -070032import android.telephony.TelephonyManager;
Eric Erfanianccca3152017-02-22 16:32:36 -080033import android.view.KeyEvent;
34import android.view.MenuItem;
35import android.view.MotionEvent;
36import android.view.View;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070037import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080038import com.android.dialer.common.LogUtil;
weijiaxu650e7cc2017-10-31 12:38:54 -070039import com.android.dialer.common.concurrent.ThreadUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080040import com.android.dialer.compat.ActivityCompat;
Eric Erfanian2ca43182017-08-31 06:57:16 -070041import com.android.dialer.configprovider.ConfigProviderBindings;
42import com.android.dialer.logging.DialerImpression;
Eric Erfanianccca3152017-02-22 16:32:36 -080043import com.android.dialer.logging.Logger;
weijiaxu94df7202017-10-25 18:21:41 -070044import com.android.dialer.logging.LoggingBindings;
Eric Erfanian8369df02017-05-03 10:27:13 -070045import com.android.dialer.logging.ScreenEvent;
Eric Erfanianccca3152017-02-22 16:32:36 -080046import com.android.incallui.answer.bindings.AnswerBindings;
47import com.android.incallui.answer.protocol.AnswerScreen;
48import com.android.incallui.answer.protocol.AnswerScreenDelegate;
49import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
50import com.android.incallui.answerproximitysensor.PseudoScreenState;
51import com.android.incallui.call.CallList;
52import com.android.incallui.call.DialerCall;
53import com.android.incallui.call.DialerCall.State;
Eric Erfanian2ca43182017-08-31 06:57:16 -070054import com.android.incallui.callpending.CallPendingActivity;
55import com.android.incallui.disconnectdialog.DisconnectMessage;
Eric Erfanianccca3152017-02-22 16:32:36 -080056import com.android.incallui.incall.bindings.InCallBindings;
57import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
58import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
59import com.android.incallui.incall.protocol.InCallScreen;
60import com.android.incallui.incall.protocol.InCallScreenDelegate;
61import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
62import com.android.incallui.video.bindings.VideoBindings;
63import com.android.incallui.video.protocol.VideoCallScreen;
64import com.android.incallui.video.protocol.VideoCallScreenDelegate;
65import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
66
67/** Version of {@link InCallActivity} that shows the new UI */
68public class InCallActivity extends TransactionSafeFragmentActivity
69 implements AnswerScreenDelegateFactory,
70 InCallScreenDelegateFactory,
71 InCallButtonUiDelegateFactory,
72 VideoCallScreenDelegateFactory,
73 PseudoScreenState.StateChangedListener {
74
Eric Erfanian2ca43182017-08-31 06:57:16 -070075 public static final int PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN = 0;
76 public static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1;
77 public static final int PENDING_INTENT_REQUEST_CODE_BUBBLE = 2;
78
Eric Erfanianccca3152017-02-22 16:32:36 -080079 private static final String TAG_IN_CALL_SCREEN = "tag_in_call_screen";
80 private static final String TAG_ANSWER_SCREEN = "tag_answer_screen";
81 private static final String TAG_VIDEO_CALL_SCREEN = "tag_video_call_screen";
82
83 private static final String DID_SHOW_ANSWER_SCREEN_KEY = "did_show_answer_screen";
84 private static final String DID_SHOW_IN_CALL_SCREEN_KEY = "did_show_in_call_screen";
85 private static final String DID_SHOW_VIDEO_CALL_SCREEN_KEY = "did_show_video_call_screen";
86
Eric Erfanian90508232017-03-24 09:31:16 -070087 private static final String CONFIG_ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
88
Eric Erfanianccca3152017-02-22 16:32:36 -080089 private final InCallActivityCommon common;
90 private boolean didShowAnswerScreen;
91 private boolean didShowInCallScreen;
92 private boolean didShowVideoCallScreen;
93 private int[] backgroundDrawableColors;
94 private GradientDrawable backgroundDrawable;
95 private boolean isVisible;
96 private View pseudoBlackScreenOverlay;
97 private boolean touchDownWhenPseudoScreenOff;
98 private boolean isInShowMainInCallFragment;
99 private boolean needDismissPendingDialogs;
wangqi9982f0d2017-10-11 17:46:07 -0700100 private boolean allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -0800101
102 public InCallActivity() {
103 common = new InCallActivityCommon(this);
104 }
105
106 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700107 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800108 Intent intent = new Intent(Intent.ACTION_MAIN, null);
109 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
110 intent.setClass(context, InCallActivity.class);
111 InCallActivityCommon.setIntentExtras(intent, showDialpad, newOutgoingCall, isForFullScreen);
112 return intent;
113 }
114
115 @Override
116 protected void onResumeFragments() {
117 super.onResumeFragments();
118 if (needDismissPendingDialogs) {
119 dismissPendingDialogs();
120 }
121 }
122
123 @Override
124 protected void onCreate(Bundle icicle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700125 Trace.beginSection("InCallActivity.onCreate");
Eric Erfanianccca3152017-02-22 16:32:36 -0800126 LogUtil.i("InCallActivity.onCreate", "");
127 super.onCreate(icicle);
128
Eric Erfanian2ca43182017-08-31 06:57:16 -0700129 if (getIntent().getBooleanExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY, false)) {
130 Logger.get(this).logImpression(DialerImpression.Type.BUBBLE_PRIMARY_BUTTON_RETURN_TO_CALL);
131 getIntent().removeExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY);
132 }
133
Eric Erfanianccca3152017-02-22 16:32:36 -0800134 if (icicle != null) {
135 didShowAnswerScreen = icicle.getBoolean(DID_SHOW_ANSWER_SCREEN_KEY);
136 didShowInCallScreen = icicle.getBoolean(DID_SHOW_IN_CALL_SCREEN_KEY);
137 didShowVideoCallScreen = icicle.getBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY);
138 }
139
140 common.onCreate(icicle);
141
142 getWindow()
143 .getDecorView()
144 .setSystemUiVisibility(
145 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
146
147 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700148 sendBroadcast(CallPendingActivity.getFinishBroadcast());
149 Trace.endSection();
weijiaxuc950a9b2017-11-06 16:39:04 -0800150 Logger.get(this)
151 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
152 Logger.get(this)
153 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
Eric Erfanianccca3152017-02-22 16:32:36 -0800154 }
155
156 @Override
157 protected void onSaveInstanceState(Bundle out) {
158 LogUtil.i("InCallActivity.onSaveInstanceState", "");
159 common.onSaveInstanceState(out);
160 out.putBoolean(DID_SHOW_ANSWER_SCREEN_KEY, didShowAnswerScreen);
161 out.putBoolean(DID_SHOW_IN_CALL_SCREEN_KEY, didShowInCallScreen);
162 out.putBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY, didShowVideoCallScreen);
163 super.onSaveInstanceState(out);
164 isVisible = false;
165 }
166
167 @Override
168 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700169 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800170 LogUtil.i("InCallActivity.onStart", "");
wangqi9982f0d2017-10-11 17:46:07 -0700171 Trace.beginSection("call super");
Eric Erfanianccca3152017-02-22 16:32:36 -0800172 super.onStart();
wangqi9982f0d2017-10-11 17:46:07 -0700173 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800174 isVisible = true;
175 showMainInCallFragment();
176 common.onStart();
177 if (ActivityCompat.isInMultiWindowMode(this)
178 && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
179 // Hide the dialpad because there may not be enough room
180 showDialpadFragment(false, false);
181 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700182 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800183 }
184
185 @Override
186 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700187 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800188 LogUtil.i("InCallActivity.onResume", "");
189 super.onResume();
190 common.onResume();
191 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
192 pseudoScreenState.addListener(this);
193 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700194 Trace.endSection();
weijiaxu650e7cc2017-10-31 12:38:54 -0700195 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
196 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800197 () ->
198 Logger.get(this)
199 .logRecordMemory(LoggingBindings.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700200 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800201 }
202
203 /** onPause is guaranteed to be called when the InCallActivity goes in the background. */
204 @Override
205 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700206 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800207 LogUtil.i("InCallActivity.onPause", "");
208 super.onPause();
209 common.onPause();
210 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700211 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800212 }
213
214 @Override
215 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700216 Trace.beginSection("InCallActivity.onStop");
Eric Erfanianccca3152017-02-22 16:32:36 -0800217 LogUtil.i("InCallActivity.onStop", "");
wangqi4d705e52017-09-28 12:23:35 -0700218 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800219 super.onStop();
220 common.onStop();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700221 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800222 }
223
224 @Override
225 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700226 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800227 LogUtil.i("InCallActivity.onDestroy", "");
228 super.onDestroy();
229 common.onDestroy();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700230 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800231 }
232
233 @Override
234 public void finish() {
235 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700236 // When user select incall ui from recents after the call is disconnected, it tries to launch
237 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
238 // crash.
239 // By calling finishAndRemoveTask() instead of finish() the task associated with
240 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
241 // this case.
242 //
243 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
244 // clear the task since there could be parent activity in the same task that's still alive.
245 // But InCallActivity is special since it's singleInstance which means it's root activity and
246 // only instance of activity in the task. So it should be safe to also remove task when
247 // finishing.
248 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
249 // finishes, the task should also be removed since it doesn't make sense to go back to it in
250 // anyway anymore.
251 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800252 }
253 }
254
255 private boolean shouldCloseActivityOnFinish() {
256 if (!isVisible()) {
257 LogUtil.i(
258 "InCallActivity.shouldCloseActivityOnFinish",
259 "allowing activity to be closed because it's not visible");
260 return true;
261 }
262
twyen8efb4952017-10-06 16:35:54 -0700263 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800264 LogUtil.i(
265 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700266 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800267 return false;
268 }
269
270 LogUtil.i(
271 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700272 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800273 return true;
274 }
275
276 @Override
277 protected void onNewIntent(Intent intent) {
278 LogUtil.i("InCallActivity.onNewIntent", "");
Eric Erfanianccca3152017-02-22 16:32:36 -0800279
280 // If the screen is off, we need to make sure it gets turned on for incoming calls.
281 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
282 // when the activity is first created. Therefore, to ensure the screen is turned on
283 // for the call waiting case, we recreate() the current activity. There should be no jank from
284 // this since the screen is already off and will remain so until our new activity is up.
285 if (!isVisible()) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700286 common.onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800287 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
288 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700289 } else {
290 common.onNewIntent(intent, false /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800291 }
292 }
293
294 @Override
295 public void onBackPressed() {
296 LogUtil.i("InCallActivity.onBackPressed", "");
297 if (!common.onBackPressed(didShowInCallScreen || didShowVideoCallScreen)) {
298 super.onBackPressed();
299 }
300 }
301
302 @Override
303 public boolean onOptionsItemSelected(MenuItem item) {
304 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
305 if (item.getItemId() == android.R.id.home) {
306 onBackPressed();
307 return true;
308 }
309 return super.onOptionsItemSelected(item);
310 }
311
312 @Override
313 public boolean onKeyUp(int keyCode, KeyEvent event) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700314 return common.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800315 }
316
317 @Override
318 public boolean onKeyDown(int keyCode, KeyEvent event) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700319 return common.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800320 }
321
322 public boolean isInCallScreenAnimating() {
323 return false;
324 }
325
326 public void showConferenceFragment(boolean show) {
327 if (show) {
328 startActivity(new Intent(this, ManageConferenceActivity.class));
329 }
330 }
331
332 public boolean showDialpadFragment(boolean show, boolean animate) {
333 boolean didChange = common.showDialpadFragment(show, animate);
334 if (didChange) {
335 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
336 // repositions itself.
337 getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
338 }
339 return didChange;
340 }
341
342 public boolean isDialpadVisible() {
343 return common.isDialpadVisible();
344 }
345
346 public void onForegroundCallChanged(DialerCall newForegroundCall) {
347 common.updateTaskDescription();
348 if (didShowAnswerScreen && newForegroundCall != null) {
349 if (newForegroundCall.getState() == State.DISCONNECTED
350 || newForegroundCall.getState() == State.IDLE) {
351 LogUtil.i(
352 "InCallActivity.onForegroundCallChanged",
353 "rejecting incoming call, not updating " + "window background color");
354 }
355 } else {
356 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
357 updateWindowBackgroundColor(0);
358 }
359 }
360
361 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
362 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
363 @ColorInt int top;
364 @ColorInt int middle;
365 @ColorInt int bottom;
366 @ColorInt int gray = 0x66000000;
367
368 if (ActivityCompat.isInMultiWindowMode(this)) {
369 top = themeColorManager.getBackgroundColorSolid();
370 middle = themeColorManager.getBackgroundColorSolid();
371 bottom = themeColorManager.getBackgroundColorSolid();
372 } else {
373 top = themeColorManager.getBackgroundColorTop();
374 middle = themeColorManager.getBackgroundColorMiddle();
375 bottom = themeColorManager.getBackgroundColorBottom();
376 }
377
378 if (progress < 0) {
379 float correctedProgress = Math.abs(progress);
380 top = ColorUtils.blendARGB(top, gray, correctedProgress);
381 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
382 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
383 }
384
385 boolean backgroundDirty = false;
386 if (backgroundDrawable == null) {
387 backgroundDrawableColors = new int[] {top, middle, bottom};
388 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
389 backgroundDirty = true;
390 } else {
391 if (backgroundDrawableColors[0] != top) {
392 backgroundDrawableColors[0] = top;
393 backgroundDirty = true;
394 }
395 if (backgroundDrawableColors[1] != middle) {
396 backgroundDrawableColors[1] = middle;
397 backgroundDirty = true;
398 }
399 if (backgroundDrawableColors[2] != bottom) {
400 backgroundDrawableColors[2] = bottom;
401 backgroundDirty = true;
402 }
403 if (backgroundDirty) {
404 backgroundDrawable.setColors(backgroundDrawableColors);
405 }
406 }
407
408 if (backgroundDirty) {
409 getWindow().setBackgroundDrawable(backgroundDrawable);
410 }
411 }
412
413 public boolean isVisible() {
414 return isVisible;
415 }
416
417 public boolean getCallCardFragmentVisible() {
418 return didShowInCallScreen || didShowVideoCallScreen;
419 }
420
421 public void dismissKeyguard(boolean dismiss) {
422 common.dismissKeyguard(dismiss);
423 }
424
425 public void showPostCharWaitDialog(String callId, String chars) {
426 common.showPostCharWaitDialog(callId, chars);
427 }
428
Eric Erfanian2ca43182017-08-31 06:57:16 -0700429 public void maybeShowErrorDialogOnDisconnect(DisconnectMessage disconnectMessage) {
430 common.maybeShowErrorDialogOnDisconnect(disconnectMessage);
Eric Erfanianccca3152017-02-22 16:32:36 -0800431 }
432
433 public void dismissPendingDialogs() {
434 if (isVisible) {
435 LogUtil.i("InCallActivity.dismissPendingDialogs", "");
436 common.dismissPendingDialogs();
437 AnswerScreen answerScreen = getAnswerScreen();
438 if (answerScreen != null) {
439 answerScreen.dismissPendingDialogs();
440 }
441 needDismissPendingDialogs = false;
442 } else {
443 // The activity is not visible and onSaveInstanceState may have been called so defer the
444 // dismissing action.
445 LogUtil.i(
446 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
447 needDismissPendingDialogs = true;
448 }
449 }
450
451 private void enableInCallOrientationEventListener(boolean enable) {
452 common.enableInCallOrientationEventListener(enable);
453 }
454
455 public void setExcludeFromRecents(boolean exclude) {
456 common.setExcludeFromRecents(exclude);
457 }
458
Eric Erfanianccca3152017-02-22 16:32:36 -0800459 @Nullable
460 public FragmentManager getDialpadFragmentManager() {
461 InCallScreen inCallScreen = getInCallScreen();
462 if (inCallScreen != null) {
463 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
464 }
465 return null;
466 }
467
468 public int getDialpadContainerId() {
469 return getInCallScreen().getAnswerAndDialpadContainerResourceId();
470 }
471
472 @Override
473 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
474 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
475 if (call == null) {
476 // This is a work around for a bug where we attempt to create a new delegate after the call
477 // has already been removed. An example of when this can happen is:
478 // 1. incoming video call in landscape mode
479 // 2. remote party hangs up
480 // 3. activity switches from landscape to portrait
481 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
482 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
483 // because this new state is transient and the activity will be destroyed soon.
484 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
485 return new AnswerScreenPresenterStub();
486 } else {
487 return new AnswerScreenPresenter(
488 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
489 }
490 }
491
492 @Override
493 public InCallScreenDelegate newInCallScreenDelegate() {
494 return new CallCardPresenter(this);
495 }
496
497 @Override
498 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
499 return new CallButtonPresenter(this);
500 }
501
502 @Override
Eric Erfanian90508232017-03-24 09:31:16 -0700503 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
504 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
505 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
506 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
507 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800508 return new VideoCallPresenter();
509 }
510
511 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700512 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -0800513 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "");
514 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700515 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800516 }
517
518 public void onWiFiToLteHandover(DialerCall call) {
519 common.showWifiToLteHandoverToast(call);
520 }
521
522 public void onHandoverToWifiFailed(DialerCall call) {
523 common.showWifiFailedDialog(call);
524 }
525
Eric Erfanianc857f902017-05-15 14:05:33 -0700526 public void onInternationalCallOnWifi(@NonNull DialerCall call) {
527 LogUtil.enterBlock("InCallActivity.onInternationalCallOnWifi");
528 common.showInternationalCallOnWifiDialog(call);
529 }
530
Eric Erfanian938468d2017-10-24 14:05:52 -0700531 @Override
532 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
533 super.onMultiWindowModeChanged(isInMultiWindowMode);
534 if (!isInMultiWindowMode) {
535 common.updateNavigationBar(isDialpadVisible());
536 }
537 }
538
Eric Erfanianccca3152017-02-22 16:32:36 -0800539 public void setAllowOrientationChange(boolean allowOrientationChange) {
wangqi9982f0d2017-10-11 17:46:07 -0700540 if (this.allowOrientationChange == allowOrientationChange) {
541 return;
542 }
543 this.allowOrientationChange = allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -0800544 if (!allowOrientationChange) {
545 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
546 } else {
547 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
548 }
549 enableInCallOrientationEventListener(allowOrientationChange);
550 }
551
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700552 public void hideMainInCallFragment() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800553 LogUtil.i("InCallActivity.hideMainInCallFragment", "");
554 if (didShowInCallScreen || didShowVideoCallScreen) {
555 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
556 hideInCallScreenFragment(transaction);
557 hideVideoCallScreenFragment(transaction);
558 transaction.commitAllowingStateLoss();
559 getSupportFragmentManager().executePendingTransactions();
560 }
561 }
562
563 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700564 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800565 // If the activity's onStart method hasn't been called yet then defer doing any work.
566 if (!isVisible) {
567 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700568 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800569 return;
570 }
571
572 // Don't let this be reentrant.
573 if (isInShowMainInCallFragment) {
574 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700575 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800576 return;
577 }
578
579 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700580 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
581 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
Eric Erfanianccca3152017-02-22 16:32:36 -0800582 LogUtil.i(
583 "InCallActivity.showMainInCallFragment",
584 "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, "
585 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b",
586 shouldShowAnswerUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -0700587 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -0800588 didShowAnswerScreen,
589 didShowInCallScreen,
590 didShowVideoCallScreen);
591 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700592 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -0800593
594 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
595 boolean didChangeInCall;
596 boolean didChangeVideo;
597 boolean didChangeAnswer;
598 if (shouldShowAnswerUi.shouldShow) {
599 didChangeInCall = hideInCallScreenFragment(transaction);
600 didChangeVideo = hideVideoCallScreenFragment(transaction);
601 didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700602 } else if (shouldShowVideoUi.shouldShow) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800603 didChangeInCall = hideInCallScreenFragment(transaction);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700604 didChangeVideo = showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800605 didChangeAnswer = hideAnswerScreenFragment(transaction);
606 } else {
607 didChangeInCall = showInCallScreenFragment(transaction);
608 didChangeVideo = hideVideoCallScreenFragment(transaction);
609 didChangeAnswer = hideAnswerScreenFragment(transaction);
610 }
611
612 if (didChangeInCall || didChangeVideo || didChangeAnswer) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700613 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -0800614 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700615 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800616 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
617 }
618 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700619 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800620 }
621
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700622 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800623 DialerCall call = CallList.getInstance().getIncomingCall();
624 if (call != null) {
625 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700626 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800627 }
628
629 call = CallList.getInstance().getVideoUpgradeRequestCall();
630 if (call != null) {
631 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700632 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800633 }
634
635 // Check if we're showing the answer screen and the call is disconnected. If this condition is
636 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
637 // the user rejects an incoming call.
638 call = CallList.getInstance().getFirstCall();
639 if (call == null) {
640 call = CallList.getInstance().getBackgroundCall();
641 }
642 if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
643 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700644 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800645 }
646
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700647 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800648 }
649
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700650 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800651 DialerCall call = CallList.getInstance().getFirstCall();
652 if (call == null) {
653 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700654 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800655 }
656
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700657 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800658 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700659 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800660 }
661
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700662 if (call.hasSentVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800663 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700664 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800665 }
666
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700667 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800668 }
669
670 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
671 // When rejecting a call the active call can become null in which case we should continue
672 // showing the answer screen.
673 if (didShowAnswerScreen && call == null) {
674 return false;
675 }
676
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700677 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
678
679 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -0800680
681 // Check if we're already showing an answer screen for this call.
682 if (didShowAnswerScreen) {
683 AnswerScreen answerScreen = getAnswerScreen();
684 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700685 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -0700686 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
687 && !answerScreen.isActionTimeout()) {
688 LogUtil.d(
689 "InCallActivity.showAnswerScreenFragment",
690 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -0800691 return false;
692 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700693 if (answerScreen.isActionTimeout()) {
694 LogUtil.i(
695 "InCallActivity.showAnswerScreenFragment",
696 "answer fragment exists but has been accepted/rejected and timed out");
697 } else {
698 LogUtil.i(
699 "InCallActivity.showAnswerScreenFragment",
700 "answer fragment exists but arguments do not match");
701 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800702 hideAnswerScreenFragment(transaction);
703 }
704
705 // Show a new answer screen.
706 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -0700707 AnswerBindings.createAnswerScreen(
708 call.getId(),
709 call.isVideoCall(),
710 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -0700711 call.getVideoTech().isSelfManagedCamera(),
712 shouldAllowAnswerAndRelease(call),
713 CallList.getInstance().getBackgroundCall() != null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800714 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), TAG_ANSWER_SCREEN);
715
716 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
717 didShowAnswerScreen = true;
718 return true;
719 }
720
Eric Erfanian90508232017-03-24 09:31:16 -0700721 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
722 if (CallList.getInstance().getActiveCall() == null) {
723 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
724 return false;
725 }
726 if (getSystemService(TelephonyManager.class).getPhoneType()
727 == TelephonyManager.PHONE_TYPE_CDMA) {
728 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
729 return false;
730 }
731 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
732 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
733 return false;
734 }
735 if (!ConfigProviderBindings.get(this).getBoolean(CONFIG_ANSWER_AND_RELEASE_ENABLED, true)) {
736 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
737 return false;
738 }
739
740 return true;
741 }
742
Eric Erfanianccca3152017-02-22 16:32:36 -0800743 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
744 if (!didShowAnswerScreen) {
745 return false;
746 }
747 AnswerScreen answerScreen = getAnswerScreen();
748 if (answerScreen != null) {
749 transaction.remove(answerScreen.getAnswerScreenFragment());
750 }
751
752 didShowAnswerScreen = false;
753 return true;
754 }
755
756 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
757 if (didShowInCallScreen) {
758 return false;
759 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700760 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
761 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), TAG_IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -0800762 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
763 didShowInCallScreen = true;
764 return true;
765 }
766
767 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
768 if (!didShowInCallScreen) {
769 return false;
770 }
771 InCallScreen inCallScreen = getInCallScreen();
772 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700773 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -0800774 }
775 didShowInCallScreen = false;
776 return true;
777 }
778
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700779 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800780 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700781 VideoCallScreen videoCallScreen = getVideoCallScreen();
782 if (videoCallScreen.getCallId().equals(call.getId())) {
783 return false;
784 }
785 LogUtil.i(
786 "InCallActivity.showVideoCallScreenFragment",
787 "video call fragment exists but arguments do not match");
788 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -0800789 }
790
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700791 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
792
Eric Erfanian90508232017-03-24 09:31:16 -0700793 VideoCallScreen videoCallScreen =
794 VideoBindings.createVideoCallScreen(
795 call.getId(), call.getVideoTech().shouldUseSurfaceView());
Eric Erfanianccca3152017-02-22 16:32:36 -0800796 transaction.add(R.id.main, videoCallScreen.getVideoCallScreenFragment(), TAG_VIDEO_CALL_SCREEN);
797
798 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
799 didShowVideoCallScreen = true;
800 return true;
801 }
802
803 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
804 if (!didShowVideoCallScreen) {
805 return false;
806 }
807 VideoCallScreen videoCallScreen = getVideoCallScreen();
808 if (videoCallScreen != null) {
809 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
810 }
811 didShowVideoCallScreen = false;
812 return true;
813 }
814
815 AnswerScreen getAnswerScreen() {
816 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(TAG_ANSWER_SCREEN);
817 }
818
819 InCallScreen getInCallScreen() {
820 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_IN_CALL_SCREEN);
821 }
822
823 VideoCallScreen getVideoCallScreen() {
824 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_VIDEO_CALL_SCREEN);
825 }
826
827 @Override
828 public void onPseudoScreenStateChanged(boolean isOn) {
829 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
830 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
831 }
832
833 /**
834 * For some touch related issue, turning off the screen can be faked by drawing a black view over
835 * the activity. All touch events started when the screen is "off" is rejected.
836 *
837 * @see PseudoScreenState
838 */
839 @Override
840 public boolean dispatchTouchEvent(MotionEvent event) {
841 // Reject any gesture that started when the screen is in the fake off state.
842 if (touchDownWhenPseudoScreenOff) {
843 if (event.getAction() == MotionEvent.ACTION_UP) {
844 touchDownWhenPseudoScreenOff = false;
845 }
846 return true;
847 }
848 // Reject all touch event when the screen is in the fake off state.
849 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
850 if (event.getAction() == MotionEvent.ACTION_DOWN) {
851 touchDownWhenPseudoScreenOff = true;
852 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
853 }
854 return true;
855 }
856 return super.dispatchTouchEvent(event);
857 }
858
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700859 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -0800860 public final boolean shouldShow;
861 public final DialerCall call;
862
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700863 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800864 this.shouldShow = shouldShow;
865 this.call = call;
866 }
867 }
868}