blob: 2ba4d98a184ad3160f57172edb6c57ee65367ca4 [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;
39import com.android.dialer.compat.ActivityCompat;
Eric Erfanian2ca43182017-08-31 06:57:16 -070040import com.android.dialer.configprovider.ConfigProviderBindings;
41import com.android.dialer.logging.DialerImpression;
Eric Erfanianccca3152017-02-22 16:32:36 -080042import com.android.dialer.logging.Logger;
Eric Erfanian8369df02017-05-03 10:27:13 -070043import com.android.dialer.logging.ScreenEvent;
Eric Erfanianccca3152017-02-22 16:32:36 -080044import com.android.incallui.answer.bindings.AnswerBindings;
45import com.android.incallui.answer.protocol.AnswerScreen;
46import com.android.incallui.answer.protocol.AnswerScreenDelegate;
47import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
48import com.android.incallui.answerproximitysensor.PseudoScreenState;
49import com.android.incallui.call.CallList;
50import com.android.incallui.call.DialerCall;
51import com.android.incallui.call.DialerCall.State;
Eric Erfanian2ca43182017-08-31 06:57:16 -070052import com.android.incallui.callpending.CallPendingActivity;
53import com.android.incallui.disconnectdialog.DisconnectMessage;
Eric Erfanianccca3152017-02-22 16:32:36 -080054import com.android.incallui.incall.bindings.InCallBindings;
55import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
56import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
57import com.android.incallui.incall.protocol.InCallScreen;
58import com.android.incallui.incall.protocol.InCallScreenDelegate;
59import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
60import com.android.incallui.video.bindings.VideoBindings;
61import com.android.incallui.video.protocol.VideoCallScreen;
62import com.android.incallui.video.protocol.VideoCallScreenDelegate;
63import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
64
65/** Version of {@link InCallActivity} that shows the new UI */
66public class InCallActivity extends TransactionSafeFragmentActivity
67 implements AnswerScreenDelegateFactory,
68 InCallScreenDelegateFactory,
69 InCallButtonUiDelegateFactory,
70 VideoCallScreenDelegateFactory,
71 PseudoScreenState.StateChangedListener {
72
Eric Erfanian2ca43182017-08-31 06:57:16 -070073 public static final int PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN = 0;
74 public static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1;
75 public static final int PENDING_INTENT_REQUEST_CODE_BUBBLE = 2;
76
Eric Erfanianccca3152017-02-22 16:32:36 -080077 private static final String TAG_IN_CALL_SCREEN = "tag_in_call_screen";
78 private static final String TAG_ANSWER_SCREEN = "tag_answer_screen";
79 private static final String TAG_VIDEO_CALL_SCREEN = "tag_video_call_screen";
80
81 private static final String DID_SHOW_ANSWER_SCREEN_KEY = "did_show_answer_screen";
82 private static final String DID_SHOW_IN_CALL_SCREEN_KEY = "did_show_in_call_screen";
83 private static final String DID_SHOW_VIDEO_CALL_SCREEN_KEY = "did_show_video_call_screen";
84
Eric Erfanian90508232017-03-24 09:31:16 -070085 private static final String CONFIG_ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
86
Eric Erfanianccca3152017-02-22 16:32:36 -080087 private final InCallActivityCommon common;
88 private boolean didShowAnswerScreen;
89 private boolean didShowInCallScreen;
90 private boolean didShowVideoCallScreen;
91 private int[] backgroundDrawableColors;
92 private GradientDrawable backgroundDrawable;
93 private boolean isVisible;
94 private View pseudoBlackScreenOverlay;
95 private boolean touchDownWhenPseudoScreenOff;
96 private boolean isInShowMainInCallFragment;
97 private boolean needDismissPendingDialogs;
98
99 public InCallActivity() {
100 common = new InCallActivityCommon(this);
101 }
102
103 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700104 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800105 Intent intent = new Intent(Intent.ACTION_MAIN, null);
106 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
107 intent.setClass(context, InCallActivity.class);
108 InCallActivityCommon.setIntentExtras(intent, showDialpad, newOutgoingCall, isForFullScreen);
109 return intent;
110 }
111
112 @Override
113 protected void onResumeFragments() {
114 super.onResumeFragments();
115 if (needDismissPendingDialogs) {
116 dismissPendingDialogs();
117 }
118 }
119
120 @Override
121 protected void onCreate(Bundle icicle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700122 Trace.beginSection("InCallActivity.onCreate");
Eric Erfanianccca3152017-02-22 16:32:36 -0800123 LogUtil.i("InCallActivity.onCreate", "");
124 super.onCreate(icicle);
125
Eric Erfanian2ca43182017-08-31 06:57:16 -0700126 if (getIntent().getBooleanExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY, false)) {
127 Logger.get(this).logImpression(DialerImpression.Type.BUBBLE_PRIMARY_BUTTON_RETURN_TO_CALL);
128 getIntent().removeExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY);
129 }
130
Eric Erfanianccca3152017-02-22 16:32:36 -0800131 if (icicle != null) {
132 didShowAnswerScreen = icicle.getBoolean(DID_SHOW_ANSWER_SCREEN_KEY);
133 didShowInCallScreen = icicle.getBoolean(DID_SHOW_IN_CALL_SCREEN_KEY);
134 didShowVideoCallScreen = icicle.getBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY);
135 }
136
137 common.onCreate(icicle);
138
139 getWindow()
140 .getDecorView()
141 .setSystemUiVisibility(
142 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
143
144 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700145 sendBroadcast(CallPendingActivity.getFinishBroadcast());
146 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800147 }
148
149 @Override
150 protected void onSaveInstanceState(Bundle out) {
151 LogUtil.i("InCallActivity.onSaveInstanceState", "");
152 common.onSaveInstanceState(out);
153 out.putBoolean(DID_SHOW_ANSWER_SCREEN_KEY, didShowAnswerScreen);
154 out.putBoolean(DID_SHOW_IN_CALL_SCREEN_KEY, didShowInCallScreen);
155 out.putBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY, didShowVideoCallScreen);
156 super.onSaveInstanceState(out);
157 isVisible = false;
158 }
159
160 @Override
161 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700162 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800163 LogUtil.i("InCallActivity.onStart", "");
164 super.onStart();
165 isVisible = true;
166 showMainInCallFragment();
167 common.onStart();
168 if (ActivityCompat.isInMultiWindowMode(this)
169 && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
170 // Hide the dialpad because there may not be enough room
171 showDialpadFragment(false, false);
172 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700173 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800174 }
175
176 @Override
177 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700178 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800179 LogUtil.i("InCallActivity.onResume", "");
180 super.onResume();
181 common.onResume();
182 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
183 pseudoScreenState.addListener(this);
184 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700185 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800186 }
187
188 /** onPause is guaranteed to be called when the InCallActivity goes in the background. */
189 @Override
190 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700191 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800192 LogUtil.i("InCallActivity.onPause", "");
193 super.onPause();
194 common.onPause();
195 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700196 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800197 }
198
199 @Override
200 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700201 Trace.beginSection("InCallActivity.onStop");
Eric Erfanianccca3152017-02-22 16:32:36 -0800202 LogUtil.i("InCallActivity.onStop", "");
wangqi4d705e52017-09-28 12:23:35 -0700203 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800204 super.onStop();
205 common.onStop();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700206 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800207 }
208
209 @Override
210 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700211 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800212 LogUtil.i("InCallActivity.onDestroy", "");
213 super.onDestroy();
214 common.onDestroy();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700215 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800216 }
217
218 @Override
219 public void finish() {
220 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700221 // When user select incall ui from recents after the call is disconnected, it tries to launch
222 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
223 // crash.
224 // By calling finishAndRemoveTask() instead of finish() the task associated with
225 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
226 // this case.
227 //
228 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
229 // clear the task since there could be parent activity in the same task that's still alive.
230 // But InCallActivity is special since it's singleInstance which means it's root activity and
231 // only instance of activity in the task. So it should be safe to also remove task when
232 // finishing.
233 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
234 // finishes, the task should also be removed since it doesn't make sense to go back to it in
235 // anyway anymore.
236 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800237 }
238 }
239
240 private boolean shouldCloseActivityOnFinish() {
241 if (!isVisible()) {
242 LogUtil.i(
243 "InCallActivity.shouldCloseActivityOnFinish",
244 "allowing activity to be closed because it's not visible");
245 return true;
246 }
247
twyen8efb4952017-10-06 16:35:54 -0700248 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800249 LogUtil.i(
250 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700251 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800252 return false;
253 }
254
255 LogUtil.i(
256 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700257 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800258 return true;
259 }
260
261 @Override
262 protected void onNewIntent(Intent intent) {
263 LogUtil.i("InCallActivity.onNewIntent", "");
Eric Erfanianccca3152017-02-22 16:32:36 -0800264
265 // If the screen is off, we need to make sure it gets turned on for incoming calls.
266 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
267 // when the activity is first created. Therefore, to ensure the screen is turned on
268 // for the call waiting case, we recreate() the current activity. There should be no jank from
269 // this since the screen is already off and will remain so until our new activity is up.
270 if (!isVisible()) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700271 common.onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800272 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
273 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700274 } else {
275 common.onNewIntent(intent, false /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800276 }
277 }
278
279 @Override
280 public void onBackPressed() {
281 LogUtil.i("InCallActivity.onBackPressed", "");
282 if (!common.onBackPressed(didShowInCallScreen || didShowVideoCallScreen)) {
283 super.onBackPressed();
284 }
285 }
286
287 @Override
288 public boolean onOptionsItemSelected(MenuItem item) {
289 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
290 if (item.getItemId() == android.R.id.home) {
291 onBackPressed();
292 return true;
293 }
294 return super.onOptionsItemSelected(item);
295 }
296
297 @Override
298 public boolean onKeyUp(int keyCode, KeyEvent event) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700299 return common.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800300 }
301
302 @Override
303 public boolean onKeyDown(int keyCode, KeyEvent event) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700304 return common.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800305 }
306
307 public boolean isInCallScreenAnimating() {
308 return false;
309 }
310
311 public void showConferenceFragment(boolean show) {
312 if (show) {
313 startActivity(new Intent(this, ManageConferenceActivity.class));
314 }
315 }
316
317 public boolean showDialpadFragment(boolean show, boolean animate) {
318 boolean didChange = common.showDialpadFragment(show, animate);
319 if (didChange) {
320 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
321 // repositions itself.
322 getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
323 }
324 return didChange;
325 }
326
327 public boolean isDialpadVisible() {
328 return common.isDialpadVisible();
329 }
330
331 public void onForegroundCallChanged(DialerCall newForegroundCall) {
332 common.updateTaskDescription();
333 if (didShowAnswerScreen && newForegroundCall != null) {
334 if (newForegroundCall.getState() == State.DISCONNECTED
335 || newForegroundCall.getState() == State.IDLE) {
336 LogUtil.i(
337 "InCallActivity.onForegroundCallChanged",
338 "rejecting incoming call, not updating " + "window background color");
339 }
340 } else {
341 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
342 updateWindowBackgroundColor(0);
343 }
344 }
345
346 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
347 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
348 @ColorInt int top;
349 @ColorInt int middle;
350 @ColorInt int bottom;
351 @ColorInt int gray = 0x66000000;
352
353 if (ActivityCompat.isInMultiWindowMode(this)) {
354 top = themeColorManager.getBackgroundColorSolid();
355 middle = themeColorManager.getBackgroundColorSolid();
356 bottom = themeColorManager.getBackgroundColorSolid();
357 } else {
358 top = themeColorManager.getBackgroundColorTop();
359 middle = themeColorManager.getBackgroundColorMiddle();
360 bottom = themeColorManager.getBackgroundColorBottom();
361 }
362
363 if (progress < 0) {
364 float correctedProgress = Math.abs(progress);
365 top = ColorUtils.blendARGB(top, gray, correctedProgress);
366 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
367 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
368 }
369
370 boolean backgroundDirty = false;
371 if (backgroundDrawable == null) {
372 backgroundDrawableColors = new int[] {top, middle, bottom};
373 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
374 backgroundDirty = true;
375 } else {
376 if (backgroundDrawableColors[0] != top) {
377 backgroundDrawableColors[0] = top;
378 backgroundDirty = true;
379 }
380 if (backgroundDrawableColors[1] != middle) {
381 backgroundDrawableColors[1] = middle;
382 backgroundDirty = true;
383 }
384 if (backgroundDrawableColors[2] != bottom) {
385 backgroundDrawableColors[2] = bottom;
386 backgroundDirty = true;
387 }
388 if (backgroundDirty) {
389 backgroundDrawable.setColors(backgroundDrawableColors);
390 }
391 }
392
393 if (backgroundDirty) {
394 getWindow().setBackgroundDrawable(backgroundDrawable);
395 }
396 }
397
398 public boolean isVisible() {
399 return isVisible;
400 }
401
402 public boolean getCallCardFragmentVisible() {
403 return didShowInCallScreen || didShowVideoCallScreen;
404 }
405
406 public void dismissKeyguard(boolean dismiss) {
407 common.dismissKeyguard(dismiss);
408 }
409
410 public void showPostCharWaitDialog(String callId, String chars) {
411 common.showPostCharWaitDialog(callId, chars);
412 }
413
Eric Erfanian2ca43182017-08-31 06:57:16 -0700414 public void maybeShowErrorDialogOnDisconnect(DisconnectMessage disconnectMessage) {
415 common.maybeShowErrorDialogOnDisconnect(disconnectMessage);
Eric Erfanianccca3152017-02-22 16:32:36 -0800416 }
417
418 public void dismissPendingDialogs() {
419 if (isVisible) {
420 LogUtil.i("InCallActivity.dismissPendingDialogs", "");
421 common.dismissPendingDialogs();
422 AnswerScreen answerScreen = getAnswerScreen();
423 if (answerScreen != null) {
424 answerScreen.dismissPendingDialogs();
425 }
426 needDismissPendingDialogs = false;
427 } else {
428 // The activity is not visible and onSaveInstanceState may have been called so defer the
429 // dismissing action.
430 LogUtil.i(
431 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
432 needDismissPendingDialogs = true;
433 }
434 }
435
436 private void enableInCallOrientationEventListener(boolean enable) {
437 common.enableInCallOrientationEventListener(enable);
438 }
439
440 public void setExcludeFromRecents(boolean exclude) {
441 common.setExcludeFromRecents(exclude);
442 }
443
Eric Erfanianccca3152017-02-22 16:32:36 -0800444 @Nullable
445 public FragmentManager getDialpadFragmentManager() {
446 InCallScreen inCallScreen = getInCallScreen();
447 if (inCallScreen != null) {
448 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
449 }
450 return null;
451 }
452
453 public int getDialpadContainerId() {
454 return getInCallScreen().getAnswerAndDialpadContainerResourceId();
455 }
456
457 @Override
458 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
459 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
460 if (call == null) {
461 // This is a work around for a bug where we attempt to create a new delegate after the call
462 // has already been removed. An example of when this can happen is:
463 // 1. incoming video call in landscape mode
464 // 2. remote party hangs up
465 // 3. activity switches from landscape to portrait
466 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
467 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
468 // because this new state is transient and the activity will be destroyed soon.
469 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
470 return new AnswerScreenPresenterStub();
471 } else {
472 return new AnswerScreenPresenter(
473 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
474 }
475 }
476
477 @Override
478 public InCallScreenDelegate newInCallScreenDelegate() {
479 return new CallCardPresenter(this);
480 }
481
482 @Override
483 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
484 return new CallButtonPresenter(this);
485 }
486
487 @Override
Eric Erfanian90508232017-03-24 09:31:16 -0700488 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
489 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
490 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
491 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
492 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800493 return new VideoCallPresenter();
494 }
495
496 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700497 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -0800498 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "");
499 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700500 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800501 }
502
503 public void onWiFiToLteHandover(DialerCall call) {
504 common.showWifiToLteHandoverToast(call);
505 }
506
507 public void onHandoverToWifiFailed(DialerCall call) {
508 common.showWifiFailedDialog(call);
509 }
510
Eric Erfanianc857f902017-05-15 14:05:33 -0700511 public void onInternationalCallOnWifi(@NonNull DialerCall call) {
512 LogUtil.enterBlock("InCallActivity.onInternationalCallOnWifi");
513 common.showInternationalCallOnWifiDialog(call);
514 }
515
Eric Erfanianccca3152017-02-22 16:32:36 -0800516 public void setAllowOrientationChange(boolean allowOrientationChange) {
517 if (!allowOrientationChange) {
518 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
519 } else {
520 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
521 }
522 enableInCallOrientationEventListener(allowOrientationChange);
523 }
524
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700525 public void hideMainInCallFragment() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800526 LogUtil.i("InCallActivity.hideMainInCallFragment", "");
527 if (didShowInCallScreen || didShowVideoCallScreen) {
528 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
529 hideInCallScreenFragment(transaction);
530 hideVideoCallScreenFragment(transaction);
531 transaction.commitAllowingStateLoss();
532 getSupportFragmentManager().executePendingTransactions();
533 }
534 }
535
536 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700537 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800538 // If the activity's onStart method hasn't been called yet then defer doing any work.
539 if (!isVisible) {
540 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700541 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800542 return;
543 }
544
545 // Don't let this be reentrant.
546 if (isInShowMainInCallFragment) {
547 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700548 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800549 return;
550 }
551
552 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700553 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
554 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
Eric Erfanianccca3152017-02-22 16:32:36 -0800555 LogUtil.i(
556 "InCallActivity.showMainInCallFragment",
557 "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, "
558 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b",
559 shouldShowAnswerUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -0700560 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -0800561 didShowAnswerScreen,
562 didShowInCallScreen,
563 didShowVideoCallScreen);
564 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700565 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -0800566
567 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
568 boolean didChangeInCall;
569 boolean didChangeVideo;
570 boolean didChangeAnswer;
571 if (shouldShowAnswerUi.shouldShow) {
572 didChangeInCall = hideInCallScreenFragment(transaction);
573 didChangeVideo = hideVideoCallScreenFragment(transaction);
574 didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700575 } else if (shouldShowVideoUi.shouldShow) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800576 didChangeInCall = hideInCallScreenFragment(transaction);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700577 didChangeVideo = showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800578 didChangeAnswer = hideAnswerScreenFragment(transaction);
579 } else {
580 didChangeInCall = showInCallScreenFragment(transaction);
581 didChangeVideo = hideVideoCallScreenFragment(transaction);
582 didChangeAnswer = hideAnswerScreenFragment(transaction);
583 }
584
585 if (didChangeInCall || didChangeVideo || didChangeAnswer) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700586 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -0800587 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700588 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800589 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
590 }
591 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700592 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800593 }
594
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700595 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800596 DialerCall call = CallList.getInstance().getIncomingCall();
597 if (call != null) {
598 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700599 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800600 }
601
602 call = CallList.getInstance().getVideoUpgradeRequestCall();
603 if (call != null) {
604 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700605 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800606 }
607
608 // Check if we're showing the answer screen and the call is disconnected. If this condition is
609 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
610 // the user rejects an incoming call.
611 call = CallList.getInstance().getFirstCall();
612 if (call == null) {
613 call = CallList.getInstance().getBackgroundCall();
614 }
615 if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
616 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700617 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800618 }
619
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700620 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800621 }
622
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700623 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800624 DialerCall call = CallList.getInstance().getFirstCall();
625 if (call == null) {
626 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700627 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800628 }
629
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700630 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800631 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700632 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800633 }
634
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700635 if (call.hasSentVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800636 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700637 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800638 }
639
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700640 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800641 }
642
643 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
644 // When rejecting a call the active call can become null in which case we should continue
645 // showing the answer screen.
646 if (didShowAnswerScreen && call == null) {
647 return false;
648 }
649
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700650 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
651
652 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -0800653
654 // Check if we're already showing an answer screen for this call.
655 if (didShowAnswerScreen) {
656 AnswerScreen answerScreen = getAnswerScreen();
657 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700658 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -0700659 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
660 && !answerScreen.isActionTimeout()) {
661 LogUtil.d(
662 "InCallActivity.showAnswerScreenFragment",
663 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -0800664 return false;
665 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700666 if (answerScreen.isActionTimeout()) {
667 LogUtil.i(
668 "InCallActivity.showAnswerScreenFragment",
669 "answer fragment exists but has been accepted/rejected and timed out");
670 } else {
671 LogUtil.i(
672 "InCallActivity.showAnswerScreenFragment",
673 "answer fragment exists but arguments do not match");
674 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800675 hideAnswerScreenFragment(transaction);
676 }
677
678 // Show a new answer screen.
679 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -0700680 AnswerBindings.createAnswerScreen(
681 call.getId(),
682 call.isVideoCall(),
683 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -0700684 call.getVideoTech().isSelfManagedCamera(),
685 shouldAllowAnswerAndRelease(call),
686 CallList.getInstance().getBackgroundCall() != null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800687 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), TAG_ANSWER_SCREEN);
688
689 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
690 didShowAnswerScreen = true;
691 return true;
692 }
693
Eric Erfanian90508232017-03-24 09:31:16 -0700694 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
695 if (CallList.getInstance().getActiveCall() == null) {
696 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
697 return false;
698 }
699 if (getSystemService(TelephonyManager.class).getPhoneType()
700 == TelephonyManager.PHONE_TYPE_CDMA) {
701 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
702 return false;
703 }
704 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
705 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
706 return false;
707 }
708 if (!ConfigProviderBindings.get(this).getBoolean(CONFIG_ANSWER_AND_RELEASE_ENABLED, true)) {
709 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
710 return false;
711 }
712
713 return true;
714 }
715
Eric Erfanianccca3152017-02-22 16:32:36 -0800716 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
717 if (!didShowAnswerScreen) {
718 return false;
719 }
720 AnswerScreen answerScreen = getAnswerScreen();
721 if (answerScreen != null) {
722 transaction.remove(answerScreen.getAnswerScreenFragment());
723 }
724
725 didShowAnswerScreen = false;
726 return true;
727 }
728
729 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
730 if (didShowInCallScreen) {
731 return false;
732 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700733 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
734 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), TAG_IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -0800735 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
736 didShowInCallScreen = true;
737 return true;
738 }
739
740 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
741 if (!didShowInCallScreen) {
742 return false;
743 }
744 InCallScreen inCallScreen = getInCallScreen();
745 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700746 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -0800747 }
748 didShowInCallScreen = false;
749 return true;
750 }
751
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700752 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800753 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700754 VideoCallScreen videoCallScreen = getVideoCallScreen();
755 if (videoCallScreen.getCallId().equals(call.getId())) {
756 return false;
757 }
758 LogUtil.i(
759 "InCallActivity.showVideoCallScreenFragment",
760 "video call fragment exists but arguments do not match");
761 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -0800762 }
763
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700764 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
765
Eric Erfanian90508232017-03-24 09:31:16 -0700766 VideoCallScreen videoCallScreen =
767 VideoBindings.createVideoCallScreen(
768 call.getId(), call.getVideoTech().shouldUseSurfaceView());
Eric Erfanianccca3152017-02-22 16:32:36 -0800769 transaction.add(R.id.main, videoCallScreen.getVideoCallScreenFragment(), TAG_VIDEO_CALL_SCREEN);
770
771 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
772 didShowVideoCallScreen = true;
773 return true;
774 }
775
776 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
777 if (!didShowVideoCallScreen) {
778 return false;
779 }
780 VideoCallScreen videoCallScreen = getVideoCallScreen();
781 if (videoCallScreen != null) {
782 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
783 }
784 didShowVideoCallScreen = false;
785 return true;
786 }
787
788 AnswerScreen getAnswerScreen() {
789 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(TAG_ANSWER_SCREEN);
790 }
791
792 InCallScreen getInCallScreen() {
793 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_IN_CALL_SCREEN);
794 }
795
796 VideoCallScreen getVideoCallScreen() {
797 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_VIDEO_CALL_SCREEN);
798 }
799
800 @Override
801 public void onPseudoScreenStateChanged(boolean isOn) {
802 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
803 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
804 }
805
806 /**
807 * For some touch related issue, turning off the screen can be faked by drawing a black view over
808 * the activity. All touch events started when the screen is "off" is rejected.
809 *
810 * @see PseudoScreenState
811 */
812 @Override
813 public boolean dispatchTouchEvent(MotionEvent event) {
814 // Reject any gesture that started when the screen is in the fake off state.
815 if (touchDownWhenPseudoScreenOff) {
816 if (event.getAction() == MotionEvent.ACTION_UP) {
817 touchDownWhenPseudoScreenOff = false;
818 }
819 return true;
820 }
821 // Reject all touch event when the screen is in the fake off state.
822 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
823 if (event.getAction() == MotionEvent.ACTION_DOWN) {
824 touchDownWhenPseudoScreenOff = true;
825 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
826 }
827 return true;
828 }
829 return super.dispatchTouchEvent(event);
830 }
831
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700832 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -0800833 public final boolean shouldShow;
834 public final DialerCall call;
835
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700836 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800837 this.shouldShow = shouldShow;
838 this.call = call;
839 }
840 }
841}