blob: 93534697c7fc977f4a694c620849329b2656d121 [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;
linyuh9c327da2017-11-14 12:33:48 -080037import android.view.WindowManager;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070038import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080039import com.android.dialer.common.LogUtil;
weijiaxu650e7cc2017-10-31 12:38:54 -070040import com.android.dialer.common.concurrent.ThreadUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080041import com.android.dialer.compat.ActivityCompat;
Eric Erfanian2ca43182017-08-31 06:57:16 -070042import com.android.dialer.configprovider.ConfigProviderBindings;
43import com.android.dialer.logging.DialerImpression;
Eric Erfanianccca3152017-02-22 16:32:36 -080044import com.android.dialer.logging.Logger;
weijiaxu94df7202017-10-25 18:21:41 -070045import com.android.dialer.logging.LoggingBindings;
Eric Erfanian8369df02017-05-03 10:27:13 -070046import com.android.dialer.logging.ScreenEvent;
Eric Erfanianccca3152017-02-22 16:32:36 -080047import com.android.incallui.answer.bindings.AnswerBindings;
48import com.android.incallui.answer.protocol.AnswerScreen;
49import com.android.incallui.answer.protocol.AnswerScreenDelegate;
50import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
51import com.android.incallui.answerproximitysensor.PseudoScreenState;
52import com.android.incallui.call.CallList;
53import com.android.incallui.call.DialerCall;
54import com.android.incallui.call.DialerCall.State;
Eric Erfanian2ca43182017-08-31 06:57:16 -070055import com.android.incallui.callpending.CallPendingActivity;
56import com.android.incallui.disconnectdialog.DisconnectMessage;
Eric Erfanianccca3152017-02-22 16:32:36 -080057import com.android.incallui.incall.bindings.InCallBindings;
58import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
59import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
60import com.android.incallui.incall.protocol.InCallScreen;
61import com.android.incallui.incall.protocol.InCallScreenDelegate;
62import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
63import com.android.incallui.video.bindings.VideoBindings;
64import com.android.incallui.video.protocol.VideoCallScreen;
65import com.android.incallui.video.protocol.VideoCallScreenDelegate;
66import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
67
68/** Version of {@link InCallActivity} that shows the new UI */
69public class InCallActivity extends TransactionSafeFragmentActivity
70 implements AnswerScreenDelegateFactory,
71 InCallScreenDelegateFactory,
72 InCallButtonUiDelegateFactory,
73 VideoCallScreenDelegateFactory,
74 PseudoScreenState.StateChangedListener {
75
Eric Erfanian2ca43182017-08-31 06:57:16 -070076 public static final int PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN = 0;
77 public static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1;
78 public static final int PENDING_INTENT_REQUEST_CODE_BUBBLE = 2;
79
Eric Erfanianccca3152017-02-22 16:32:36 -080080 private static final String TAG_IN_CALL_SCREEN = "tag_in_call_screen";
81 private static final String TAG_ANSWER_SCREEN = "tag_answer_screen";
82 private static final String TAG_VIDEO_CALL_SCREEN = "tag_video_call_screen";
83
84 private static final String DID_SHOW_ANSWER_SCREEN_KEY = "did_show_answer_screen";
85 private static final String DID_SHOW_IN_CALL_SCREEN_KEY = "did_show_in_call_screen";
86 private static final String DID_SHOW_VIDEO_CALL_SCREEN_KEY = "did_show_video_call_screen";
87
Eric Erfanian90508232017-03-24 09:31:16 -070088 private static final String CONFIG_ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
89
Eric Erfanianccca3152017-02-22 16:32:36 -080090 private final InCallActivityCommon common;
91 private boolean didShowAnswerScreen;
92 private boolean didShowInCallScreen;
93 private boolean didShowVideoCallScreen;
linyuh9c327da2017-11-14 12:33:48 -080094 private boolean dismissKeyguard;
Eric Erfanianccca3152017-02-22 16:32:36 -080095 private int[] backgroundDrawableColors;
96 private GradientDrawable backgroundDrawable;
97 private boolean isVisible;
98 private View pseudoBlackScreenOverlay;
99 private boolean touchDownWhenPseudoScreenOff;
100 private boolean isInShowMainInCallFragment;
101 private boolean needDismissPendingDialogs;
wangqi9982f0d2017-10-11 17:46:07 -0700102 private boolean allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -0800103
104 public InCallActivity() {
105 common = new InCallActivityCommon(this);
106 }
107
108 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700109 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800110 Intent intent = new Intent(Intent.ACTION_MAIN, null);
111 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
112 intent.setClass(context, InCallActivity.class);
113 InCallActivityCommon.setIntentExtras(intent, showDialpad, newOutgoingCall, isForFullScreen);
114 return intent;
115 }
116
117 @Override
118 protected void onResumeFragments() {
119 super.onResumeFragments();
120 if (needDismissPendingDialogs) {
121 dismissPendingDialogs();
122 }
123 }
124
125 @Override
126 protected void onCreate(Bundle icicle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700127 Trace.beginSection("InCallActivity.onCreate");
Eric Erfanianccca3152017-02-22 16:32:36 -0800128 LogUtil.i("InCallActivity.onCreate", "");
129 super.onCreate(icicle);
130
Eric Erfanian2ca43182017-08-31 06:57:16 -0700131 if (getIntent().getBooleanExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY, false)) {
132 Logger.get(this).logImpression(DialerImpression.Type.BUBBLE_PRIMARY_BUTTON_RETURN_TO_CALL);
133 getIntent().removeExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY);
134 }
135
Eric Erfanianccca3152017-02-22 16:32:36 -0800136 if (icicle != null) {
137 didShowAnswerScreen = icicle.getBoolean(DID_SHOW_ANSWER_SCREEN_KEY);
138 didShowInCallScreen = icicle.getBoolean(DID_SHOW_IN_CALL_SCREEN_KEY);
139 didShowVideoCallScreen = icicle.getBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY);
140 }
141
142 common.onCreate(icicle);
143
144 getWindow()
145 .getDecorView()
146 .setSystemUiVisibility(
147 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
148
149 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700150 sendBroadcast(CallPendingActivity.getFinishBroadcast());
151 Trace.endSection();
weijiaxuc950a9b2017-11-06 16:39:04 -0800152 Logger.get(this)
153 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
154 Logger.get(this)
155 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
Eric Erfanianccca3152017-02-22 16:32:36 -0800156 }
157
158 @Override
159 protected void onSaveInstanceState(Bundle out) {
160 LogUtil.i("InCallActivity.onSaveInstanceState", "");
161 common.onSaveInstanceState(out);
162 out.putBoolean(DID_SHOW_ANSWER_SCREEN_KEY, didShowAnswerScreen);
163 out.putBoolean(DID_SHOW_IN_CALL_SCREEN_KEY, didShowInCallScreen);
164 out.putBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY, didShowVideoCallScreen);
165 super.onSaveInstanceState(out);
166 isVisible = false;
167 }
168
169 @Override
170 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700171 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800172 LogUtil.i("InCallActivity.onStart", "");
wangqi9982f0d2017-10-11 17:46:07 -0700173 Trace.beginSection("call super");
Eric Erfanianccca3152017-02-22 16:32:36 -0800174 super.onStart();
wangqi9982f0d2017-10-11 17:46:07 -0700175 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800176 isVisible = true;
177 showMainInCallFragment();
178 common.onStart();
179 if (ActivityCompat.isInMultiWindowMode(this)
180 && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
181 // Hide the dialpad because there may not be enough room
182 showDialpadFragment(false, false);
183 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700184 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800185 }
186
187 @Override
188 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700189 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800190 LogUtil.i("InCallActivity.onResume", "");
191 super.onResume();
192 common.onResume();
193 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
194 pseudoScreenState.addListener(this);
195 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700196 Trace.endSection();
weijiaxu650e7cc2017-10-31 12:38:54 -0700197 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
198 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800199 () ->
200 Logger.get(this)
201 .logRecordMemory(LoggingBindings.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700202 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800203 }
204
205 /** onPause is guaranteed to be called when the InCallActivity goes in the background. */
206 @Override
207 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700208 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800209 LogUtil.i("InCallActivity.onPause", "");
210 super.onPause();
211 common.onPause();
212 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700213 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800214 }
215
216 @Override
217 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700218 Trace.beginSection("InCallActivity.onStop");
Eric Erfanianccca3152017-02-22 16:32:36 -0800219 LogUtil.i("InCallActivity.onStop", "");
wangqi4d705e52017-09-28 12:23:35 -0700220 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800221 super.onStop();
222 common.onStop();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700223 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800224 }
225
226 @Override
227 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700228 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800229 LogUtil.i("InCallActivity.onDestroy", "");
230 super.onDestroy();
231 common.onDestroy();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700232 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800233 }
234
235 @Override
236 public void finish() {
237 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700238 // When user select incall ui from recents after the call is disconnected, it tries to launch
239 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
240 // crash.
241 // By calling finishAndRemoveTask() instead of finish() the task associated with
242 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
243 // this case.
244 //
245 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
246 // clear the task since there could be parent activity in the same task that's still alive.
247 // But InCallActivity is special since it's singleInstance which means it's root activity and
248 // only instance of activity in the task. So it should be safe to also remove task when
249 // finishing.
250 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
251 // finishes, the task should also be removed since it doesn't make sense to go back to it in
252 // anyway anymore.
253 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800254 }
255 }
256
257 private boolean shouldCloseActivityOnFinish() {
258 if (!isVisible()) {
259 LogUtil.i(
260 "InCallActivity.shouldCloseActivityOnFinish",
261 "allowing activity to be closed because it's not visible");
262 return true;
263 }
264
twyen8efb4952017-10-06 16:35:54 -0700265 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800266 LogUtil.i(
267 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700268 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800269 return false;
270 }
271
272 LogUtil.i(
273 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700274 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800275 return true;
276 }
277
278 @Override
279 protected void onNewIntent(Intent intent) {
280 LogUtil.i("InCallActivity.onNewIntent", "");
Eric Erfanianccca3152017-02-22 16:32:36 -0800281
282 // If the screen is off, we need to make sure it gets turned on for incoming calls.
283 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
284 // when the activity is first created. Therefore, to ensure the screen is turned on
285 // for the call waiting case, we recreate() the current activity. There should be no jank from
286 // this since the screen is already off and will remain so until our new activity is up.
287 if (!isVisible()) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700288 common.onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800289 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
290 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700291 } else {
292 common.onNewIntent(intent, false /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800293 }
294 }
295
296 @Override
297 public void onBackPressed() {
298 LogUtil.i("InCallActivity.onBackPressed", "");
299 if (!common.onBackPressed(didShowInCallScreen || didShowVideoCallScreen)) {
300 super.onBackPressed();
301 }
302 }
303
304 @Override
305 public boolean onOptionsItemSelected(MenuItem item) {
306 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
307 if (item.getItemId() == android.R.id.home) {
308 onBackPressed();
309 return true;
310 }
311 return super.onOptionsItemSelected(item);
312 }
313
314 @Override
315 public boolean onKeyUp(int keyCode, KeyEvent event) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700316 return common.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800317 }
318
319 @Override
320 public boolean onKeyDown(int keyCode, KeyEvent event) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700321 return common.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800322 }
323
324 public boolean isInCallScreenAnimating() {
325 return false;
326 }
327
328 public void showConferenceFragment(boolean show) {
329 if (show) {
330 startActivity(new Intent(this, ManageConferenceActivity.class));
331 }
332 }
333
334 public boolean showDialpadFragment(boolean show, boolean animate) {
335 boolean didChange = common.showDialpadFragment(show, animate);
336 if (didChange) {
337 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
338 // repositions itself.
339 getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
340 }
341 return didChange;
342 }
343
344 public boolean isDialpadVisible() {
345 return common.isDialpadVisible();
346 }
347
348 public void onForegroundCallChanged(DialerCall newForegroundCall) {
349 common.updateTaskDescription();
350 if (didShowAnswerScreen && newForegroundCall != null) {
351 if (newForegroundCall.getState() == State.DISCONNECTED
352 || newForegroundCall.getState() == State.IDLE) {
353 LogUtil.i(
354 "InCallActivity.onForegroundCallChanged",
355 "rejecting incoming call, not updating " + "window background color");
356 }
357 } else {
358 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
359 updateWindowBackgroundColor(0);
360 }
361 }
362
363 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
364 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
365 @ColorInt int top;
366 @ColorInt int middle;
367 @ColorInt int bottom;
368 @ColorInt int gray = 0x66000000;
369
370 if (ActivityCompat.isInMultiWindowMode(this)) {
371 top = themeColorManager.getBackgroundColorSolid();
372 middle = themeColorManager.getBackgroundColorSolid();
373 bottom = themeColorManager.getBackgroundColorSolid();
374 } else {
375 top = themeColorManager.getBackgroundColorTop();
376 middle = themeColorManager.getBackgroundColorMiddle();
377 bottom = themeColorManager.getBackgroundColorBottom();
378 }
379
380 if (progress < 0) {
381 float correctedProgress = Math.abs(progress);
382 top = ColorUtils.blendARGB(top, gray, correctedProgress);
383 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
384 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
385 }
386
387 boolean backgroundDirty = false;
388 if (backgroundDrawable == null) {
389 backgroundDrawableColors = new int[] {top, middle, bottom};
390 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
391 backgroundDirty = true;
392 } else {
393 if (backgroundDrawableColors[0] != top) {
394 backgroundDrawableColors[0] = top;
395 backgroundDirty = true;
396 }
397 if (backgroundDrawableColors[1] != middle) {
398 backgroundDrawableColors[1] = middle;
399 backgroundDirty = true;
400 }
401 if (backgroundDrawableColors[2] != bottom) {
402 backgroundDrawableColors[2] = bottom;
403 backgroundDirty = true;
404 }
405 if (backgroundDirty) {
406 backgroundDrawable.setColors(backgroundDrawableColors);
407 }
408 }
409
410 if (backgroundDirty) {
411 getWindow().setBackgroundDrawable(backgroundDrawable);
412 }
413 }
414
415 public boolean isVisible() {
416 return isVisible;
417 }
418
419 public boolean getCallCardFragmentVisible() {
420 return didShowInCallScreen || didShowVideoCallScreen;
421 }
422
423 public void dismissKeyguard(boolean dismiss) {
linyuh9c327da2017-11-14 12:33:48 -0800424 if (dismissKeyguard == dismiss) {
425 return;
426 }
427
428 dismissKeyguard = dismiss;
429 if (dismiss) {
430 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
431 } else {
432 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
433 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800434 }
435
436 public void showPostCharWaitDialog(String callId, String chars) {
437 common.showPostCharWaitDialog(callId, chars);
438 }
439
Eric Erfanian2ca43182017-08-31 06:57:16 -0700440 public void maybeShowErrorDialogOnDisconnect(DisconnectMessage disconnectMessage) {
441 common.maybeShowErrorDialogOnDisconnect(disconnectMessage);
Eric Erfanianccca3152017-02-22 16:32:36 -0800442 }
443
444 public void dismissPendingDialogs() {
445 if (isVisible) {
446 LogUtil.i("InCallActivity.dismissPendingDialogs", "");
447 common.dismissPendingDialogs();
448 AnswerScreen answerScreen = getAnswerScreen();
449 if (answerScreen != null) {
450 answerScreen.dismissPendingDialogs();
451 }
452 needDismissPendingDialogs = false;
453 } else {
454 // The activity is not visible and onSaveInstanceState may have been called so defer the
455 // dismissing action.
456 LogUtil.i(
457 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
458 needDismissPendingDialogs = true;
459 }
460 }
461
462 private void enableInCallOrientationEventListener(boolean enable) {
463 common.enableInCallOrientationEventListener(enable);
464 }
465
466 public void setExcludeFromRecents(boolean exclude) {
467 common.setExcludeFromRecents(exclude);
468 }
469
Eric Erfanianccca3152017-02-22 16:32:36 -0800470 @Nullable
471 public FragmentManager getDialpadFragmentManager() {
472 InCallScreen inCallScreen = getInCallScreen();
473 if (inCallScreen != null) {
474 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
475 }
476 return null;
477 }
478
479 public int getDialpadContainerId() {
480 return getInCallScreen().getAnswerAndDialpadContainerResourceId();
481 }
482
483 @Override
484 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
485 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
486 if (call == null) {
487 // This is a work around for a bug where we attempt to create a new delegate after the call
488 // has already been removed. An example of when this can happen is:
489 // 1. incoming video call in landscape mode
490 // 2. remote party hangs up
491 // 3. activity switches from landscape to portrait
492 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
493 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
494 // because this new state is transient and the activity will be destroyed soon.
495 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
496 return new AnswerScreenPresenterStub();
497 } else {
498 return new AnswerScreenPresenter(
499 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
500 }
501 }
502
503 @Override
504 public InCallScreenDelegate newInCallScreenDelegate() {
505 return new CallCardPresenter(this);
506 }
507
508 @Override
509 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
510 return new CallButtonPresenter(this);
511 }
512
513 @Override
Eric Erfanian90508232017-03-24 09:31:16 -0700514 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
515 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
516 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
517 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
518 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800519 return new VideoCallPresenter();
520 }
521
522 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700523 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -0800524 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "");
525 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700526 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800527 }
528
529 public void onWiFiToLteHandover(DialerCall call) {
530 common.showWifiToLteHandoverToast(call);
531 }
532
533 public void onHandoverToWifiFailed(DialerCall call) {
534 common.showWifiFailedDialog(call);
535 }
536
Eric Erfanianc857f902017-05-15 14:05:33 -0700537 public void onInternationalCallOnWifi(@NonNull DialerCall call) {
538 LogUtil.enterBlock("InCallActivity.onInternationalCallOnWifi");
539 common.showInternationalCallOnWifiDialog(call);
540 }
541
Eric Erfanian938468d2017-10-24 14:05:52 -0700542 @Override
543 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
544 super.onMultiWindowModeChanged(isInMultiWindowMode);
545 if (!isInMultiWindowMode) {
546 common.updateNavigationBar(isDialpadVisible());
547 }
548 }
549
Eric Erfanianccca3152017-02-22 16:32:36 -0800550 public void setAllowOrientationChange(boolean allowOrientationChange) {
wangqi9982f0d2017-10-11 17:46:07 -0700551 if (this.allowOrientationChange == allowOrientationChange) {
552 return;
553 }
554 this.allowOrientationChange = allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -0800555 if (!allowOrientationChange) {
556 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
557 } else {
558 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
559 }
560 enableInCallOrientationEventListener(allowOrientationChange);
561 }
562
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700563 public void hideMainInCallFragment() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800564 LogUtil.i("InCallActivity.hideMainInCallFragment", "");
565 if (didShowInCallScreen || didShowVideoCallScreen) {
566 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
567 hideInCallScreenFragment(transaction);
568 hideVideoCallScreenFragment(transaction);
569 transaction.commitAllowingStateLoss();
570 getSupportFragmentManager().executePendingTransactions();
571 }
572 }
573
574 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700575 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800576 // If the activity's onStart method hasn't been called yet then defer doing any work.
577 if (!isVisible) {
578 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700579 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800580 return;
581 }
582
583 // Don't let this be reentrant.
584 if (isInShowMainInCallFragment) {
585 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700586 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 return;
588 }
589
590 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700591 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
592 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
Eric Erfanianccca3152017-02-22 16:32:36 -0800593 LogUtil.i(
594 "InCallActivity.showMainInCallFragment",
595 "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, "
596 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b",
597 shouldShowAnswerUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -0700598 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -0800599 didShowAnswerScreen,
600 didShowInCallScreen,
601 didShowVideoCallScreen);
602 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700603 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -0800604
605 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
606 boolean didChangeInCall;
607 boolean didChangeVideo;
608 boolean didChangeAnswer;
609 if (shouldShowAnswerUi.shouldShow) {
610 didChangeInCall = hideInCallScreenFragment(transaction);
611 didChangeVideo = hideVideoCallScreenFragment(transaction);
612 didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700613 } else if (shouldShowVideoUi.shouldShow) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800614 didChangeInCall = hideInCallScreenFragment(transaction);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700615 didChangeVideo = showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800616 didChangeAnswer = hideAnswerScreenFragment(transaction);
617 } else {
618 didChangeInCall = showInCallScreenFragment(transaction);
619 didChangeVideo = hideVideoCallScreenFragment(transaction);
620 didChangeAnswer = hideAnswerScreenFragment(transaction);
621 }
622
623 if (didChangeInCall || didChangeVideo || didChangeAnswer) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700624 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -0800625 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700626 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800627 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
628 }
629 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700630 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800631 }
632
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700633 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800634 DialerCall call = CallList.getInstance().getIncomingCall();
635 if (call != null) {
636 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700637 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800638 }
639
640 call = CallList.getInstance().getVideoUpgradeRequestCall();
641 if (call != null) {
642 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700643 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800644 }
645
646 // Check if we're showing the answer screen and the call is disconnected. If this condition is
647 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
648 // the user rejects an incoming call.
649 call = CallList.getInstance().getFirstCall();
650 if (call == null) {
651 call = CallList.getInstance().getBackgroundCall();
652 }
653 if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
654 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700655 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800656 }
657
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700658 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800659 }
660
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700661 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800662 DialerCall call = CallList.getInstance().getFirstCall();
663 if (call == null) {
664 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700665 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800666 }
667
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700668 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800669 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700670 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800671 }
672
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700673 if (call.hasSentVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800674 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700675 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800676 }
677
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700678 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800679 }
680
681 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
682 // When rejecting a call the active call can become null in which case we should continue
683 // showing the answer screen.
684 if (didShowAnswerScreen && call == null) {
685 return false;
686 }
687
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700688 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
689
690 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -0800691
692 // Check if we're already showing an answer screen for this call.
693 if (didShowAnswerScreen) {
694 AnswerScreen answerScreen = getAnswerScreen();
695 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700696 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -0700697 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
698 && !answerScreen.isActionTimeout()) {
699 LogUtil.d(
700 "InCallActivity.showAnswerScreenFragment",
701 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -0800702 return false;
703 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700704 if (answerScreen.isActionTimeout()) {
705 LogUtil.i(
706 "InCallActivity.showAnswerScreenFragment",
707 "answer fragment exists but has been accepted/rejected and timed out");
708 } else {
709 LogUtil.i(
710 "InCallActivity.showAnswerScreenFragment",
711 "answer fragment exists but arguments do not match");
712 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800713 hideAnswerScreenFragment(transaction);
714 }
715
716 // Show a new answer screen.
717 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -0700718 AnswerBindings.createAnswerScreen(
719 call.getId(),
720 call.isVideoCall(),
721 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -0700722 call.getVideoTech().isSelfManagedCamera(),
723 shouldAllowAnswerAndRelease(call),
724 CallList.getInstance().getBackgroundCall() != null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800725 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), TAG_ANSWER_SCREEN);
726
727 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
728 didShowAnswerScreen = true;
729 return true;
730 }
731
Eric Erfanian90508232017-03-24 09:31:16 -0700732 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
733 if (CallList.getInstance().getActiveCall() == null) {
734 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
735 return false;
736 }
737 if (getSystemService(TelephonyManager.class).getPhoneType()
738 == TelephonyManager.PHONE_TYPE_CDMA) {
739 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
740 return false;
741 }
742 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
743 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
744 return false;
745 }
746 if (!ConfigProviderBindings.get(this).getBoolean(CONFIG_ANSWER_AND_RELEASE_ENABLED, true)) {
747 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
748 return false;
749 }
750
751 return true;
752 }
753
Eric Erfanianccca3152017-02-22 16:32:36 -0800754 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
755 if (!didShowAnswerScreen) {
756 return false;
757 }
758 AnswerScreen answerScreen = getAnswerScreen();
759 if (answerScreen != null) {
760 transaction.remove(answerScreen.getAnswerScreenFragment());
761 }
762
763 didShowAnswerScreen = false;
764 return true;
765 }
766
767 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
768 if (didShowInCallScreen) {
769 return false;
770 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700771 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
772 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), TAG_IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -0800773 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
774 didShowInCallScreen = true;
775 return true;
776 }
777
778 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
779 if (!didShowInCallScreen) {
780 return false;
781 }
782 InCallScreen inCallScreen = getInCallScreen();
783 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700784 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -0800785 }
786 didShowInCallScreen = false;
787 return true;
788 }
789
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700790 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800791 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700792 VideoCallScreen videoCallScreen = getVideoCallScreen();
793 if (videoCallScreen.getCallId().equals(call.getId())) {
794 return false;
795 }
796 LogUtil.i(
797 "InCallActivity.showVideoCallScreenFragment",
798 "video call fragment exists but arguments do not match");
799 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -0800800 }
801
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700802 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
803
Eric Erfanian90508232017-03-24 09:31:16 -0700804 VideoCallScreen videoCallScreen =
805 VideoBindings.createVideoCallScreen(
806 call.getId(), call.getVideoTech().shouldUseSurfaceView());
Eric Erfanianccca3152017-02-22 16:32:36 -0800807 transaction.add(R.id.main, videoCallScreen.getVideoCallScreenFragment(), TAG_VIDEO_CALL_SCREEN);
808
809 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
810 didShowVideoCallScreen = true;
811 return true;
812 }
813
814 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
815 if (!didShowVideoCallScreen) {
816 return false;
817 }
818 VideoCallScreen videoCallScreen = getVideoCallScreen();
819 if (videoCallScreen != null) {
820 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
821 }
822 didShowVideoCallScreen = false;
823 return true;
824 }
825
826 AnswerScreen getAnswerScreen() {
827 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(TAG_ANSWER_SCREEN);
828 }
829
830 InCallScreen getInCallScreen() {
831 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_IN_CALL_SCREEN);
832 }
833
834 VideoCallScreen getVideoCallScreen() {
835 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_VIDEO_CALL_SCREEN);
836 }
837
838 @Override
839 public void onPseudoScreenStateChanged(boolean isOn) {
840 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
841 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
842 }
843
844 /**
845 * For some touch related issue, turning off the screen can be faked by drawing a black view over
846 * the activity. All touch events started when the screen is "off" is rejected.
847 *
848 * @see PseudoScreenState
849 */
850 @Override
851 public boolean dispatchTouchEvent(MotionEvent event) {
852 // Reject any gesture that started when the screen is in the fake off state.
853 if (touchDownWhenPseudoScreenOff) {
854 if (event.getAction() == MotionEvent.ACTION_UP) {
855 touchDownWhenPseudoScreenOff = false;
856 }
857 return true;
858 }
859 // Reject all touch event when the screen is in the fake off state.
860 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
861 if (event.getAction() == MotionEvent.ACTION_DOWN) {
862 touchDownWhenPseudoScreenOff = true;
863 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
864 }
865 return true;
866 }
867 return super.dispatchTouchEvent(event);
868 }
869
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700870 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -0800871 public final boolean shouldShow;
872 public final DialerCall call;
873
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700874 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800875 this.shouldShow = shouldShow;
876 this.call = call;
877 }
878 }
879}