blob: 34ffdf5b7502f6277773a87b7dd223db103d1493 [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;
45import com.android.dialer.logging.LoggingBindingsFactory;
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;
94 private int[] backgroundDrawableColors;
95 private GradientDrawable backgroundDrawable;
96 private boolean isVisible;
97 private View pseudoBlackScreenOverlay;
98 private boolean touchDownWhenPseudoScreenOff;
99 private boolean isInShowMainInCallFragment;
100 private boolean needDismissPendingDialogs;
wangqi9982f0d2017-10-11 17:46:07 -0700101 private boolean allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -0800102
103 public InCallActivity() {
104 common = new InCallActivityCommon(this);
105 }
106
107 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700108 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800109 Intent intent = new Intent(Intent.ACTION_MAIN, null);
110 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
111 intent.setClass(context, InCallActivity.class);
112 InCallActivityCommon.setIntentExtras(intent, showDialpad, newOutgoingCall, isForFullScreen);
113 return intent;
114 }
115
116 @Override
117 protected void onResumeFragments() {
118 super.onResumeFragments();
119 if (needDismissPendingDialogs) {
120 dismissPendingDialogs();
121 }
122 }
123
124 @Override
125 protected void onCreate(Bundle icicle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700126 Trace.beginSection("InCallActivity.onCreate");
Eric Erfanianccca3152017-02-22 16:32:36 -0800127 LogUtil.i("InCallActivity.onCreate", "");
128 super.onCreate(icicle);
129
Eric Erfanian2ca43182017-08-31 06:57:16 -0700130 if (getIntent().getBooleanExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY, false)) {
131 Logger.get(this).logImpression(DialerImpression.Type.BUBBLE_PRIMARY_BUTTON_RETURN_TO_CALL);
132 getIntent().removeExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY);
133 }
134
Eric Erfanianccca3152017-02-22 16:32:36 -0800135 if (icicle != null) {
136 didShowAnswerScreen = icicle.getBoolean(DID_SHOW_ANSWER_SCREEN_KEY);
137 didShowInCallScreen = icicle.getBoolean(DID_SHOW_IN_CALL_SCREEN_KEY);
138 didShowVideoCallScreen = icicle.getBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY);
139 }
140
141 common.onCreate(icicle);
142
143 getWindow()
144 .getDecorView()
145 .setSystemUiVisibility(
146 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
147
148 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700149 sendBroadcast(CallPendingActivity.getFinishBroadcast());
150 Trace.endSection();
weijiaxu94df7202017-10-25 18:21:41 -0700151 if (getApplicationContext() instanceof LoggingBindingsFactory) {
152 LoggingBindings loggingBindings =
153 ((LoggingBindingsFactory) getApplicationContext()).newLoggingBindings();
154 loggingBindings.logStopLatencyTimer(
155 LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
156 loggingBindings.logStopLatencyTimer(
157 LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
158 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800159 }
160
161 @Override
162 protected void onSaveInstanceState(Bundle out) {
163 LogUtil.i("InCallActivity.onSaveInstanceState", "");
164 common.onSaveInstanceState(out);
165 out.putBoolean(DID_SHOW_ANSWER_SCREEN_KEY, didShowAnswerScreen);
166 out.putBoolean(DID_SHOW_IN_CALL_SCREEN_KEY, didShowInCallScreen);
167 out.putBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY, didShowVideoCallScreen);
168 super.onSaveInstanceState(out);
169 isVisible = false;
170 }
171
172 @Override
173 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700174 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800175 LogUtil.i("InCallActivity.onStart", "");
wangqi9982f0d2017-10-11 17:46:07 -0700176 Trace.beginSection("call super");
Eric Erfanianccca3152017-02-22 16:32:36 -0800177 super.onStart();
wangqi9982f0d2017-10-11 17:46:07 -0700178 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800179 isVisible = true;
180 showMainInCallFragment();
181 common.onStart();
182 if (ActivityCompat.isInMultiWindowMode(this)
183 && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
184 // Hide the dialpad because there may not be enough room
185 showDialpadFragment(false, false);
186 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700187 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800188 }
189
190 @Override
191 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700192 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800193 LogUtil.i("InCallActivity.onResume", "");
194 super.onResume();
195 common.onResume();
196 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
197 pseudoScreenState.addListener(this);
198 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700199 Trace.endSection();
weijiaxu650e7cc2017-10-31 12:38:54 -0700200 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
201 ThreadUtil.postDelayedOnUiThread(
202 () -> {
203 if (getApplicationContext() instanceof LoggingBindingsFactory) {
204 ((LoggingBindingsFactory) getApplicationContext())
205 .newLoggingBindings()
206 .logRecordMemory(LoggingBindings.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME);
207 }
208 },
209 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800210 }
211
212 /** onPause is guaranteed to be called when the InCallActivity goes in the background. */
213 @Override
214 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700215 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800216 LogUtil.i("InCallActivity.onPause", "");
217 super.onPause();
218 common.onPause();
219 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700220 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800221 }
222
223 @Override
224 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700225 Trace.beginSection("InCallActivity.onStop");
Eric Erfanianccca3152017-02-22 16:32:36 -0800226 LogUtil.i("InCallActivity.onStop", "");
wangqi4d705e52017-09-28 12:23:35 -0700227 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800228 super.onStop();
229 common.onStop();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700230 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800231 }
232
233 @Override
234 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700235 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800236 LogUtil.i("InCallActivity.onDestroy", "");
237 super.onDestroy();
238 common.onDestroy();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700239 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800240 }
241
242 @Override
243 public void finish() {
244 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700245 // When user select incall ui from recents after the call is disconnected, it tries to launch
246 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
247 // crash.
248 // By calling finishAndRemoveTask() instead of finish() the task associated with
249 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
250 // this case.
251 //
252 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
253 // clear the task since there could be parent activity in the same task that's still alive.
254 // But InCallActivity is special since it's singleInstance which means it's root activity and
255 // only instance of activity in the task. So it should be safe to also remove task when
256 // finishing.
257 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
258 // finishes, the task should also be removed since it doesn't make sense to go back to it in
259 // anyway anymore.
260 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800261 }
262 }
263
264 private boolean shouldCloseActivityOnFinish() {
265 if (!isVisible()) {
266 LogUtil.i(
267 "InCallActivity.shouldCloseActivityOnFinish",
268 "allowing activity to be closed because it's not visible");
269 return true;
270 }
271
twyen8efb4952017-10-06 16:35:54 -0700272 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800273 LogUtil.i(
274 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700275 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800276 return false;
277 }
278
279 LogUtil.i(
280 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700281 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800282 return true;
283 }
284
285 @Override
286 protected void onNewIntent(Intent intent) {
287 LogUtil.i("InCallActivity.onNewIntent", "");
Eric Erfanianccca3152017-02-22 16:32:36 -0800288
289 // If the screen is off, we need to make sure it gets turned on for incoming calls.
290 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
291 // when the activity is first created. Therefore, to ensure the screen is turned on
292 // for the call waiting case, we recreate() the current activity. There should be no jank from
293 // this since the screen is already off and will remain so until our new activity is up.
294 if (!isVisible()) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700295 common.onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800296 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
297 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700298 } else {
299 common.onNewIntent(intent, false /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800300 }
301 }
302
303 @Override
304 public void onBackPressed() {
305 LogUtil.i("InCallActivity.onBackPressed", "");
306 if (!common.onBackPressed(didShowInCallScreen || didShowVideoCallScreen)) {
307 super.onBackPressed();
308 }
309 }
310
311 @Override
312 public boolean onOptionsItemSelected(MenuItem item) {
313 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
314 if (item.getItemId() == android.R.id.home) {
315 onBackPressed();
316 return true;
317 }
318 return super.onOptionsItemSelected(item);
319 }
320
321 @Override
322 public boolean onKeyUp(int keyCode, KeyEvent event) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700323 return common.onKeyUp(keyCode, event) || super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800324 }
325
326 @Override
327 public boolean onKeyDown(int keyCode, KeyEvent event) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700328 return common.onKeyDown(keyCode, event) || super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800329 }
330
331 public boolean isInCallScreenAnimating() {
332 return false;
333 }
334
335 public void showConferenceFragment(boolean show) {
336 if (show) {
337 startActivity(new Intent(this, ManageConferenceActivity.class));
338 }
339 }
340
341 public boolean showDialpadFragment(boolean show, boolean animate) {
342 boolean didChange = common.showDialpadFragment(show, animate);
343 if (didChange) {
344 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
345 // repositions itself.
346 getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
347 }
348 return didChange;
349 }
350
351 public boolean isDialpadVisible() {
352 return common.isDialpadVisible();
353 }
354
355 public void onForegroundCallChanged(DialerCall newForegroundCall) {
356 common.updateTaskDescription();
357 if (didShowAnswerScreen && newForegroundCall != null) {
358 if (newForegroundCall.getState() == State.DISCONNECTED
359 || newForegroundCall.getState() == State.IDLE) {
360 LogUtil.i(
361 "InCallActivity.onForegroundCallChanged",
362 "rejecting incoming call, not updating " + "window background color");
363 }
364 } else {
365 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
366 updateWindowBackgroundColor(0);
367 }
368 }
369
370 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
371 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
372 @ColorInt int top;
373 @ColorInt int middle;
374 @ColorInt int bottom;
375 @ColorInt int gray = 0x66000000;
376
377 if (ActivityCompat.isInMultiWindowMode(this)) {
378 top = themeColorManager.getBackgroundColorSolid();
379 middle = themeColorManager.getBackgroundColorSolid();
380 bottom = themeColorManager.getBackgroundColorSolid();
381 } else {
382 top = themeColorManager.getBackgroundColorTop();
383 middle = themeColorManager.getBackgroundColorMiddle();
384 bottom = themeColorManager.getBackgroundColorBottom();
385 }
386
387 if (progress < 0) {
388 float correctedProgress = Math.abs(progress);
389 top = ColorUtils.blendARGB(top, gray, correctedProgress);
390 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
391 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
392 }
393
394 boolean backgroundDirty = false;
395 if (backgroundDrawable == null) {
396 backgroundDrawableColors = new int[] {top, middle, bottom};
397 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
398 backgroundDirty = true;
399 } else {
400 if (backgroundDrawableColors[0] != top) {
401 backgroundDrawableColors[0] = top;
402 backgroundDirty = true;
403 }
404 if (backgroundDrawableColors[1] != middle) {
405 backgroundDrawableColors[1] = middle;
406 backgroundDirty = true;
407 }
408 if (backgroundDrawableColors[2] != bottom) {
409 backgroundDrawableColors[2] = bottom;
410 backgroundDirty = true;
411 }
412 if (backgroundDirty) {
413 backgroundDrawable.setColors(backgroundDrawableColors);
414 }
415 }
416
417 if (backgroundDirty) {
418 getWindow().setBackgroundDrawable(backgroundDrawable);
419 }
420 }
421
422 public boolean isVisible() {
423 return isVisible;
424 }
425
426 public boolean getCallCardFragmentVisible() {
427 return didShowInCallScreen || didShowVideoCallScreen;
428 }
429
430 public void dismissKeyguard(boolean dismiss) {
431 common.dismissKeyguard(dismiss);
432 }
433
434 public void showPostCharWaitDialog(String callId, String chars) {
435 common.showPostCharWaitDialog(callId, chars);
436 }
437
Eric Erfanian2ca43182017-08-31 06:57:16 -0700438 public void maybeShowErrorDialogOnDisconnect(DisconnectMessage disconnectMessage) {
439 common.maybeShowErrorDialogOnDisconnect(disconnectMessage);
Eric Erfanianccca3152017-02-22 16:32:36 -0800440 }
441
442 public void dismissPendingDialogs() {
443 if (isVisible) {
444 LogUtil.i("InCallActivity.dismissPendingDialogs", "");
445 common.dismissPendingDialogs();
446 AnswerScreen answerScreen = getAnswerScreen();
447 if (answerScreen != null) {
448 answerScreen.dismissPendingDialogs();
449 }
450 needDismissPendingDialogs = false;
451 } else {
452 // The activity is not visible and onSaveInstanceState may have been called so defer the
453 // dismissing action.
454 LogUtil.i(
455 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
456 needDismissPendingDialogs = true;
457 }
458 }
459
460 private void enableInCallOrientationEventListener(boolean enable) {
461 common.enableInCallOrientationEventListener(enable);
462 }
463
464 public void setExcludeFromRecents(boolean exclude) {
465 common.setExcludeFromRecents(exclude);
466 }
467
Eric Erfanianccca3152017-02-22 16:32:36 -0800468 @Nullable
469 public FragmentManager getDialpadFragmentManager() {
470 InCallScreen inCallScreen = getInCallScreen();
471 if (inCallScreen != null) {
472 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
473 }
474 return null;
475 }
476
477 public int getDialpadContainerId() {
478 return getInCallScreen().getAnswerAndDialpadContainerResourceId();
479 }
480
481 @Override
482 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
483 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
484 if (call == null) {
485 // This is a work around for a bug where we attempt to create a new delegate after the call
486 // has already been removed. An example of when this can happen is:
487 // 1. incoming video call in landscape mode
488 // 2. remote party hangs up
489 // 3. activity switches from landscape to portrait
490 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
491 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
492 // because this new state is transient and the activity will be destroyed soon.
493 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
494 return new AnswerScreenPresenterStub();
495 } else {
496 return new AnswerScreenPresenter(
497 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
498 }
499 }
500
501 @Override
502 public InCallScreenDelegate newInCallScreenDelegate() {
503 return new CallCardPresenter(this);
504 }
505
506 @Override
507 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
508 return new CallButtonPresenter(this);
509 }
510
511 @Override
Eric Erfanian90508232017-03-24 09:31:16 -0700512 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
513 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
514 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
515 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
516 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800517 return new VideoCallPresenter();
518 }
519
520 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700521 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -0800522 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "");
523 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700524 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800525 }
526
527 public void onWiFiToLteHandover(DialerCall call) {
528 common.showWifiToLteHandoverToast(call);
529 }
530
531 public void onHandoverToWifiFailed(DialerCall call) {
532 common.showWifiFailedDialog(call);
533 }
534
Eric Erfanianc857f902017-05-15 14:05:33 -0700535 public void onInternationalCallOnWifi(@NonNull DialerCall call) {
536 LogUtil.enterBlock("InCallActivity.onInternationalCallOnWifi");
537 common.showInternationalCallOnWifiDialog(call);
538 }
539
Eric Erfanian938468d2017-10-24 14:05:52 -0700540 @Override
541 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
542 super.onMultiWindowModeChanged(isInMultiWindowMode);
543 if (!isInMultiWindowMode) {
544 common.updateNavigationBar(isDialpadVisible());
545 }
546 }
547
Eric Erfanianccca3152017-02-22 16:32:36 -0800548 public void setAllowOrientationChange(boolean allowOrientationChange) {
wangqi9982f0d2017-10-11 17:46:07 -0700549 if (this.allowOrientationChange == allowOrientationChange) {
550 return;
551 }
552 this.allowOrientationChange = allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -0800553 if (!allowOrientationChange) {
554 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
555 } else {
556 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
557 }
558 enableInCallOrientationEventListener(allowOrientationChange);
559 }
560
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700561 public void hideMainInCallFragment() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800562 LogUtil.i("InCallActivity.hideMainInCallFragment", "");
563 if (didShowInCallScreen || didShowVideoCallScreen) {
564 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
565 hideInCallScreenFragment(transaction);
566 hideVideoCallScreenFragment(transaction);
567 transaction.commitAllowingStateLoss();
568 getSupportFragmentManager().executePendingTransactions();
569 }
570 }
571
572 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700573 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800574 // If the activity's onStart method hasn't been called yet then defer doing any work.
575 if (!isVisible) {
576 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700577 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800578 return;
579 }
580
581 // Don't let this be reentrant.
582 if (isInShowMainInCallFragment) {
583 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700584 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800585 return;
586 }
587
588 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700589 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
590 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
Eric Erfanianccca3152017-02-22 16:32:36 -0800591 LogUtil.i(
592 "InCallActivity.showMainInCallFragment",
593 "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, "
594 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b",
595 shouldShowAnswerUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -0700596 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -0800597 didShowAnswerScreen,
598 didShowInCallScreen,
599 didShowVideoCallScreen);
600 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700601 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -0800602
603 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
604 boolean didChangeInCall;
605 boolean didChangeVideo;
606 boolean didChangeAnswer;
607 if (shouldShowAnswerUi.shouldShow) {
608 didChangeInCall = hideInCallScreenFragment(transaction);
609 didChangeVideo = hideVideoCallScreenFragment(transaction);
610 didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700611 } else if (shouldShowVideoUi.shouldShow) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800612 didChangeInCall = hideInCallScreenFragment(transaction);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700613 didChangeVideo = showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800614 didChangeAnswer = hideAnswerScreenFragment(transaction);
615 } else {
616 didChangeInCall = showInCallScreenFragment(transaction);
617 didChangeVideo = hideVideoCallScreenFragment(transaction);
618 didChangeAnswer = hideAnswerScreenFragment(transaction);
619 }
620
621 if (didChangeInCall || didChangeVideo || didChangeAnswer) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700622 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -0800623 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700624 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800625 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
626 }
627 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700628 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800629 }
630
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700631 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800632 DialerCall call = CallList.getInstance().getIncomingCall();
633 if (call != null) {
634 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700635 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800636 }
637
638 call = CallList.getInstance().getVideoUpgradeRequestCall();
639 if (call != null) {
640 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700641 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800642 }
643
644 // Check if we're showing the answer screen and the call is disconnected. If this condition is
645 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
646 // the user rejects an incoming call.
647 call = CallList.getInstance().getFirstCall();
648 if (call == null) {
649 call = CallList.getInstance().getBackgroundCall();
650 }
651 if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
652 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700653 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800654 }
655
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700656 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800657 }
658
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700659 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800660 DialerCall call = CallList.getInstance().getFirstCall();
661 if (call == null) {
662 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700663 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800664 }
665
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700666 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800667 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700668 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800669 }
670
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700671 if (call.hasSentVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800672 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700673 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800674 }
675
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700676 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800677 }
678
679 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
680 // When rejecting a call the active call can become null in which case we should continue
681 // showing the answer screen.
682 if (didShowAnswerScreen && call == null) {
683 return false;
684 }
685
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700686 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
687
688 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -0800689
690 // Check if we're already showing an answer screen for this call.
691 if (didShowAnswerScreen) {
692 AnswerScreen answerScreen = getAnswerScreen();
693 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700694 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -0700695 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
696 && !answerScreen.isActionTimeout()) {
697 LogUtil.d(
698 "InCallActivity.showAnswerScreenFragment",
699 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -0800700 return false;
701 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700702 if (answerScreen.isActionTimeout()) {
703 LogUtil.i(
704 "InCallActivity.showAnswerScreenFragment",
705 "answer fragment exists but has been accepted/rejected and timed out");
706 } else {
707 LogUtil.i(
708 "InCallActivity.showAnswerScreenFragment",
709 "answer fragment exists but arguments do not match");
710 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800711 hideAnswerScreenFragment(transaction);
712 }
713
714 // Show a new answer screen.
715 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -0700716 AnswerBindings.createAnswerScreen(
717 call.getId(),
718 call.isVideoCall(),
719 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -0700720 call.getVideoTech().isSelfManagedCamera(),
721 shouldAllowAnswerAndRelease(call),
722 CallList.getInstance().getBackgroundCall() != null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800723 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), TAG_ANSWER_SCREEN);
724
725 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
726 didShowAnswerScreen = true;
727 return true;
728 }
729
Eric Erfanian90508232017-03-24 09:31:16 -0700730 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
731 if (CallList.getInstance().getActiveCall() == null) {
732 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
733 return false;
734 }
735 if (getSystemService(TelephonyManager.class).getPhoneType()
736 == TelephonyManager.PHONE_TYPE_CDMA) {
737 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
738 return false;
739 }
740 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
741 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
742 return false;
743 }
744 if (!ConfigProviderBindings.get(this).getBoolean(CONFIG_ANSWER_AND_RELEASE_ENABLED, true)) {
745 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
746 return false;
747 }
748
749 return true;
750 }
751
Eric Erfanianccca3152017-02-22 16:32:36 -0800752 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
753 if (!didShowAnswerScreen) {
754 return false;
755 }
756 AnswerScreen answerScreen = getAnswerScreen();
757 if (answerScreen != null) {
758 transaction.remove(answerScreen.getAnswerScreenFragment());
759 }
760
761 didShowAnswerScreen = false;
762 return true;
763 }
764
765 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
766 if (didShowInCallScreen) {
767 return false;
768 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700769 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
770 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), TAG_IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -0800771 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
772 didShowInCallScreen = true;
773 return true;
774 }
775
776 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
777 if (!didShowInCallScreen) {
778 return false;
779 }
780 InCallScreen inCallScreen = getInCallScreen();
781 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700782 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -0800783 }
784 didShowInCallScreen = false;
785 return true;
786 }
787
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700788 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800789 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700790 VideoCallScreen videoCallScreen = getVideoCallScreen();
791 if (videoCallScreen.getCallId().equals(call.getId())) {
792 return false;
793 }
794 LogUtil.i(
795 "InCallActivity.showVideoCallScreenFragment",
796 "video call fragment exists but arguments do not match");
797 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -0800798 }
799
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700800 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
801
Eric Erfanian90508232017-03-24 09:31:16 -0700802 VideoCallScreen videoCallScreen =
803 VideoBindings.createVideoCallScreen(
804 call.getId(), call.getVideoTech().shouldUseSurfaceView());
Eric Erfanianccca3152017-02-22 16:32:36 -0800805 transaction.add(R.id.main, videoCallScreen.getVideoCallScreenFragment(), TAG_VIDEO_CALL_SCREEN);
806
807 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
808 didShowVideoCallScreen = true;
809 return true;
810 }
811
812 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
813 if (!didShowVideoCallScreen) {
814 return false;
815 }
816 VideoCallScreen videoCallScreen = getVideoCallScreen();
817 if (videoCallScreen != null) {
818 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
819 }
820 didShowVideoCallScreen = false;
821 return true;
822 }
823
824 AnswerScreen getAnswerScreen() {
825 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(TAG_ANSWER_SCREEN);
826 }
827
828 InCallScreen getInCallScreen() {
829 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_IN_CALL_SCREEN);
830 }
831
832 VideoCallScreen getVideoCallScreen() {
833 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_VIDEO_CALL_SCREEN);
834 }
835
836 @Override
837 public void onPseudoScreenStateChanged(boolean isOn) {
838 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
839 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
840 }
841
842 /**
843 * For some touch related issue, turning off the screen can be faked by drawing a black view over
844 * the activity. All touch events started when the screen is "off" is rejected.
845 *
846 * @see PseudoScreenState
847 */
848 @Override
849 public boolean dispatchTouchEvent(MotionEvent event) {
850 // Reject any gesture that started when the screen is in the fake off state.
851 if (touchDownWhenPseudoScreenOff) {
852 if (event.getAction() == MotionEvent.ACTION_UP) {
853 touchDownWhenPseudoScreenOff = false;
854 }
855 return true;
856 }
857 // Reject all touch event when the screen is in the fake off state.
858 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
859 if (event.getAction() == MotionEvent.ACTION_DOWN) {
860 touchDownWhenPseudoScreenOff = true;
861 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
862 }
863 return true;
864 }
865 return super.dispatchTouchEvent(event);
866 }
867
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700868 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -0800869 public final boolean shouldShow;
870 public final DialerCall call;
871
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700872 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800873 this.shouldShow = shouldShow;
874 this.call = call;
875 }
876 }
877}