blob: 1e5a5fc02e132663da7a660c11f1c3b245ccdbf6 [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
linyuh57b093b2017-11-17 14:32:32 -080019import android.app.ActivityManager.TaskDescription;
linyuh7b86f562017-11-16 11:24:09 -080020import android.app.AlertDialog;
linyuhf99f6302017-11-15 11:23:51 -080021import android.app.Dialog;
linyuh57b093b2017-11-17 14:32:32 -080022import android.app.KeyguardManager;
Eric Erfanianccca3152017-02-22 16:32:36 -080023import android.content.Context;
24import android.content.Intent;
25import android.graphics.drawable.GradientDrawable;
26import android.graphics.drawable.GradientDrawable.Orientation;
27import android.os.Bundle;
Eric Erfanian2ca43182017-08-31 06:57:16 -070028import android.os.Trace;
Eric Erfanianccca3152017-02-22 16:32:36 -080029import android.support.annotation.ColorInt;
30import android.support.annotation.FloatRange;
Eric Erfanianc857f902017-05-15 14:05:33 -070031import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080032import android.support.annotation.Nullable;
33import android.support.v4.app.FragmentManager;
34import android.support.v4.app.FragmentTransaction;
linyuh57b093b2017-11-17 14:32:32 -080035import android.support.v4.content.res.ResourcesCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import android.support.v4.graphics.ColorUtils;
Eric Erfanian90508232017-03-24 09:31:16 -070037import android.telephony.TelephonyManager;
Eric Erfanianccca3152017-02-22 16:32:36 -080038import android.view.KeyEvent;
39import android.view.MenuItem;
40import android.view.MotionEvent;
41import android.view.View;
linyuh9c327da2017-11-14 12:33:48 -080042import android.view.WindowManager;
linyuh7b86f562017-11-16 11:24:09 -080043import android.widget.CheckBox;
44import android.widget.Toast;
linyuhf99f6302017-11-15 11:23:51 -080045import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070046import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080047import com.android.dialer.common.LogUtil;
weijiaxu650e7cc2017-10-31 12:38:54 -070048import com.android.dialer.common.concurrent.ThreadUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080049import com.android.dialer.compat.ActivityCompat;
Eric Erfanian2ca43182017-08-31 06:57:16 -070050import com.android.dialer.configprovider.ConfigProviderBindings;
51import com.android.dialer.logging.DialerImpression;
Eric Erfanianccca3152017-02-22 16:32:36 -080052import com.android.dialer.logging.Logger;
weijiaxu94df7202017-10-25 18:21:41 -070053import com.android.dialer.logging.LoggingBindings;
Eric Erfanian8369df02017-05-03 10:27:13 -070054import com.android.dialer.logging.ScreenEvent;
Eric Erfanianccca3152017-02-22 16:32:36 -080055import com.android.incallui.answer.bindings.AnswerBindings;
56import com.android.incallui.answer.protocol.AnswerScreen;
57import com.android.incallui.answer.protocol.AnswerScreenDelegate;
58import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
59import com.android.incallui.answerproximitysensor.PseudoScreenState;
linyuh57b093b2017-11-17 14:32:32 -080060import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080061import com.android.incallui.call.CallList;
62import com.android.incallui.call.DialerCall;
63import com.android.incallui.call.DialerCall.State;
linyuh57b093b2017-11-17 14:32:32 -080064import com.android.incallui.call.TelecomAdapter;
Eric Erfanian2ca43182017-08-31 06:57:16 -070065import com.android.incallui.callpending.CallPendingActivity;
66import com.android.incallui.disconnectdialog.DisconnectMessage;
Eric Erfanianccca3152017-02-22 16:32:36 -080067import com.android.incallui.incall.bindings.InCallBindings;
68import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
69import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
70import com.android.incallui.incall.protocol.InCallScreen;
71import com.android.incallui.incall.protocol.InCallScreenDelegate;
72import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
linyuh7b86f562017-11-16 11:24:09 -080073import com.android.incallui.incalluilock.InCallUiLock;
linyuhf99f6302017-11-15 11:23:51 -080074import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -080075import com.android.incallui.video.bindings.VideoBindings;
76import com.android.incallui.video.protocol.VideoCallScreen;
77import com.android.incallui.video.protocol.VideoCallScreenDelegate;
78import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
79
80/** Version of {@link InCallActivity} that shows the new UI */
81public class InCallActivity extends TransactionSafeFragmentActivity
82 implements AnswerScreenDelegateFactory,
83 InCallScreenDelegateFactory,
84 InCallButtonUiDelegateFactory,
85 VideoCallScreenDelegateFactory,
86 PseudoScreenState.StateChangedListener {
87
Eric Erfanian2ca43182017-08-31 06:57:16 -070088 public static final int PENDING_INTENT_REQUEST_CODE_NON_FULL_SCREEN = 0;
89 public static final int PENDING_INTENT_REQUEST_CODE_FULL_SCREEN = 1;
90 public static final int PENDING_INTENT_REQUEST_CODE_BUBBLE = 2;
91
linyuh57b093b2017-11-17 14:32:32 -080092 private static final String DIALPAD_TEXT_KEY = "InCallActivity.dialpad_text";
93
94 private static final String INTENT_EXTRA_SHOW_DIALPAD = "InCallActivity.show_dialpad";
95
Eric Erfanianccca3152017-02-22 16:32:36 -080096 private static final String TAG_ANSWER_SCREEN = "tag_answer_screen";
linyuh69a25062017-11-15 16:18:51 -080097 private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment";
linyuhf99f6302017-11-15 11:23:51 -080098 private static final String TAG_INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi";
99 private static final String TAG_IN_CALL_SCREEN = "tag_in_call_screen";
Eric Erfanianccca3152017-02-22 16:32:36 -0800100 private static final String TAG_VIDEO_CALL_SCREEN = "tag_video_call_screen";
101
102 private static final String DID_SHOW_ANSWER_SCREEN_KEY = "did_show_answer_screen";
103 private static final String DID_SHOW_IN_CALL_SCREEN_KEY = "did_show_in_call_screen";
104 private static final String DID_SHOW_VIDEO_CALL_SCREEN_KEY = "did_show_video_call_screen";
105
Eric Erfanian90508232017-03-24 09:31:16 -0700106 private static final String CONFIG_ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
107
Eric Erfanianccca3152017-02-22 16:32:36 -0800108 private final InCallActivityCommon common;
linyuh69a25062017-11-15 16:18:51 -0800109 private InCallOrientationEventListener inCallOrientationEventListener;
Eric Erfanianccca3152017-02-22 16:32:36 -0800110 private boolean didShowAnswerScreen;
111 private boolean didShowInCallScreen;
112 private boolean didShowVideoCallScreen;
linyuh9c327da2017-11-14 12:33:48 -0800113 private boolean dismissKeyguard;
Eric Erfanianccca3152017-02-22 16:32:36 -0800114 private int[] backgroundDrawableColors;
115 private GradientDrawable backgroundDrawable;
116 private boolean isVisible;
117 private View pseudoBlackScreenOverlay;
118 private boolean touchDownWhenPseudoScreenOff;
119 private boolean isInShowMainInCallFragment;
120 private boolean needDismissPendingDialogs;
wangqi9982f0d2017-10-11 17:46:07 -0700121 private boolean allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -0800122
123 public InCallActivity() {
124 common = new InCallActivityCommon(this);
125 }
126
127 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700128 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800129 Intent intent = new Intent(Intent.ACTION_MAIN, null);
130 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
131 intent.setClass(context, InCallActivity.class);
132 InCallActivityCommon.setIntentExtras(intent, showDialpad, newOutgoingCall, isForFullScreen);
133 return intent;
134 }
135
136 @Override
137 protected void onResumeFragments() {
138 super.onResumeFragments();
139 if (needDismissPendingDialogs) {
140 dismissPendingDialogs();
141 }
142 }
143
144 @Override
145 protected void onCreate(Bundle icicle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700146 Trace.beginSection("InCallActivity.onCreate");
Eric Erfanianccca3152017-02-22 16:32:36 -0800147 LogUtil.i("InCallActivity.onCreate", "");
148 super.onCreate(icicle);
149
Eric Erfanian2ca43182017-08-31 06:57:16 -0700150 if (getIntent().getBooleanExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY, false)) {
151 Logger.get(this).logImpression(DialerImpression.Type.BUBBLE_PRIMARY_BUTTON_RETURN_TO_CALL);
152 getIntent().removeExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY);
153 }
154
Eric Erfanianccca3152017-02-22 16:32:36 -0800155 if (icicle != null) {
156 didShowAnswerScreen = icicle.getBoolean(DID_SHOW_ANSWER_SCREEN_KEY);
157 didShowInCallScreen = icicle.getBoolean(DID_SHOW_IN_CALL_SCREEN_KEY);
158 didShowVideoCallScreen = icicle.getBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY);
159 }
160
161 common.onCreate(icicle);
linyuh69a25062017-11-15 16:18:51 -0800162 inCallOrientationEventListener = new InCallOrientationEventListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800163
164 getWindow()
165 .getDecorView()
166 .setSystemUiVisibility(
167 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
168
169 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700170 sendBroadcast(CallPendingActivity.getFinishBroadcast());
171 Trace.endSection();
weijiaxuc950a9b2017-11-06 16:39:04 -0800172 Logger.get(this)
173 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
174 Logger.get(this)
175 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
Eric Erfanianccca3152017-02-22 16:32:36 -0800176 }
177
178 @Override
179 protected void onSaveInstanceState(Bundle out) {
linyuh57b093b2017-11-17 14:32:32 -0800180 LogUtil.enterBlock("InCallActivity.onSaveInstanceState");
181
182 // TODO: DialpadFragment should handle this as part of its own state
183 out.putBoolean(INTENT_EXTRA_SHOW_DIALPAD, isDialpadVisible());
184 DialpadFragment dialpadFragment = getDialpadFragment();
185 if (dialpadFragment != null) {
186 out.putString(DIALPAD_TEXT_KEY, dialpadFragment.getDtmfText());
187 }
188
Eric Erfanianccca3152017-02-22 16:32:36 -0800189 out.putBoolean(DID_SHOW_ANSWER_SCREEN_KEY, didShowAnswerScreen);
190 out.putBoolean(DID_SHOW_IN_CALL_SCREEN_KEY, didShowInCallScreen);
191 out.putBoolean(DID_SHOW_VIDEO_CALL_SCREEN_KEY, didShowVideoCallScreen);
linyuh57b093b2017-11-17 14:32:32 -0800192
Eric Erfanianccca3152017-02-22 16:32:36 -0800193 super.onSaveInstanceState(out);
194 isVisible = false;
195 }
196
197 @Override
198 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700199 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800200 super.onStart();
linyuh57b093b2017-11-17 14:32:32 -0800201
Eric Erfanianccca3152017-02-22 16:32:36 -0800202 isVisible = true;
203 showMainInCallFragment();
linyuh57b093b2017-11-17 14:32:32 -0800204
205 InCallPresenter.getInstance().setActivity(this);
206 enableInCallOrientationEventListener(
207 getRequestedOrientation()
208 == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
209 InCallPresenter.getInstance().onActivityStarted();
210
Eric Erfanianccca3152017-02-22 16:32:36 -0800211 if (ActivityCompat.isInMultiWindowMode(this)
212 && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
213 // Hide the dialpad because there may not be enough room
214 showDialpadFragment(false, false);
215 }
linyuh57b093b2017-11-17 14:32:32 -0800216
Eric Erfanian2ca43182017-08-31 06:57:16 -0700217 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800218 }
219
220 @Override
221 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700222 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800223 LogUtil.i("InCallActivity.onResume", "");
224 super.onResume();
225 common.onResume();
226 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
227 pseudoScreenState.addListener(this);
228 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700229 Trace.endSection();
weijiaxu650e7cc2017-10-31 12:38:54 -0700230 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
231 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800232 () ->
233 Logger.get(this)
234 .logRecordMemory(LoggingBindings.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700235 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800236 }
237
Eric Erfanianccca3152017-02-22 16:32:36 -0800238 @Override
239 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700240 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800241 super.onPause();
linyuh57b093b2017-11-17 14:32:32 -0800242
243 DialpadFragment dialpadFragment = getDialpadFragment();
244 if (dialpadFragment != null) {
245 dialpadFragment.onDialerKeyUp(null);
246 }
247
248 InCallPresenter.getInstance().onUiShowing(false);
249 if (isFinishing()) {
250 InCallPresenter.getInstance().unsetActivity(this);
251 }
252
Eric Erfanianccca3152017-02-22 16:32:36 -0800253 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700254 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800255 }
256
257 @Override
258 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700259 Trace.beginSection("InCallActivity.onStop");
wangqi4d705e52017-09-28 12:23:35 -0700260 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800261 super.onStop();
linyuh57b093b2017-11-17 14:32:32 -0800262
263 // Disconnects the call waiting for a phone account when the activity is hidden (e.g., after the
264 // user presses the home button).
265 // Without this the pending call will get stuck on phone account selection and new calls can't
266 // be created.
267 // Skip this when the screen is locked since the activity may complete its current life cycle
268 // and restart.
269 if (!common.getIsRecreating() && !getSystemService(KeyguardManager.class).isKeyguardLocked()) {
270 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
271 if (waitingForAccountCall != null) {
272 waitingForAccountCall.disconnect();
273 }
274 }
275
276 enableInCallOrientationEventListener(false);
277 InCallPresenter.getInstance().updateIsChangingConfigurations();
278 InCallPresenter.getInstance().onActivityStopped();
279 if (!common.getIsRecreating()) {
280 Dialog errorDialog = common.getErrorDialog();
281 if (errorDialog != null) {
282 errorDialog.dismiss();
283 }
284 }
285
Eric Erfanian2ca43182017-08-31 06:57:16 -0700286 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800287 }
288
289 @Override
290 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700291 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800292 super.onDestroy();
linyuh57b093b2017-11-17 14:32:32 -0800293
294 InCallPresenter.getInstance().unsetActivity(this);
295 InCallPresenter.getInstance().updateIsChangingConfigurations();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700296 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800297 }
298
299 @Override
300 public void finish() {
301 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700302 // When user select incall ui from recents after the call is disconnected, it tries to launch
303 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
304 // crash.
305 // By calling finishAndRemoveTask() instead of finish() the task associated with
306 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
307 // this case.
308 //
309 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
310 // clear the task since there could be parent activity in the same task that's still alive.
311 // But InCallActivity is special since it's singleInstance which means it's root activity and
312 // only instance of activity in the task. So it should be safe to also remove task when
313 // finishing.
314 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
315 // finishes, the task should also be removed since it doesn't make sense to go back to it in
316 // anyway anymore.
317 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800318 }
319 }
320
321 private boolean shouldCloseActivityOnFinish() {
322 if (!isVisible()) {
323 LogUtil.i(
324 "InCallActivity.shouldCloseActivityOnFinish",
325 "allowing activity to be closed because it's not visible");
326 return true;
327 }
328
twyen8efb4952017-10-06 16:35:54 -0700329 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800330 LogUtil.i(
331 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700332 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800333 return false;
334 }
335
336 LogUtil.i(
337 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700338 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800339 return true;
340 }
341
342 @Override
343 protected void onNewIntent(Intent intent) {
344 LogUtil.i("InCallActivity.onNewIntent", "");
Eric Erfanianccca3152017-02-22 16:32:36 -0800345
346 // If the screen is off, we need to make sure it gets turned on for incoming calls.
347 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
348 // when the activity is first created. Therefore, to ensure the screen is turned on
349 // for the call waiting case, we recreate() the current activity. There should be no jank from
350 // this since the screen is already off and will remain so until our new activity is up.
351 if (!isVisible()) {
Eric Erfanian10b34a52017-05-04 08:23:17 -0700352 common.onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800353 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
354 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700355 } else {
356 common.onNewIntent(intent, false /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800357 }
358 }
359
360 @Override
361 public void onBackPressed() {
linyuh57b093b2017-11-17 14:32:32 -0800362 LogUtil.enterBlock("InCallActivity.onBackPressed");
363
364 if (!isVisible()) {
365 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800366 }
linyuh57b093b2017-11-17 14:32:32 -0800367
368 if (!getCallCardFragmentVisible()) {
369 return;
370 }
371
372 DialpadFragment dialpadFragment = getDialpadFragment();
373 if (dialpadFragment != null && dialpadFragment.isVisible()) {
374 showDialpadFragment(false /* show */, true /* animate */);
375 return;
376 }
377
378 if (CallList.getInstance().getIncomingCall() != null) {
379 LogUtil.i(
380 "InCallActivity.onBackPressed",
381 "Ignore the press of the back key when an incoming call is ringing");
382 return;
383 }
384
385 // Nothing special to do. Fall back to the default behavior.
386 super.onBackPressed();
Eric Erfanianccca3152017-02-22 16:32:36 -0800387 }
388
389 @Override
390 public boolean onOptionsItemSelected(MenuItem item) {
391 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
392 if (item.getItemId() == android.R.id.home) {
393 onBackPressed();
394 return true;
395 }
396 return super.onOptionsItemSelected(item);
397 }
398
399 @Override
400 public boolean onKeyUp(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800401 DialpadFragment dialpadFragment = getDialpadFragment();
402 if (dialpadFragment != null
403 && dialpadFragment.isVisible()
404 && dialpadFragment.onDialerKeyUp(event)) {
405 return true;
406 }
407
408 if (keyCode == KeyEvent.KEYCODE_CALL) {
409 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
410 return true;
411 }
412
413 return super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800414 }
415
416 @Override
417 public boolean onKeyDown(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800418 switch (keyCode) {
419 case KeyEvent.KEYCODE_CALL:
420 if (!InCallPresenter.getInstance().handleCallKey()) {
421 LogUtil.e(
422 "InCallActivity.onKeyDown",
423 "InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
424 }
425 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
426 return true;
427
428 // Note that KEYCODE_ENDCALL isn't handled here as the standard system-wide handling of it
429 // is exactly what's needed, namely
430 // (1) "hang up" if there's an active call, or
431 // (2) "don't answer" if there's an incoming call.
432 // (See PhoneWindowManager for implementation details.)
433
434 case KeyEvent.KEYCODE_CAMERA:
435 // Consume KEYCODE_CAMERA since it's easy to accidentally press the camera button.
436 return true;
437
438 case KeyEvent.KEYCODE_VOLUME_UP:
439 case KeyEvent.KEYCODE_VOLUME_DOWN:
440 case KeyEvent.KEYCODE_VOLUME_MUTE:
441 // Ringer silencing handled by PhoneWindowManager.
442 break;
443
444 case KeyEvent.KEYCODE_MUTE:
445 TelecomAdapter.getInstance()
446 .mute(!AudioModeProvider.getInstance().getAudioState().isMuted());
447 return true;
448
449 case KeyEvent.KEYCODE_SLASH:
450 // When verbose logging is enabled, dump the view for debugging/testing purposes.
451 if (LogUtil.isVerboseEnabled()) {
452 View decorView = getWindow().getDecorView();
453 LogUtil.v("InCallActivity.onKeyDown", "View dump:\n%s", decorView);
454 return true;
455 }
456 break;
457
458 case KeyEvent.KEYCODE_EQUALS:
459 break;
460
461 default: // fall out
462 }
463
464 // Pass other key events to DialpadFragment's "onDialerKeyDown" method in case the user types
465 // in DTMF (Dual-tone multi-frequency signaling) code.
466 DialpadFragment dialpadFragment = getDialpadFragment();
467 if (dialpadFragment != null
468 && dialpadFragment.isVisible()
469 && dialpadFragment.onDialerKeyDown(event)) {
470 return true;
471 }
472
473 return super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800474 }
475
476 public boolean isInCallScreenAnimating() {
477 return false;
478 }
479
480 public void showConferenceFragment(boolean show) {
481 if (show) {
482 startActivity(new Intent(this, ManageConferenceActivity.class));
483 }
484 }
485
486 public boolean showDialpadFragment(boolean show, boolean animate) {
487 boolean didChange = common.showDialpadFragment(show, animate);
488 if (didChange) {
489 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
490 // repositions itself.
491 getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
492 }
493 return didChange;
494 }
495
496 public boolean isDialpadVisible() {
linyuh69a25062017-11-15 16:18:51 -0800497 DialpadFragment dialpadFragment = getDialpadFragment();
498 return dialpadFragment != null && dialpadFragment.isVisible();
499 }
500
501 /**
502 * Returns the {@link DialpadFragment} that's shown by this activity, or {@code null}
503 * TODO(a bug): Make this method private after InCallActivityCommon is deleted.
504 */
505 @Nullable
506 DialpadFragment getDialpadFragment() {
507 FragmentManager fragmentManager = getDialpadFragmentManager();
508 if (fragmentManager == null) {
509 return null;
510 }
511 return (DialpadFragment) fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800512 }
513
514 public void onForegroundCallChanged(DialerCall newForegroundCall) {
linyuh57b093b2017-11-17 14:32:32 -0800515 updateTaskDescription();
516
517 if (newForegroundCall == null || !didShowAnswerScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800518 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
linyuh57b093b2017-11-17 14:32:32 -0800519 updateWindowBackgroundColor(0 /* progress */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800520 }
521 }
522
linyuh57b093b2017-11-17 14:32:32 -0800523 // TODO(a bug): Make this method private after InCallActivityCommon is deleted.
524 void updateTaskDescription() {
525 int color =
526 getResources().getBoolean(R.bool.is_layout_landscape)
527 ? ResourcesCompat.getColor(
528 getResources(), R.color.statusbar_background_color, getTheme())
529 : InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
530 setTaskDescription(
531 new TaskDescription(
532 getResources().getString(R.string.notification_ongoing_call), null /* icon */, color));
533 }
534
Eric Erfanianccca3152017-02-22 16:32:36 -0800535 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
536 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
537 @ColorInt int top;
538 @ColorInt int middle;
539 @ColorInt int bottom;
540 @ColorInt int gray = 0x66000000;
541
542 if (ActivityCompat.isInMultiWindowMode(this)) {
543 top = themeColorManager.getBackgroundColorSolid();
544 middle = themeColorManager.getBackgroundColorSolid();
545 bottom = themeColorManager.getBackgroundColorSolid();
546 } else {
547 top = themeColorManager.getBackgroundColorTop();
548 middle = themeColorManager.getBackgroundColorMiddle();
549 bottom = themeColorManager.getBackgroundColorBottom();
550 }
551
552 if (progress < 0) {
553 float correctedProgress = Math.abs(progress);
554 top = ColorUtils.blendARGB(top, gray, correctedProgress);
555 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
556 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
557 }
558
559 boolean backgroundDirty = false;
560 if (backgroundDrawable == null) {
561 backgroundDrawableColors = new int[] {top, middle, bottom};
562 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
563 backgroundDirty = true;
564 } else {
565 if (backgroundDrawableColors[0] != top) {
566 backgroundDrawableColors[0] = top;
567 backgroundDirty = true;
568 }
569 if (backgroundDrawableColors[1] != middle) {
570 backgroundDrawableColors[1] = middle;
571 backgroundDirty = true;
572 }
573 if (backgroundDrawableColors[2] != bottom) {
574 backgroundDrawableColors[2] = bottom;
575 backgroundDirty = true;
576 }
577 if (backgroundDirty) {
578 backgroundDrawable.setColors(backgroundDrawableColors);
579 }
580 }
581
582 if (backgroundDirty) {
583 getWindow().setBackgroundDrawable(backgroundDrawable);
584 }
585 }
586
587 public boolean isVisible() {
588 return isVisible;
589 }
590
591 public boolean getCallCardFragmentVisible() {
592 return didShowInCallScreen || didShowVideoCallScreen;
593 }
594
595 public void dismissKeyguard(boolean dismiss) {
linyuh9c327da2017-11-14 12:33:48 -0800596 if (dismissKeyguard == dismiss) {
597 return;
598 }
599
600 dismissKeyguard = dismiss;
601 if (dismiss) {
602 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
603 } else {
604 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
605 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800606 }
607
608 public void showPostCharWaitDialog(String callId, String chars) {
609 common.showPostCharWaitDialog(callId, chars);
610 }
611
linyuh7b86f562017-11-16 11:24:09 -0800612 public void showDialogOrToastForDisconnectedCall(DisconnectMessage disconnectMessage) {
613 LogUtil.i(
614 "InCallActivity.showDialogOrToastForDisconnectedCall",
615 "disconnect cause: %s",
616 disconnectMessage);
617
618 if (disconnectMessage.dialog == null || isFinishing()) {
619 return;
620 }
621
622 dismissPendingDialogs();
623
624 // Show a toast if the app is in background when a dialog can't be visible.
625 if (!isVisible()) {
626 Toast.makeText(getApplicationContext(), disconnectMessage.toastMessage, Toast.LENGTH_LONG)
627 .show();
628 return;
629 }
630
631 // Show the dialog.
632 common.setErrorDialog(disconnectMessage.dialog);
633 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("showErrorDialog");
634 disconnectMessage.dialog.setOnDismissListener(
635 dialogInterface -> {
636 lock.release();
637 onDialogDismissed();
638 });
639 disconnectMessage.dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
640 disconnectMessage.dialog.show();
641 }
642
643 private void onDialogDismissed() {
644 common.setErrorDialog(null);
645 CallList.getInstance().onErrorDialogDismissed();
Eric Erfanianccca3152017-02-22 16:32:36 -0800646 }
647
648 public void dismissPendingDialogs() {
linyuhf99f6302017-11-15 11:23:51 -0800649 LogUtil.i("InCallActivity.dismissPendingDialogs", "");
650
651 if (!isVisible) {
652 // Defer the dismissing action as the activity is not visible and onSaveInstanceState may have
653 // been called.
Eric Erfanianccca3152017-02-22 16:32:36 -0800654 LogUtil.i(
655 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
656 needDismissPendingDialogs = true;
linyuhf99f6302017-11-15 11:23:51 -0800657 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800658 }
linyuhf99f6302017-11-15 11:23:51 -0800659
660 // Dismiss the error dialog
661 Dialog errorDialog = common.getErrorDialog();
662 if (errorDialog != null) {
663 errorDialog.dismiss();
664 common.setErrorDialog(null);
665 }
666
667 // Dismiss the phone account selection dialog
668 SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment =
669 common.getSelectPhoneAccountDialogFragment();
670 if (selectPhoneAccountDialogFragment != null) {
671 selectPhoneAccountDialogFragment.dismiss();
672 common.setSelectPhoneAccountDialogFragment(null);
673 }
674
675 // Dismiss the dialog for international call on WiFi
676 InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
677 (InternationalCallOnWifiDialogFragment)
678 getSupportFragmentManager().findFragmentByTag(TAG_INTERNATIONAL_CALL_ON_WIFI);
679 if (internationalCallOnWifiFragment != null) {
680 internationalCallOnWifiFragment.dismiss();
681 }
682
683 // Dismiss the answer screen
684 AnswerScreen answerScreen = getAnswerScreen();
685 if (answerScreen != null) {
686 answerScreen.dismissPendingDialogs();
687 }
688
689 needDismissPendingDialogs = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800690 }
691
linyuh69a25062017-11-15 16:18:51 -0800692 // TODO(a bug): Make this method private after InCallActivityCommon is deleted.
693 void enableInCallOrientationEventListener(boolean enable) {
694 if (enable) {
695 inCallOrientationEventListener.enable(true /* notifyDeviceOrientationChange */);
696 } else {
697 inCallOrientationEventListener.disable();
698 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800699 }
700
701 public void setExcludeFromRecents(boolean exclude) {
702 common.setExcludeFromRecents(exclude);
703 }
704
Eric Erfanianccca3152017-02-22 16:32:36 -0800705 @Nullable
706 public FragmentManager getDialpadFragmentManager() {
707 InCallScreen inCallScreen = getInCallScreen();
708 if (inCallScreen != null) {
709 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
710 }
711 return null;
712 }
713
714 public int getDialpadContainerId() {
715 return getInCallScreen().getAnswerAndDialpadContainerResourceId();
716 }
717
718 @Override
719 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
720 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
721 if (call == null) {
722 // This is a work around for a bug where we attempt to create a new delegate after the call
723 // has already been removed. An example of when this can happen is:
724 // 1. incoming video call in landscape mode
725 // 2. remote party hangs up
726 // 3. activity switches from landscape to portrait
727 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
728 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
729 // because this new state is transient and the activity will be destroyed soon.
730 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
731 return new AnswerScreenPresenterStub();
732 } else {
733 return new AnswerScreenPresenter(
734 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
735 }
736 }
737
738 @Override
739 public InCallScreenDelegate newInCallScreenDelegate() {
740 return new CallCardPresenter(this);
741 }
742
743 @Override
744 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
745 return new CallButtonPresenter(this);
746 }
747
748 @Override
Eric Erfanian90508232017-03-24 09:31:16 -0700749 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
750 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
751 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
752 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
753 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800754 return new VideoCallPresenter();
755 }
756
757 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700758 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -0800759 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "");
760 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700761 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800762 }
763
linyuh7b86f562017-11-16 11:24:09 -0800764 public void showToastForWiFiToLteHandover(DialerCall call) {
765 if (call.hasShownWiFiToLteHandoverToast()) {
766 return;
767 }
768
769 Toast.makeText(this, R.string.video_call_wifi_to_lte_handover_toast, Toast.LENGTH_LONG).show();
770 call.setHasShownWiFiToLteHandoverToast();
Eric Erfanianccca3152017-02-22 16:32:36 -0800771 }
772
linyuh7b86f562017-11-16 11:24:09 -0800773 public void showDialogOrToastForWifiHandoverFailure(DialerCall call) {
774 if (call.showWifiHandoverAlertAsToast()) {
775 Toast.makeText(this, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
776 .show();
777 return;
778 }
779
780 dismissPendingDialogs();
781
782 AlertDialog.Builder builder =
783 new AlertDialog.Builder(this).setTitle(R.string.video_call_lte_to_wifi_failed_title);
784
785 // This allows us to use the theme of the dialog instead of the activity
786 View dialogCheckBoxView =
787 View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null /* root */);
788 CheckBox wifiHandoverFailureCheckbox =
789 (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
790 wifiHandoverFailureCheckbox.setChecked(false);
791
792 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("WifiFailedDialog");
793 Dialog errorDialog =
794 builder
795 .setView(dialogCheckBoxView)
796 .setMessage(R.string.video_call_lte_to_wifi_failed_message)
797 .setOnCancelListener(dialogInterface -> onDialogDismissed())
798 .setPositiveButton(
799 android.R.string.ok,
800 (dialogInterface, id) -> {
801 call.setDoNotShowDialogForHandoffToWifiFailure(
802 wifiHandoverFailureCheckbox.isChecked());
803 dialogInterface.cancel();
804 onDialogDismissed();
805 })
806 .setOnDismissListener(dialogInterface -> lock.release())
807 .create();
808
809 common.setErrorDialog(errorDialog);
810 errorDialog.show();
Eric Erfanianccca3152017-02-22 16:32:36 -0800811 }
812
linyuh7b86f562017-11-16 11:24:09 -0800813 public void showDialogForInternationalCallOnWifi(@NonNull DialerCall call) {
814 if (!InternationalCallOnWifiDialogFragment.shouldShow(this)) {
815 LogUtil.i(
816 "InCallActivity.showDialogForInternationalCallOnWifi",
817 "InternationalCallOnWifiDialogFragment.shouldShow returned false");
818 return;
819 }
820
821 InternationalCallOnWifiDialogFragment fragment =
822 InternationalCallOnWifiDialogFragment.newInstance(
823 call.getId(), common.getCallbackForInternationalCallOnWifiDialog());
824 fragment.show(getSupportFragmentManager(), TAG_INTERNATIONAL_CALL_ON_WIFI);
Eric Erfanianc857f902017-05-15 14:05:33 -0700825 }
826
Eric Erfanian938468d2017-10-24 14:05:52 -0700827 @Override
828 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
829 super.onMultiWindowModeChanged(isInMultiWindowMode);
linyuh57b093b2017-11-17 14:32:32 -0800830 updateNavigationBar(isDialpadVisible());
831 }
832
833 // TODO(a bug): Make this method private after InCallActivityCommon is deleted.
834 void updateNavigationBar(boolean isDialpadVisible) {
835 if (ActivityCompat.isInMultiWindowMode(this)) {
836 return;
837 }
838
839 View navigationBarBackground = getWindow().findViewById(R.id.navigation_bar_background);
840 if (navigationBarBackground != null) {
841 navigationBarBackground.setVisibility(isDialpadVisible ? View.VISIBLE : View.GONE);
Eric Erfanian938468d2017-10-24 14:05:52 -0700842 }
843 }
844
Eric Erfanianccca3152017-02-22 16:32:36 -0800845 public void setAllowOrientationChange(boolean allowOrientationChange) {
wangqi9982f0d2017-10-11 17:46:07 -0700846 if (this.allowOrientationChange == allowOrientationChange) {
847 return;
848 }
849 this.allowOrientationChange = allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -0800850 if (!allowOrientationChange) {
851 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
852 } else {
853 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
854 }
855 enableInCallOrientationEventListener(allowOrientationChange);
856 }
857
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700858 public void hideMainInCallFragment() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800859 LogUtil.i("InCallActivity.hideMainInCallFragment", "");
860 if (didShowInCallScreen || didShowVideoCallScreen) {
861 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
862 hideInCallScreenFragment(transaction);
863 hideVideoCallScreenFragment(transaction);
864 transaction.commitAllowingStateLoss();
865 getSupportFragmentManager().executePendingTransactions();
866 }
867 }
868
869 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700870 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -0800871 // If the activity's onStart method hasn't been called yet then defer doing any work.
872 if (!isVisible) {
873 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700874 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800875 return;
876 }
877
878 // Don't let this be reentrant.
879 if (isInShowMainInCallFragment) {
880 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -0700881 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800882 return;
883 }
884
885 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700886 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
887 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
Eric Erfanianccca3152017-02-22 16:32:36 -0800888 LogUtil.i(
889 "InCallActivity.showMainInCallFragment",
890 "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, "
891 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b",
892 shouldShowAnswerUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -0700893 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -0800894 didShowAnswerScreen,
895 didShowInCallScreen,
896 didShowVideoCallScreen);
897 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700898 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -0800899
900 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
901 boolean didChangeInCall;
902 boolean didChangeVideo;
903 boolean didChangeAnswer;
904 if (shouldShowAnswerUi.shouldShow) {
905 didChangeInCall = hideInCallScreenFragment(transaction);
906 didChangeVideo = hideVideoCallScreenFragment(transaction);
907 didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700908 } else if (shouldShowVideoUi.shouldShow) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800909 didChangeInCall = hideInCallScreenFragment(transaction);
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700910 didChangeVideo = showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800911 didChangeAnswer = hideAnswerScreenFragment(transaction);
912 } else {
913 didChangeInCall = showInCallScreenFragment(transaction);
914 didChangeVideo = hideVideoCallScreenFragment(transaction);
915 didChangeAnswer = hideAnswerScreenFragment(transaction);
916 }
917
918 if (didChangeInCall || didChangeVideo || didChangeAnswer) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700919 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -0800920 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700921 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800922 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
923 }
924 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -0700925 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800926 }
927
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700928 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800929 DialerCall call = CallList.getInstance().getIncomingCall();
930 if (call != null) {
931 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700932 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800933 }
934
935 call = CallList.getInstance().getVideoUpgradeRequestCall();
936 if (call != null) {
937 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700938 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800939 }
940
941 // Check if we're showing the answer screen and the call is disconnected. If this condition is
942 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
943 // the user rejects an incoming call.
944 call = CallList.getInstance().getFirstCall();
945 if (call == null) {
946 call = CallList.getInstance().getBackgroundCall();
947 }
948 if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
949 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700950 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800951 }
952
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700953 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800954 }
955
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700956 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -0800957 DialerCall call = CallList.getInstance().getFirstCall();
958 if (call == null) {
959 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700960 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800961 }
962
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700963 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800964 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700965 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800966 }
967
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700968 if (call.hasSentVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800969 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700970 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -0800971 }
972
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700973 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -0800974 }
975
976 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
977 // When rejecting a call the active call can become null in which case we should continue
978 // showing the answer screen.
979 if (didShowAnswerScreen && call == null) {
980 return false;
981 }
982
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700983 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
984
985 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -0800986
987 // Check if we're already showing an answer screen for this call.
988 if (didShowAnswerScreen) {
989 AnswerScreen answerScreen = getAnswerScreen();
990 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700991 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -0700992 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
993 && !answerScreen.isActionTimeout()) {
994 LogUtil.d(
995 "InCallActivity.showAnswerScreenFragment",
996 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -0800997 return false;
998 }
Eric Erfanian2ca43182017-08-31 06:57:16 -0700999 if (answerScreen.isActionTimeout()) {
1000 LogUtil.i(
1001 "InCallActivity.showAnswerScreenFragment",
1002 "answer fragment exists but has been accepted/rejected and timed out");
1003 } else {
1004 LogUtil.i(
1005 "InCallActivity.showAnswerScreenFragment",
1006 "answer fragment exists but arguments do not match");
1007 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001008 hideAnswerScreenFragment(transaction);
1009 }
1010
1011 // Show a new answer screen.
1012 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -07001013 AnswerBindings.createAnswerScreen(
1014 call.getId(),
1015 call.isVideoCall(),
1016 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -07001017 call.getVideoTech().isSelfManagedCamera(),
1018 shouldAllowAnswerAndRelease(call),
1019 CallList.getInstance().getBackgroundCall() != null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001020 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), TAG_ANSWER_SCREEN);
1021
1022 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
1023 didShowAnswerScreen = true;
1024 return true;
1025 }
1026
Eric Erfanian90508232017-03-24 09:31:16 -07001027 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
1028 if (CallList.getInstance().getActiveCall() == null) {
1029 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
1030 return false;
1031 }
1032 if (getSystemService(TelephonyManager.class).getPhoneType()
1033 == TelephonyManager.PHONE_TYPE_CDMA) {
1034 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
1035 return false;
1036 }
1037 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
1038 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
1039 return false;
1040 }
1041 if (!ConfigProviderBindings.get(this).getBoolean(CONFIG_ANSWER_AND_RELEASE_ENABLED, true)) {
1042 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
1043 return false;
1044 }
1045
1046 return true;
1047 }
1048
Eric Erfanianccca3152017-02-22 16:32:36 -08001049 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
1050 if (!didShowAnswerScreen) {
1051 return false;
1052 }
1053 AnswerScreen answerScreen = getAnswerScreen();
1054 if (answerScreen != null) {
1055 transaction.remove(answerScreen.getAnswerScreenFragment());
1056 }
1057
1058 didShowAnswerScreen = false;
1059 return true;
1060 }
1061
1062 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
1063 if (didShowInCallScreen) {
1064 return false;
1065 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001066 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
1067 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), TAG_IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001068 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1069 didShowInCallScreen = true;
1070 return true;
1071 }
1072
1073 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
1074 if (!didShowInCallScreen) {
1075 return false;
1076 }
1077 InCallScreen inCallScreen = getInCallScreen();
1078 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001079 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -08001080 }
1081 didShowInCallScreen = false;
1082 return true;
1083 }
1084
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001085 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001086 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001087 VideoCallScreen videoCallScreen = getVideoCallScreen();
1088 if (videoCallScreen.getCallId().equals(call.getId())) {
1089 return false;
1090 }
1091 LogUtil.i(
1092 "InCallActivity.showVideoCallScreenFragment",
1093 "video call fragment exists but arguments do not match");
1094 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -08001095 }
1096
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001097 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
1098
Eric Erfanian90508232017-03-24 09:31:16 -07001099 VideoCallScreen videoCallScreen =
1100 VideoBindings.createVideoCallScreen(
1101 call.getId(), call.getVideoTech().shouldUseSurfaceView());
Eric Erfanianccca3152017-02-22 16:32:36 -08001102 transaction.add(R.id.main, videoCallScreen.getVideoCallScreenFragment(), TAG_VIDEO_CALL_SCREEN);
1103
1104 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1105 didShowVideoCallScreen = true;
1106 return true;
1107 }
1108
1109 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
1110 if (!didShowVideoCallScreen) {
1111 return false;
1112 }
1113 VideoCallScreen videoCallScreen = getVideoCallScreen();
1114 if (videoCallScreen != null) {
1115 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
1116 }
1117 didShowVideoCallScreen = false;
1118 return true;
1119 }
1120
1121 AnswerScreen getAnswerScreen() {
1122 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(TAG_ANSWER_SCREEN);
1123 }
1124
1125 InCallScreen getInCallScreen() {
1126 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_IN_CALL_SCREEN);
1127 }
1128
1129 VideoCallScreen getVideoCallScreen() {
1130 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(TAG_VIDEO_CALL_SCREEN);
1131 }
1132
1133 @Override
1134 public void onPseudoScreenStateChanged(boolean isOn) {
1135 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
1136 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
1137 }
1138
1139 /**
1140 * For some touch related issue, turning off the screen can be faked by drawing a black view over
1141 * the activity. All touch events started when the screen is "off" is rejected.
1142 *
1143 * @see PseudoScreenState
1144 */
1145 @Override
1146 public boolean dispatchTouchEvent(MotionEvent event) {
1147 // Reject any gesture that started when the screen is in the fake off state.
1148 if (touchDownWhenPseudoScreenOff) {
1149 if (event.getAction() == MotionEvent.ACTION_UP) {
1150 touchDownWhenPseudoScreenOff = false;
1151 }
1152 return true;
1153 }
1154 // Reject all touch event when the screen is in the fake off state.
1155 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
1156 if (event.getAction() == MotionEvent.ACTION_DOWN) {
1157 touchDownWhenPseudoScreenOff = true;
1158 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
1159 }
1160 return true;
1161 }
1162 return super.dispatchTouchEvent(event);
1163 }
1164
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001165 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -08001166 public final boolean shouldShow;
1167 public final DialerCall call;
1168
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001169 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001170 this.shouldShow = shouldShow;
1171 this.call = call;
1172 }
1173 }
1174}