blob: 3fc7f6c76976aed4ae073e1ad32f0f8855ef2471 [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
linyuhc3968e62017-11-20 17:40:50 -080019import android.app.ActivityManager;
20import android.app.ActivityManager.AppTask;
linyuh57b093b2017-11-17 14:32:32 -080021import android.app.ActivityManager.TaskDescription;
linyuh7b86f562017-11-16 11:24:09 -080022import android.app.AlertDialog;
linyuhf99f6302017-11-15 11:23:51 -080023import android.app.Dialog;
linyuh57b093b2017-11-17 14:32:32 -080024import android.app.KeyguardManager;
Eric Erfanianccca3152017-02-22 16:32:36 -080025import android.content.Context;
26import android.content.Intent;
linyuhc3968e62017-11-20 17:40:50 -080027import android.content.res.Configuration;
Eric Erfanianccca3152017-02-22 16:32:36 -080028import android.graphics.drawable.GradientDrawable;
29import android.graphics.drawable.GradientDrawable.Orientation;
30import android.os.Bundle;
Eric Erfanian2ca43182017-08-31 06:57:16 -070031import android.os.Trace;
Eric Erfanianccca3152017-02-22 16:32:36 -080032import android.support.annotation.ColorInt;
33import android.support.annotation.FloatRange;
linyuhc3968e62017-11-20 17:40:50 -080034import android.support.annotation.IntDef;
Eric Erfanianc857f902017-05-15 14:05:33 -070035import android.support.annotation.NonNull;
Eric Erfanianccca3152017-02-22 16:32:36 -080036import android.support.annotation.Nullable;
linyuhc3968e62017-11-20 17:40:50 -080037import android.support.annotation.VisibleForTesting;
Eric Erfanianccca3152017-02-22 16:32:36 -080038import android.support.v4.app.FragmentManager;
39import android.support.v4.app.FragmentTransaction;
linyuh57b093b2017-11-17 14:32:32 -080040import android.support.v4.content.res.ResourcesCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080041import android.support.v4.graphics.ColorUtils;
linyuhc3968e62017-11-20 17:40:50 -080042import android.telecom.CallAudioState;
43import android.telecom.PhoneAccountHandle;
Eric Erfanian90508232017-03-24 09:31:16 -070044import android.telephony.TelephonyManager;
Eric Erfanianccca3152017-02-22 16:32:36 -080045import android.view.KeyEvent;
46import android.view.MenuItem;
47import android.view.MotionEvent;
48import android.view.View;
linyuh9c327da2017-11-14 12:33:48 -080049import android.view.WindowManager;
linyuhc3968e62017-11-20 17:40:50 -080050import android.view.animation.Animation;
51import android.view.animation.AnimationUtils;
linyuh7b86f562017-11-16 11:24:09 -080052import android.widget.CheckBox;
53import android.widget.Toast;
linyuhf99f6302017-11-15 11:23:51 -080054import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
linyuhc3968e62017-11-20 17:40:50 -080055import com.android.dialer.animation.AnimUtils;
56import com.android.dialer.animation.AnimationListenerAdapter;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070057import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080058import com.android.dialer.common.LogUtil;
weijiaxu650e7cc2017-10-31 12:38:54 -070059import com.android.dialer.common.concurrent.ThreadUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080060import com.android.dialer.compat.ActivityCompat;
linyuhc3968e62017-11-20 17:40:50 -080061import com.android.dialer.compat.CompatUtils;
Eric Erfanian2ca43182017-08-31 06:57:16 -070062import com.android.dialer.configprovider.ConfigProviderBindings;
Eric Erfanianccca3152017-02-22 16:32:36 -080063import com.android.dialer.logging.Logger;
weijiaxu94df7202017-10-25 18:21:41 -070064import com.android.dialer.logging.LoggingBindings;
Eric Erfanian8369df02017-05-03 10:27:13 -070065import com.android.dialer.logging.ScreenEvent;
linyuhc3968e62017-11-20 17:40:50 -080066import com.android.dialer.util.ViewUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080067import com.android.incallui.answer.bindings.AnswerBindings;
68import com.android.incallui.answer.protocol.AnswerScreen;
69import com.android.incallui.answer.protocol.AnswerScreenDelegate;
70import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
71import com.android.incallui.answerproximitysensor.PseudoScreenState;
linyuh57b093b2017-11-17 14:32:32 -080072import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080073import com.android.incallui.call.CallList;
74import com.android.incallui.call.DialerCall;
75import com.android.incallui.call.DialerCall.State;
linyuh57b093b2017-11-17 14:32:32 -080076import com.android.incallui.call.TelecomAdapter;
Eric Erfanian2ca43182017-08-31 06:57:16 -070077import com.android.incallui.callpending.CallPendingActivity;
78import com.android.incallui.disconnectdialog.DisconnectMessage;
Eric Erfanianccca3152017-02-22 16:32:36 -080079import com.android.incallui.incall.bindings.InCallBindings;
80import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
81import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
82import com.android.incallui.incall.protocol.InCallScreen;
83import com.android.incallui.incall.protocol.InCallScreenDelegate;
84import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
linyuh7b86f562017-11-16 11:24:09 -080085import com.android.incallui.incalluilock.InCallUiLock;
wangqi219b8702018-02-13 09:34:41 -080086import com.android.incallui.rtt.bindings.RttBindings;
87import com.android.incallui.rtt.protocol.RttCallScreen;
88import com.android.incallui.rtt.protocol.RttCallScreenDelegate;
89import com.android.incallui.rtt.protocol.RttCallScreenDelegateFactory;
linyuhf99f6302017-11-15 11:23:51 -080090import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -080091import com.android.incallui.video.bindings.VideoBindings;
92import com.android.incallui.video.protocol.VideoCallScreen;
93import com.android.incallui.video.protocol.VideoCallScreenDelegate;
94import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
linyuhc3968e62017-11-20 17:40:50 -080095import com.google.common.base.Optional;
96import java.lang.annotation.Retention;
97import java.lang.annotation.RetentionPolicy;
98import java.util.ArrayList;
99import java.util.List;
Eric Erfanianccca3152017-02-22 16:32:36 -0800100
101/** Version of {@link InCallActivity} that shows the new UI */
102public class InCallActivity extends TransactionSafeFragmentActivity
103 implements AnswerScreenDelegateFactory,
104 InCallScreenDelegateFactory,
105 InCallButtonUiDelegateFactory,
106 VideoCallScreenDelegateFactory,
wangqi219b8702018-02-13 09:34:41 -0800107 RttCallScreenDelegateFactory,
Eric Erfanianccca3152017-02-22 16:32:36 -0800108 PseudoScreenState.StateChangedListener {
109
linyuhc3968e62017-11-20 17:40:50 -0800110 @Retention(RetentionPolicy.SOURCE)
111 @IntDef({
112 DIALPAD_REQUEST_NONE,
113 DIALPAD_REQUEST_SHOW,
114 DIALPAD_REQUEST_HIDE,
115 })
116 @interface DialpadRequestType {}
Eric Erfanian2ca43182017-08-31 06:57:16 -0700117
linyuhc3968e62017-11-20 17:40:50 -0800118 private static final int DIALPAD_REQUEST_NONE = 1;
119 private static final int DIALPAD_REQUEST_SHOW = 2;
120 private static final int DIALPAD_REQUEST_HIDE = 3;
linyuh57b093b2017-11-17 14:32:32 -0800121
linyuhc3968e62017-11-20 17:40:50 -0800122 private static Optional<Integer> audioRouteForTesting = Optional.absent();
linyuh57b093b2017-11-17 14:32:32 -0800123
linyuhc3968e62017-11-20 17:40:50 -0800124 private final InternationalCallOnWifiCallback internationalCallOnWifiCallback =
125 new InternationalCallOnWifiCallback();
126 private final SelectPhoneAccountListener selectPhoneAccountListener =
127 new SelectPhoneAccountListener();
Eric Erfanianccca3152017-02-22 16:32:36 -0800128
linyuhc3968e62017-11-20 17:40:50 -0800129 private Animation dialpadSlideInAnimation;
130 private Animation dialpadSlideOutAnimation;
131 private Dialog errorDialog;
132 private GradientDrawable backgroundDrawable;
linyuh69a25062017-11-15 16:18:51 -0800133 private InCallOrientationEventListener inCallOrientationEventListener;
linyuhc3968e62017-11-20 17:40:50 -0800134 private View pseudoBlackScreenOverlay;
135 private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment;
136 private String dtmfTextToPrepopulate;
137 private String showPostCharWaitDialogCallId;
138 private String showPostCharWaitDialogChars;
139 private boolean allowOrientationChange;
140 private boolean animateDialpadOnShow;
Eric Erfanianccca3152017-02-22 16:32:36 -0800141 private boolean didShowAnswerScreen;
142 private boolean didShowInCallScreen;
143 private boolean didShowVideoCallScreen;
wangqi219b8702018-02-13 09:34:41 -0800144 private boolean didShowRttCallScreen;
linyuh9c327da2017-11-14 12:33:48 -0800145 private boolean dismissKeyguard;
Eric Erfanianccca3152017-02-22 16:32:36 -0800146 private boolean isInShowMainInCallFragment;
linyuhc3968e62017-11-20 17:40:50 -0800147 private boolean isRecreating; // whether the activity is going to be recreated
148 private boolean isVisible;
Eric Erfanianccca3152017-02-22 16:32:36 -0800149 private boolean needDismissPendingDialogs;
linyuhc3968e62017-11-20 17:40:50 -0800150 private boolean showPostCharWaitDialogOnResume;
151 private boolean touchDownWhenPseudoScreenOff;
152 private int[] backgroundDrawableColors;
153 @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
Eric Erfanianccca3152017-02-22 16:32:36 -0800154
155 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700156 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800157 Intent intent = new Intent(Intent.ACTION_MAIN, null);
158 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
159 intent.setClass(context, InCallActivity.class);
linyuhc3968e62017-11-20 17:40:50 -0800160 if (showDialpad) {
161 intent.putExtra(IntentExtraNames.SHOW_DIALPAD, true);
162 }
163 intent.putExtra(IntentExtraNames.NEW_OUTGOING_CALL, newOutgoingCall);
164 intent.putExtra(IntentExtraNames.FOR_FULL_SCREEN, isForFullScreen);
Eric Erfanianccca3152017-02-22 16:32:36 -0800165 return intent;
166 }
167
168 @Override
169 protected void onResumeFragments() {
170 super.onResumeFragments();
171 if (needDismissPendingDialogs) {
172 dismissPendingDialogs();
173 }
174 }
175
176 @Override
linyuhc3968e62017-11-20 17:40:50 -0800177 protected void onCreate(Bundle bundle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700178 Trace.beginSection("InCallActivity.onCreate");
linyuhc3968e62017-11-20 17:40:50 -0800179 super.onCreate(bundle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800180
linyuhc3968e62017-11-20 17:40:50 -0800181 if (bundle != null) {
182 didShowAnswerScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN);
183 didShowInCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN);
184 didShowVideoCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN);
wangqi153af2f2018-02-15 16:21:49 -0800185 didShowRttCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -0800186 }
187
linyuhc3968e62017-11-20 17:40:50 -0800188 setWindowFlags();
189 setContentView(R.layout.incall_screen);
190 internalResolveIntent(getIntent());
191
192 boolean isLandscape =
193 getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
194 boolean isRtl = ViewUtil.isRtl();
195 if (isLandscape) {
196 dialpadSlideInAnimation =
197 AnimationUtils.loadAnimation(
198 this, isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
199 dialpadSlideOutAnimation =
200 AnimationUtils.loadAnimation(
201 this, isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
202 } else {
203 dialpadSlideInAnimation = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
204 dialpadSlideOutAnimation =
205 AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
206 }
207 dialpadSlideInAnimation.setInterpolator(AnimUtils.EASE_IN);
208 dialpadSlideOutAnimation.setInterpolator(AnimUtils.EASE_OUT);
209 dialpadSlideOutAnimation.setAnimationListener(
210 new AnimationListenerAdapter() {
211 @Override
212 public void onAnimationEnd(Animation animation) {
213 hideDialpadFragment();
214 }
215 });
216
217 if (bundle != null && showDialpadRequest == DIALPAD_REQUEST_NONE) {
218 // If the dialpad was shown before, set related variables so that it can be shown and
219 // populated with the previous DTMF text during onResume().
220 if (bundle.containsKey(IntentExtraNames.SHOW_DIALPAD)) {
221 boolean showDialpad = bundle.getBoolean(IntentExtraNames.SHOW_DIALPAD);
222 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
223 animateDialpadOnShow = false;
224 }
225 dtmfTextToPrepopulate = bundle.getString(KeysForSavedInstance.DIALPAD_TEXT);
226
227 SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment =
228 (SelectPhoneAccountDialogFragment)
229 getFragmentManager().findFragmentByTag(Tags.SELECT_ACCOUNT_FRAGMENT);
230 if (selectPhoneAccountDialogFragment != null) {
231 selectPhoneAccountDialogFragment.setListener(selectPhoneAccountListener);
232 }
233 }
234
235 InternationalCallOnWifiDialogFragment existingInternationalCallOnWifiDialogFragment =
236 (InternationalCallOnWifiDialogFragment)
237 getSupportFragmentManager().findFragmentByTag(Tags.INTERNATIONAL_CALL_ON_WIFI);
238 if (existingInternationalCallOnWifiDialogFragment != null) {
239 existingInternationalCallOnWifiDialogFragment.setCallback(internationalCallOnWifiCallback);
240 }
241
linyuh69a25062017-11-15 16:18:51 -0800242 inCallOrientationEventListener = new InCallOrientationEventListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800243
244 getWindow()
245 .getDecorView()
246 .setSystemUiVisibility(
247 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
248
249 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700250 sendBroadcast(CallPendingActivity.getFinishBroadcast());
251 Trace.endSection();
weijiaxuc950a9b2017-11-06 16:39:04 -0800252 Logger.get(this)
253 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
254 Logger.get(this)
255 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
Eric Erfanianccca3152017-02-22 16:32:36 -0800256 }
257
linyuhc3968e62017-11-20 17:40:50 -0800258 private void setWindowFlags() {
259 // Allow the activity to be shown when the screen is locked and filter out touch events that are
260 // "too fat".
261 int flags =
262 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
263 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
264
linyuhf79d1cb2017-12-15 17:49:56 -0800265 // When the audio stream is not via Bluetooth, turn on the screen once the activity is shown.
266 // When the audio stream is via Bluetooth, turn on the screen only for an incoming call.
linyuhc3968e62017-11-20 17:40:50 -0800267 final int audioRoute = getAudioRoute();
linyuhf79d1cb2017-12-15 17:49:56 -0800268 if (audioRoute != CallAudioState.ROUTE_BLUETOOTH
269 || CallList.getInstance().getIncomingCall() != null) {
linyuhc3968e62017-11-20 17:40:50 -0800270 flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
271 }
272
273 getWindow().addFlags(flags);
274 }
275
276 private static int getAudioRoute() {
277 if (audioRouteForTesting.isPresent()) {
278 return audioRouteForTesting.get();
279 }
280
281 return AudioModeProvider.getInstance().getAudioState().getRoute();
282 }
283
284 @VisibleForTesting(otherwise = VisibleForTesting.NONE)
285 public static void setAudioRouteForTesting(int audioRoute) {
286 audioRouteForTesting = Optional.of(audioRoute);
287 }
288
289 private void internalResolveIntent(Intent intent) {
290 if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
291 return;
292 }
293
294 if (intent.hasExtra(IntentExtraNames.SHOW_DIALPAD)) {
295 // IntentExtraNames.SHOW_DIALPAD can be used to specify whether the DTMF dialpad should be
296 // initially visible. If the extra is absent, leave the dialpad in its previous state.
297 boolean showDialpad = intent.getBooleanExtra(IntentExtraNames.SHOW_DIALPAD, false);
298 relaunchedFromDialer(showDialpad);
299 }
300
301 DialerCall outgoingCall = CallList.getInstance().getOutgoingCall();
302 if (outgoingCall == null) {
303 outgoingCall = CallList.getInstance().getPendingOutgoingCall();
304 }
305 if (intent.getBooleanExtra(IntentExtraNames.NEW_OUTGOING_CALL, false)) {
306 intent.removeExtra(IntentExtraNames.NEW_OUTGOING_CALL);
307
308 // InCallActivity is responsible for disconnecting a new outgoing call if there is no way of
309 // making it (i.e. no valid call capable accounts).
310 // If the version is not MSIM compatible, ignore this code.
311 if (CompatUtils.isMSIMCompatible()
312 && InCallPresenter.isCallWithNoValidAccounts(outgoingCall)) {
313 LogUtil.i(
314 "InCallActivity.internalResolveIntent", "Call with no valid accounts, disconnecting");
315 outgoingCall.disconnect();
316 }
317
318 dismissKeyguard(true);
319 }
320
321 if (showPhoneAccountSelectionDialog()) {
322 hideMainInCallFragment();
323 }
324 }
325
326 /**
327 * When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad should
328 * be shown on launch.
329 *
330 * @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and {@code
331 * false} to indicate no change should be made to the dialpad visibility.
332 */
333 private void relaunchedFromDialer(boolean showDialpad) {
334 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
335 animateDialpadOnShow = true;
336
337 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
338 // If there's only one line in use, AND it's on hold, then we're sure the user
339 // wants to use the dialpad toward the exact line, so un-hold the holding line.
340 DialerCall call = CallList.getInstance().getActiveOrBackgroundCall();
341 if (call != null && call.getState() == State.ONHOLD) {
342 call.unhold();
343 }
344 }
345 }
346
347 /**
348 * Show a phone account selection dialog if there is a call waiting for phone account selection.
349 *
350 * @return true if the dialog was shown.
351 */
352 private boolean showPhoneAccountSelectionDialog() {
353 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
354 if (waitingForAccountCall == null) {
355 return false;
356 }
357
358 Bundle extras = waitingForAccountCall.getIntentExtras();
359 List<PhoneAccountHandle> phoneAccountHandles =
360 extras == null
361 ? new ArrayList<>()
362 : extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
363
364 selectPhoneAccountDialogFragment =
365 SelectPhoneAccountDialogFragment.newInstance(
366 R.string.select_phone_account_for_calls,
367 true /* canSetDefault */,
368 0 /* setDefaultResId */,
369 phoneAccountHandles,
370 selectPhoneAccountListener,
371 waitingForAccountCall.getId(),
372 null /* hints */);
373 selectPhoneAccountDialogFragment.show(getFragmentManager(), Tags.SELECT_ACCOUNT_FRAGMENT);
374 return true;
375 }
376
Eric Erfanianccca3152017-02-22 16:32:36 -0800377 @Override
378 protected void onSaveInstanceState(Bundle out) {
linyuh57b093b2017-11-17 14:32:32 -0800379 LogUtil.enterBlock("InCallActivity.onSaveInstanceState");
380
381 // TODO: DialpadFragment should handle this as part of its own state
linyuhc3968e62017-11-20 17:40:50 -0800382 out.putBoolean(IntentExtraNames.SHOW_DIALPAD, isDialpadVisible());
linyuh57b093b2017-11-17 14:32:32 -0800383 DialpadFragment dialpadFragment = getDialpadFragment();
384 if (dialpadFragment != null) {
linyuhc3968e62017-11-20 17:40:50 -0800385 out.putString(KeysForSavedInstance.DIALPAD_TEXT, dialpadFragment.getDtmfText());
linyuh57b093b2017-11-17 14:32:32 -0800386 }
387
linyuhc3968e62017-11-20 17:40:50 -0800388 out.putBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN, didShowAnswerScreen);
389 out.putBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN, didShowInCallScreen);
390 out.putBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN, didShowVideoCallScreen);
wangqi153af2f2018-02-15 16:21:49 -0800391 out.putBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN, didShowRttCallScreen);
linyuh57b093b2017-11-17 14:32:32 -0800392
Eric Erfanianccca3152017-02-22 16:32:36 -0800393 super.onSaveInstanceState(out);
394 isVisible = false;
395 }
396
397 @Override
398 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700399 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800400 super.onStart();
linyuh57b093b2017-11-17 14:32:32 -0800401
Eric Erfanianccca3152017-02-22 16:32:36 -0800402 isVisible = true;
403 showMainInCallFragment();
linyuh57b093b2017-11-17 14:32:32 -0800404
405 InCallPresenter.getInstance().setActivity(this);
406 enableInCallOrientationEventListener(
407 getRequestedOrientation()
408 == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
409 InCallPresenter.getInstance().onActivityStarted();
410
yueg10f6e822018-01-17 15:32:18 -0800411 if (!isRecreating) {
412 InCallPresenter.getInstance().onUiShowing(true);
413 }
414
Eric Erfanianccca3152017-02-22 16:32:36 -0800415 if (ActivityCompat.isInMultiWindowMode(this)
416 && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
417 // Hide the dialpad because there may not be enough room
418 showDialpadFragment(false, false);
419 }
linyuh57b093b2017-11-17 14:32:32 -0800420
Eric Erfanian2ca43182017-08-31 06:57:16 -0700421 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800422 }
423
424 @Override
425 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700426 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800427 super.onResume();
linyuhc3968e62017-11-20 17:40:50 -0800428
429 if (!InCallPresenter.getInstance().isReadyForTearDown()) {
430 updateTaskDescription();
yueg10f6e822018-01-17 15:32:18 -0800431 InCallPresenter.getInstance().updateNotification();
linyuhc3968e62017-11-20 17:40:50 -0800432 }
433
434 // If there is a pending request to show or hide the dialpad, handle that now.
435 if (showDialpadRequest != DIALPAD_REQUEST_NONE) {
436 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
437 // Exit fullscreen so that the user has access to the dialpad hide/show button.
438 // This is important when showing the dialpad from within dialer.
439 InCallPresenter.getInstance().setFullScreen(false /* isFullScreen */, true /* force */);
440
441 showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
442 animateDialpadOnShow = false;
443
444 DialpadFragment dialpadFragment = getDialpadFragment();
445 if (dialpadFragment != null) {
446 dialpadFragment.setDtmfText(dtmfTextToPrepopulate);
447 dtmfTextToPrepopulate = null;
448 }
449 } else {
450 LogUtil.i("InCallActivity.onResume", "Force-hide the dialpad");
451 if (getDialpadFragment() != null) {
452 showDialpadFragment(false /* show */, false /* animate */);
453 }
454 }
455 showDialpadRequest = DIALPAD_REQUEST_NONE;
456 }
457 updateNavigationBar(isDialpadVisible());
458
459 if (showPostCharWaitDialogOnResume) {
460 showDialogForPostCharWait(showPostCharWaitDialogCallId, showPostCharWaitDialogChars);
461 }
462
463 CallList.getInstance()
464 .onInCallUiShown(getIntent().getBooleanExtra(IntentExtraNames.FOR_FULL_SCREEN, false));
465
Eric Erfanianccca3152017-02-22 16:32:36 -0800466 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
467 pseudoScreenState.addListener(this);
468 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700469 Trace.endSection();
weijiaxu650e7cc2017-10-31 12:38:54 -0700470 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
471 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800472 () ->
473 Logger.get(this)
474 .logRecordMemory(LoggingBindings.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700475 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800476 }
477
Eric Erfanianccca3152017-02-22 16:32:36 -0800478 @Override
479 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700480 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800481 super.onPause();
linyuh57b093b2017-11-17 14:32:32 -0800482
483 DialpadFragment dialpadFragment = getDialpadFragment();
484 if (dialpadFragment != null) {
485 dialpadFragment.onDialerKeyUp(null);
486 }
487
yueg10f6e822018-01-17 15:32:18 -0800488 InCallPresenter.getInstance().updateNotification();
linyuh57b093b2017-11-17 14:32:32 -0800489
Eric Erfanianccca3152017-02-22 16:32:36 -0800490 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700491 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800492 }
493
494 @Override
495 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700496 Trace.beginSection("InCallActivity.onStop");
wangqi4d705e52017-09-28 12:23:35 -0700497 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800498 super.onStop();
linyuh57b093b2017-11-17 14:32:32 -0800499
500 // Disconnects the call waiting for a phone account when the activity is hidden (e.g., after the
501 // user presses the home button).
502 // Without this the pending call will get stuck on phone account selection and new calls can't
503 // be created.
504 // Skip this when the screen is locked since the activity may complete its current life cycle
505 // and restart.
linyuhc3968e62017-11-20 17:40:50 -0800506 if (!isRecreating && !getSystemService(KeyguardManager.class).isKeyguardLocked()) {
linyuh57b093b2017-11-17 14:32:32 -0800507 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
508 if (waitingForAccountCall != null) {
509 waitingForAccountCall.disconnect();
510 }
511 }
512
513 enableInCallOrientationEventListener(false);
514 InCallPresenter.getInstance().updateIsChangingConfigurations();
515 InCallPresenter.getInstance().onActivityStopped();
linyuhc3968e62017-11-20 17:40:50 -0800516 if (!isRecreating) {
yueg10f6e822018-01-17 15:32:18 -0800517 InCallPresenter.getInstance().onUiShowing(false);
linyuh57b093b2017-11-17 14:32:32 -0800518 if (errorDialog != null) {
519 errorDialog.dismiss();
520 }
521 }
522
yueg10f6e822018-01-17 15:32:18 -0800523 if (isFinishing()) {
524 InCallPresenter.getInstance().unsetActivity(this);
525 }
526
Eric Erfanian2ca43182017-08-31 06:57:16 -0700527 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800528 }
529
530 @Override
531 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700532 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800533 super.onDestroy();
linyuh57b093b2017-11-17 14:32:32 -0800534
535 InCallPresenter.getInstance().unsetActivity(this);
536 InCallPresenter.getInstance().updateIsChangingConfigurations();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700537 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800538 }
539
540 @Override
541 public void finish() {
542 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700543 // When user select incall ui from recents after the call is disconnected, it tries to launch
544 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
545 // crash.
546 // By calling finishAndRemoveTask() instead of finish() the task associated with
547 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
548 // this case.
549 //
550 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
551 // clear the task since there could be parent activity in the same task that's still alive.
552 // But InCallActivity is special since it's singleInstance which means it's root activity and
553 // only instance of activity in the task. So it should be safe to also remove task when
554 // finishing.
555 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
556 // finishes, the task should also be removed since it doesn't make sense to go back to it in
557 // anyway anymore.
558 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800559 }
560 }
561
562 private boolean shouldCloseActivityOnFinish() {
linyuhc3968e62017-11-20 17:40:50 -0800563 if (!isVisible) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800564 LogUtil.i(
565 "InCallActivity.shouldCloseActivityOnFinish",
566 "allowing activity to be closed because it's not visible");
567 return true;
568 }
569
twyen8efb4952017-10-06 16:35:54 -0700570 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800571 LogUtil.i(
572 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700573 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800574 return false;
575 }
576
577 LogUtil.i(
578 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700579 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800580 return true;
581 }
582
583 @Override
584 protected void onNewIntent(Intent intent) {
linyuhc3968e62017-11-20 17:40:50 -0800585 LogUtil.enterBlock("InCallActivity.onNewIntent");
Eric Erfanianccca3152017-02-22 16:32:36 -0800586
587 // If the screen is off, we need to make sure it gets turned on for incoming calls.
588 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
589 // when the activity is first created. Therefore, to ensure the screen is turned on
590 // for the call waiting case, we recreate() the current activity. There should be no jank from
591 // this since the screen is already off and will remain so until our new activity is up.
linyuhc3968e62017-11-20 17:40:50 -0800592 if (!isVisible) {
593 onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800594 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
595 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700596 } else {
linyuhc3968e62017-11-20 17:40:50 -0800597 onNewIntent(intent, false /* isRecreating */);
598 }
599 }
600
yuega3305352018-01-09 11:02:47 -0800601 @VisibleForTesting
602 void onNewIntent(Intent intent, boolean isRecreating) {
linyuhc3968e62017-11-20 17:40:50 -0800603 this.isRecreating = isRecreating;
604
605 // We're being re-launched with a new Intent. Since it's possible for a single InCallActivity
606 // instance to persist indefinitely (even if we finish() ourselves), this sequence can
607 // happen any time the InCallActivity needs to be displayed.
608
609 // Stash away the new intent so that we can get it in the future by calling getIntent().
610 // Otherwise getIntent() will return the original Intent from when we first got created.
611 setIntent(intent);
612
613 // Activities are always paused before receiving a new intent, so we can count on our onResume()
614 // method being called next.
615
616 // Just like in onCreate(), handle the intent.
617 // Skip if InCallActivity is going to be recreated since this will be called in onCreate().
618 if (!isRecreating) {
619 internalResolveIntent(intent);
Eric Erfanianccca3152017-02-22 16:32:36 -0800620 }
621 }
622
623 @Override
624 public void onBackPressed() {
linyuh57b093b2017-11-17 14:32:32 -0800625 LogUtil.enterBlock("InCallActivity.onBackPressed");
626
linyuhc3968e62017-11-20 17:40:50 -0800627 if (!isVisible) {
linyuh57b093b2017-11-17 14:32:32 -0800628 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800629 }
linyuh57b093b2017-11-17 14:32:32 -0800630
631 if (!getCallCardFragmentVisible()) {
632 return;
633 }
634
635 DialpadFragment dialpadFragment = getDialpadFragment();
636 if (dialpadFragment != null && dialpadFragment.isVisible()) {
637 showDialpadFragment(false /* show */, true /* animate */);
638 return;
639 }
640
641 if (CallList.getInstance().getIncomingCall() != null) {
642 LogUtil.i(
643 "InCallActivity.onBackPressed",
644 "Ignore the press of the back key when an incoming call is ringing");
645 return;
646 }
647
648 // Nothing special to do. Fall back to the default behavior.
649 super.onBackPressed();
Eric Erfanianccca3152017-02-22 16:32:36 -0800650 }
651
652 @Override
653 public boolean onOptionsItemSelected(MenuItem item) {
654 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
655 if (item.getItemId() == android.R.id.home) {
656 onBackPressed();
657 return true;
658 }
659 return super.onOptionsItemSelected(item);
660 }
661
662 @Override
663 public boolean onKeyUp(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800664 DialpadFragment dialpadFragment = getDialpadFragment();
665 if (dialpadFragment != null
666 && dialpadFragment.isVisible()
667 && dialpadFragment.onDialerKeyUp(event)) {
668 return true;
669 }
670
671 if (keyCode == KeyEvent.KEYCODE_CALL) {
672 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
673 return true;
674 }
675
676 return super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800677 }
678
679 @Override
680 public boolean onKeyDown(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800681 switch (keyCode) {
682 case KeyEvent.KEYCODE_CALL:
683 if (!InCallPresenter.getInstance().handleCallKey()) {
684 LogUtil.e(
685 "InCallActivity.onKeyDown",
686 "InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
687 }
688 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
689 return true;
690
691 // Note that KEYCODE_ENDCALL isn't handled here as the standard system-wide handling of it
692 // is exactly what's needed, namely
693 // (1) "hang up" if there's an active call, or
694 // (2) "don't answer" if there's an incoming call.
695 // (See PhoneWindowManager for implementation details.)
696
697 case KeyEvent.KEYCODE_CAMERA:
698 // Consume KEYCODE_CAMERA since it's easy to accidentally press the camera button.
699 return true;
700
701 case KeyEvent.KEYCODE_VOLUME_UP:
702 case KeyEvent.KEYCODE_VOLUME_DOWN:
703 case KeyEvent.KEYCODE_VOLUME_MUTE:
704 // Ringer silencing handled by PhoneWindowManager.
705 break;
706
707 case KeyEvent.KEYCODE_MUTE:
708 TelecomAdapter.getInstance()
709 .mute(!AudioModeProvider.getInstance().getAudioState().isMuted());
710 return true;
711
712 case KeyEvent.KEYCODE_SLASH:
713 // When verbose logging is enabled, dump the view for debugging/testing purposes.
714 if (LogUtil.isVerboseEnabled()) {
715 View decorView = getWindow().getDecorView();
716 LogUtil.v("InCallActivity.onKeyDown", "View dump:\n%s", decorView);
717 return true;
718 }
719 break;
720
721 case KeyEvent.KEYCODE_EQUALS:
722 break;
723
724 default: // fall out
725 }
726
727 // Pass other key events to DialpadFragment's "onDialerKeyDown" method in case the user types
728 // in DTMF (Dual-tone multi-frequency signaling) code.
729 DialpadFragment dialpadFragment = getDialpadFragment();
730 if (dialpadFragment != null
731 && dialpadFragment.isVisible()
732 && dialpadFragment.onDialerKeyDown(event)) {
733 return true;
734 }
735
736 return super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800737 }
738
739 public boolean isInCallScreenAnimating() {
740 return false;
741 }
742
743 public void showConferenceFragment(boolean show) {
744 if (show) {
745 startActivity(new Intent(this, ManageConferenceActivity.class));
746 }
747 }
748
linyuhc3968e62017-11-20 17:40:50 -0800749 public void showDialpadFragment(boolean show, boolean animate) {
750 if (show == isDialpadVisible()) {
751 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800752 }
linyuhc3968e62017-11-20 17:40:50 -0800753
754 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
755 if (dialpadFragmentManager == null) {
756 LogUtil.i("InCallActivity.showDialpadFragment", "Unable to obtain a FragmentManager");
757 return;
758 }
759
760 if (!animate) {
761 if (show) {
762 showDialpadFragment();
763 } else {
764 hideDialpadFragment();
765 }
766 } else {
767 if (show) {
768 showDialpadFragment();
769 getDialpadFragment().animateShowDialpad();
770 }
771 getDialpadFragment()
772 .getView()
773 .startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
774 }
775
776 ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
777 if (sensor != null) {
778 sensor.onDialpadVisible(show);
779 }
780 showDialpadRequest = DIALPAD_REQUEST_NONE;
781
782 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
783 // repositions itself.
784 getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
785 }
786
787 private void showDialpadFragment() {
788 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
789 if (dialpadFragmentManager == null) {
790 return;
791 }
792
793 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
794 DialpadFragment dialpadFragment = getDialpadFragment();
795 if (dialpadFragment == null) {
796 transaction.add(getDialpadContainerId(), new DialpadFragment(), Tags.DIALPAD_FRAGMENT);
797 } else {
798 transaction.show(dialpadFragment);
calderwoodrad5883872017-12-12 15:29:12 -0800799 dialpadFragment.setUserVisibleHint(true);
linyuhc3968e62017-11-20 17:40:50 -0800800 }
801 transaction.commitAllowingStateLoss();
802 dialpadFragmentManager.executePendingTransactions();
803
804 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, this);
805 updateNavigationBar(true /* isDialpadVisible */);
806 }
807
808 private void hideDialpadFragment() {
809 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
810 if (dialpadFragmentManager == null) {
811 return;
812 }
813
calderwoodrad5883872017-12-12 15:29:12 -0800814 DialpadFragment dialpadFragment = getDialpadFragment();
linyuhc3968e62017-11-20 17:40:50 -0800815 if (dialpadFragment != null) {
816 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
817 transaction.hide(dialpadFragment);
818 transaction.commitAllowingStateLoss();
819 dialpadFragmentManager.executePendingTransactions();
calderwoodrad5883872017-12-12 15:29:12 -0800820 dialpadFragment.setUserVisibleHint(false);
linyuhc3968e62017-11-20 17:40:50 -0800821 }
822 updateNavigationBar(false /* isDialpadVisible */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800823 }
824
825 public boolean isDialpadVisible() {
linyuh69a25062017-11-15 16:18:51 -0800826 DialpadFragment dialpadFragment = getDialpadFragment();
calderwoodraa584bcd2018-01-24 12:19:56 -0800827 return dialpadFragment != null
828 && dialpadFragment.isAdded()
829 && !dialpadFragment.isHidden()
830 && dialpadFragment.getView() != null
831 && dialpadFragment.getUserVisibleHint();
linyuh69a25062017-11-15 16:18:51 -0800832 }
833
linyuhc3968e62017-11-20 17:40:50 -0800834 /** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
linyuh69a25062017-11-15 16:18:51 -0800835 @Nullable
linyuhc3968e62017-11-20 17:40:50 -0800836 private DialpadFragment getDialpadFragment() {
linyuh69a25062017-11-15 16:18:51 -0800837 FragmentManager fragmentManager = getDialpadFragmentManager();
838 if (fragmentManager == null) {
839 return null;
840 }
linyuhc3968e62017-11-20 17:40:50 -0800841 return (DialpadFragment) fragmentManager.findFragmentByTag(Tags.DIALPAD_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800842 }
843
844 public void onForegroundCallChanged(DialerCall newForegroundCall) {
linyuh57b093b2017-11-17 14:32:32 -0800845 updateTaskDescription();
846
847 if (newForegroundCall == null || !didShowAnswerScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800848 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
linyuh57b093b2017-11-17 14:32:32 -0800849 updateWindowBackgroundColor(0 /* progress */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800850 }
851 }
852
linyuhc3968e62017-11-20 17:40:50 -0800853 private void updateTaskDescription() {
linyuh57b093b2017-11-17 14:32:32 -0800854 int color =
855 getResources().getBoolean(R.bool.is_layout_landscape)
856 ? ResourcesCompat.getColor(
857 getResources(), R.color.statusbar_background_color, getTheme())
858 : InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
859 setTaskDescription(
860 new TaskDescription(
861 getResources().getString(R.string.notification_ongoing_call), null /* icon */, color));
862 }
863
Eric Erfanianccca3152017-02-22 16:32:36 -0800864 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
865 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
866 @ColorInt int top;
867 @ColorInt int middle;
868 @ColorInt int bottom;
869 @ColorInt int gray = 0x66000000;
870
871 if (ActivityCompat.isInMultiWindowMode(this)) {
872 top = themeColorManager.getBackgroundColorSolid();
873 middle = themeColorManager.getBackgroundColorSolid();
874 bottom = themeColorManager.getBackgroundColorSolid();
875 } else {
876 top = themeColorManager.getBackgroundColorTop();
877 middle = themeColorManager.getBackgroundColorMiddle();
878 bottom = themeColorManager.getBackgroundColorBottom();
879 }
880
881 if (progress < 0) {
882 float correctedProgress = Math.abs(progress);
883 top = ColorUtils.blendARGB(top, gray, correctedProgress);
884 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
885 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
886 }
887
888 boolean backgroundDirty = false;
889 if (backgroundDrawable == null) {
890 backgroundDrawableColors = new int[] {top, middle, bottom};
891 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
892 backgroundDirty = true;
893 } else {
894 if (backgroundDrawableColors[0] != top) {
895 backgroundDrawableColors[0] = top;
896 backgroundDirty = true;
897 }
898 if (backgroundDrawableColors[1] != middle) {
899 backgroundDrawableColors[1] = middle;
900 backgroundDirty = true;
901 }
902 if (backgroundDrawableColors[2] != bottom) {
903 backgroundDrawableColors[2] = bottom;
904 backgroundDirty = true;
905 }
906 if (backgroundDirty) {
907 backgroundDrawable.setColors(backgroundDrawableColors);
908 }
909 }
910
911 if (backgroundDirty) {
912 getWindow().setBackgroundDrawable(backgroundDrawable);
913 }
914 }
915
916 public boolean isVisible() {
917 return isVisible;
918 }
919
920 public boolean getCallCardFragmentVisible() {
921 return didShowInCallScreen || didShowVideoCallScreen;
922 }
923
924 public void dismissKeyguard(boolean dismiss) {
linyuh9c327da2017-11-14 12:33:48 -0800925 if (dismissKeyguard == dismiss) {
926 return;
927 }
928
929 dismissKeyguard = dismiss;
930 if (dismiss) {
931 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
932 } else {
933 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
934 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800935 }
936
linyuhc3968e62017-11-20 17:40:50 -0800937 public void showDialogForPostCharWait(String callId, String chars) {
938 if (isVisible) {
939 PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
940 fragment.show(getSupportFragmentManager(), Tags.POST_CHAR_DIALOG_FRAGMENT);
941
942 showPostCharWaitDialogOnResume = false;
943 showPostCharWaitDialogCallId = null;
944 showPostCharWaitDialogChars = null;
945 } else {
946 showPostCharWaitDialogOnResume = true;
947 showPostCharWaitDialogCallId = callId;
948 showPostCharWaitDialogChars = chars;
949 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800950 }
951
linyuh7b86f562017-11-16 11:24:09 -0800952 public void showDialogOrToastForDisconnectedCall(DisconnectMessage disconnectMessage) {
953 LogUtil.i(
954 "InCallActivity.showDialogOrToastForDisconnectedCall",
955 "disconnect cause: %s",
956 disconnectMessage);
957
958 if (disconnectMessage.dialog == null || isFinishing()) {
959 return;
960 }
961
962 dismissPendingDialogs();
963
964 // Show a toast if the app is in background when a dialog can't be visible.
965 if (!isVisible()) {
966 Toast.makeText(getApplicationContext(), disconnectMessage.toastMessage, Toast.LENGTH_LONG)
967 .show();
968 return;
969 }
970
971 // Show the dialog.
linyuhc3968e62017-11-20 17:40:50 -0800972 errorDialog = disconnectMessage.dialog;
linyuh7b86f562017-11-16 11:24:09 -0800973 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("showErrorDialog");
974 disconnectMessage.dialog.setOnDismissListener(
975 dialogInterface -> {
976 lock.release();
977 onDialogDismissed();
978 });
979 disconnectMessage.dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
980 disconnectMessage.dialog.show();
981 }
982
983 private void onDialogDismissed() {
linyuhc3968e62017-11-20 17:40:50 -0800984 errorDialog = null;
linyuh7b86f562017-11-16 11:24:09 -0800985 CallList.getInstance().onErrorDialogDismissed();
Eric Erfanianccca3152017-02-22 16:32:36 -0800986 }
987
988 public void dismissPendingDialogs() {
linyuhc3968e62017-11-20 17:40:50 -0800989 LogUtil.enterBlock("InCallActivity.dismissPendingDialogs");
linyuhf99f6302017-11-15 11:23:51 -0800990
991 if (!isVisible) {
992 // Defer the dismissing action as the activity is not visible and onSaveInstanceState may have
993 // been called.
Eric Erfanianccca3152017-02-22 16:32:36 -0800994 LogUtil.i(
995 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
996 needDismissPendingDialogs = true;
linyuhf99f6302017-11-15 11:23:51 -0800997 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800998 }
linyuhf99f6302017-11-15 11:23:51 -0800999
1000 // Dismiss the error dialog
linyuhf99f6302017-11-15 11:23:51 -08001001 if (errorDialog != null) {
1002 errorDialog.dismiss();
linyuhc3968e62017-11-20 17:40:50 -08001003 errorDialog = null;
linyuhf99f6302017-11-15 11:23:51 -08001004 }
1005
1006 // Dismiss the phone account selection dialog
linyuhf99f6302017-11-15 11:23:51 -08001007 if (selectPhoneAccountDialogFragment != null) {
1008 selectPhoneAccountDialogFragment.dismiss();
linyuhc3968e62017-11-20 17:40:50 -08001009 selectPhoneAccountDialogFragment = null;
linyuhf99f6302017-11-15 11:23:51 -08001010 }
1011
1012 // Dismiss the dialog for international call on WiFi
1013 InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
1014 (InternationalCallOnWifiDialogFragment)
linyuhc3968e62017-11-20 17:40:50 -08001015 getSupportFragmentManager().findFragmentByTag(Tags.INTERNATIONAL_CALL_ON_WIFI);
linyuhf99f6302017-11-15 11:23:51 -08001016 if (internationalCallOnWifiFragment != null) {
1017 internationalCallOnWifiFragment.dismiss();
1018 }
1019
1020 // Dismiss the answer screen
1021 AnswerScreen answerScreen = getAnswerScreen();
1022 if (answerScreen != null) {
1023 answerScreen.dismissPendingDialogs();
1024 }
1025
1026 needDismissPendingDialogs = false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001027 }
1028
linyuhc3968e62017-11-20 17:40:50 -08001029 private void enableInCallOrientationEventListener(boolean enable) {
linyuh69a25062017-11-15 16:18:51 -08001030 if (enable) {
1031 inCallOrientationEventListener.enable(true /* notifyDeviceOrientationChange */);
1032 } else {
1033 inCallOrientationEventListener.disable();
1034 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001035 }
1036
1037 public void setExcludeFromRecents(boolean exclude) {
linyuhc3968e62017-11-20 17:40:50 -08001038 int taskId = getTaskId();
1039
1040 List<AppTask> tasks = getSystemService(ActivityManager.class).getAppTasks();
1041 for (AppTask task : tasks) {
1042 try {
1043 if (task.getTaskInfo().id == taskId) {
1044 task.setExcludeFromRecents(exclude);
1045 }
1046 } catch (RuntimeException e) {
1047 LogUtil.e("InCallActivity.setExcludeFromRecents", "RuntimeException:\n%s", e);
1048 }
1049 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001050 }
1051
Eric Erfanianccca3152017-02-22 16:32:36 -08001052 @Nullable
1053 public FragmentManager getDialpadFragmentManager() {
1054 InCallScreen inCallScreen = getInCallScreen();
1055 if (inCallScreen != null) {
1056 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
1057 }
1058 return null;
1059 }
1060
1061 public int getDialpadContainerId() {
1062 return getInCallScreen().getAnswerAndDialpadContainerResourceId();
1063 }
1064
1065 @Override
1066 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
1067 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
1068 if (call == null) {
1069 // This is a work around for a bug where we attempt to create a new delegate after the call
1070 // has already been removed. An example of when this can happen is:
1071 // 1. incoming video call in landscape mode
1072 // 2. remote party hangs up
1073 // 3. activity switches from landscape to portrait
1074 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
1075 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
1076 // because this new state is transient and the activity will be destroyed soon.
1077 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
1078 return new AnswerScreenPresenterStub();
1079 } else {
1080 return new AnswerScreenPresenter(
1081 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
1082 }
1083 }
1084
1085 @Override
1086 public InCallScreenDelegate newInCallScreenDelegate() {
1087 return new CallCardPresenter(this);
1088 }
1089
1090 @Override
1091 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
1092 return new CallButtonPresenter(this);
1093 }
1094
1095 @Override
Eric Erfanian90508232017-03-24 09:31:16 -07001096 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
1097 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
1098 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
1099 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
1100 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001101 return new VideoCallPresenter();
1102 }
1103
1104 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001105 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -08001106 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001107 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001108 }
1109
linyuh7b86f562017-11-16 11:24:09 -08001110 public void showToastForWiFiToLteHandover(DialerCall call) {
1111 if (call.hasShownWiFiToLteHandoverToast()) {
1112 return;
1113 }
1114
1115 Toast.makeText(this, R.string.video_call_wifi_to_lte_handover_toast, Toast.LENGTH_LONG).show();
1116 call.setHasShownWiFiToLteHandoverToast();
Eric Erfanianccca3152017-02-22 16:32:36 -08001117 }
1118
linyuh7b86f562017-11-16 11:24:09 -08001119 public void showDialogOrToastForWifiHandoverFailure(DialerCall call) {
1120 if (call.showWifiHandoverAlertAsToast()) {
1121 Toast.makeText(this, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
1122 .show();
1123 return;
1124 }
1125
1126 dismissPendingDialogs();
1127
1128 AlertDialog.Builder builder =
1129 new AlertDialog.Builder(this).setTitle(R.string.video_call_lte_to_wifi_failed_title);
1130
1131 // This allows us to use the theme of the dialog instead of the activity
1132 View dialogCheckBoxView =
1133 View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null /* root */);
1134 CheckBox wifiHandoverFailureCheckbox =
1135 (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
1136 wifiHandoverFailureCheckbox.setChecked(false);
1137
1138 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("WifiFailedDialog");
linyuhc3968e62017-11-20 17:40:50 -08001139 errorDialog =
linyuh7b86f562017-11-16 11:24:09 -08001140 builder
1141 .setView(dialogCheckBoxView)
1142 .setMessage(R.string.video_call_lte_to_wifi_failed_message)
1143 .setOnCancelListener(dialogInterface -> onDialogDismissed())
1144 .setPositiveButton(
1145 android.R.string.ok,
1146 (dialogInterface, id) -> {
1147 call.setDoNotShowDialogForHandoffToWifiFailure(
1148 wifiHandoverFailureCheckbox.isChecked());
1149 dialogInterface.cancel();
1150 onDialogDismissed();
1151 })
1152 .setOnDismissListener(dialogInterface -> lock.release())
1153 .create();
linyuh7b86f562017-11-16 11:24:09 -08001154 errorDialog.show();
Eric Erfanianccca3152017-02-22 16:32:36 -08001155 }
1156
linyuh7b86f562017-11-16 11:24:09 -08001157 public void showDialogForInternationalCallOnWifi(@NonNull DialerCall call) {
1158 if (!InternationalCallOnWifiDialogFragment.shouldShow(this)) {
1159 LogUtil.i(
1160 "InCallActivity.showDialogForInternationalCallOnWifi",
1161 "InternationalCallOnWifiDialogFragment.shouldShow returned false");
1162 return;
1163 }
1164
1165 InternationalCallOnWifiDialogFragment fragment =
1166 InternationalCallOnWifiDialogFragment.newInstance(
linyuhc3968e62017-11-20 17:40:50 -08001167 call.getId(), internationalCallOnWifiCallback);
1168 fragment.show(getSupportFragmentManager(), Tags.INTERNATIONAL_CALL_ON_WIFI);
Eric Erfanianc857f902017-05-15 14:05:33 -07001169 }
1170
Eric Erfanian938468d2017-10-24 14:05:52 -07001171 @Override
1172 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
1173 super.onMultiWindowModeChanged(isInMultiWindowMode);
linyuh57b093b2017-11-17 14:32:32 -08001174 updateNavigationBar(isDialpadVisible());
1175 }
1176
linyuhc3968e62017-11-20 17:40:50 -08001177 private void updateNavigationBar(boolean isDialpadVisible) {
linyuh57b093b2017-11-17 14:32:32 -08001178 if (ActivityCompat.isInMultiWindowMode(this)) {
1179 return;
1180 }
1181
1182 View navigationBarBackground = getWindow().findViewById(R.id.navigation_bar_background);
1183 if (navigationBarBackground != null) {
1184 navigationBarBackground.setVisibility(isDialpadVisible ? View.VISIBLE : View.GONE);
Eric Erfanian938468d2017-10-24 14:05:52 -07001185 }
1186 }
1187
Eric Erfanianccca3152017-02-22 16:32:36 -08001188 public void setAllowOrientationChange(boolean allowOrientationChange) {
wangqi9982f0d2017-10-11 17:46:07 -07001189 if (this.allowOrientationChange == allowOrientationChange) {
1190 return;
1191 }
1192 this.allowOrientationChange = allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -08001193 if (!allowOrientationChange) {
1194 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
1195 } else {
1196 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
1197 }
1198 enableInCallOrientationEventListener(allowOrientationChange);
1199 }
1200
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001201 public void hideMainInCallFragment() {
linyuhc3968e62017-11-20 17:40:50 -08001202 LogUtil.enterBlock("InCallActivity.hideMainInCallFragment");
1203 if (getCallCardFragmentVisible()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001204 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1205 hideInCallScreenFragment(transaction);
1206 hideVideoCallScreenFragment(transaction);
1207 transaction.commitAllowingStateLoss();
1208 getSupportFragmentManager().executePendingTransactions();
1209 }
1210 }
1211
1212 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001213 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -08001214 // If the activity's onStart method hasn't been called yet then defer doing any work.
1215 if (!isVisible) {
1216 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001217 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001218 return;
1219 }
1220
1221 // Don't let this be reentrant.
1222 if (isInShowMainInCallFragment) {
1223 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001224 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001225 return;
1226 }
1227
1228 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001229 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
1230 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
wangqi219b8702018-02-13 09:34:41 -08001231 ShouldShowUiResult shouldShowRttUi = getShouldShowRttUi();
Eric Erfanianccca3152017-02-22 16:32:36 -08001232 LogUtil.i(
1233 "InCallActivity.showMainInCallFragment",
wangqi219b8702018-02-13 09:34:41 -08001234 "shouldShowAnswerUi: %b, shouldShowRttUi: %b, shouldShowVideoUi: %b "
1235 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowRttCallScreen: %b, "
1236 + "didShowVideoCallScreen: %b",
Eric Erfanianccca3152017-02-22 16:32:36 -08001237 shouldShowAnswerUi.shouldShow,
wangqi219b8702018-02-13 09:34:41 -08001238 shouldShowRttUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -07001239 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -08001240 didShowAnswerScreen,
1241 didShowInCallScreen,
wangqi219b8702018-02-13 09:34:41 -08001242 didShowRttCallScreen,
Eric Erfanianccca3152017-02-22 16:32:36 -08001243 didShowVideoCallScreen);
1244 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001245 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -08001246
1247 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
wangqi219b8702018-02-13 09:34:41 -08001248 boolean didChange;
Eric Erfanianccca3152017-02-22 16:32:36 -08001249 if (shouldShowAnswerUi.shouldShow) {
wangqi219b8702018-02-13 09:34:41 -08001250 didChange = hideInCallScreenFragment(transaction);
1251 didChange |= hideVideoCallScreenFragment(transaction);
1252 didChange |= hideRttCallScreenFragment(transaction);
1253 didChange |= showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001254 } else if (shouldShowVideoUi.shouldShow) {
wangqi219b8702018-02-13 09:34:41 -08001255 didChange = hideInCallScreenFragment(transaction);
1256 didChange |= showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
1257 didChange |= hideRttCallScreenFragment(transaction);
1258 didChange |= hideAnswerScreenFragment(transaction);
1259 } else if (shouldShowRttUi.shouldShow) {
1260 didChange = hideInCallScreenFragment(transaction);
1261 didChange |= hideVideoCallScreenFragment(transaction);
1262 didChange |= hideAnswerScreenFragment(transaction);
1263 didChange |= showRttCallScreenFragment(transaction, shouldShowRttUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001264 } else {
wangqi219b8702018-02-13 09:34:41 -08001265 didChange = showInCallScreenFragment(transaction);
1266 didChange |= hideVideoCallScreenFragment(transaction);
1267 didChange |= hideRttCallScreenFragment(transaction);
1268 didChange |= hideAnswerScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -08001269 }
1270
wangqi219b8702018-02-13 09:34:41 -08001271 if (didChange) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001272 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -08001273 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001274 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001275 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1276 }
1277 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001278 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001279 }
1280
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001281 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001282 DialerCall call = CallList.getInstance().getIncomingCall();
1283 if (call != null) {
1284 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001285 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001286 }
1287
1288 call = CallList.getInstance().getVideoUpgradeRequestCall();
1289 if (call != null) {
1290 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001291 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001292 }
1293
1294 // Check if we're showing the answer screen and the call is disconnected. If this condition is
1295 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
1296 // the user rejects an incoming call.
1297 call = CallList.getInstance().getFirstCall();
1298 if (call == null) {
1299 call = CallList.getInstance().getBackgroundCall();
1300 }
1301 if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
1302 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001303 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001304 }
1305
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001306 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001307 }
1308
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001309 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001310 DialerCall call = CallList.getInstance().getFirstCall();
1311 if (call == null) {
1312 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001313 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001314 }
1315
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001316 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001317 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001318 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001319 }
1320
linyuh8fbecce2017-12-18 13:53:09 -08001321 if (call.hasSentVideoUpgradeRequest() || call.hasReceivedVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001322 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001323 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001324 }
1325
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001326 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001327 }
1328
wangqi219b8702018-02-13 09:34:41 -08001329 private static ShouldShowUiResult getShouldShowRttUi() {
1330 DialerCall call = CallList.getInstance().getFirstCall();
1331 if (call == null) {
1332 LogUtil.i("InCallActivity.getShouldShowRttUi", "null call");
1333 return new ShouldShowUiResult(false, null);
1334 }
1335
1336 if (call.isRttCall()) {
1337 LogUtil.i("InCallActivity.getShouldShowRttUi", "found rtt call");
1338 return new ShouldShowUiResult(true, call);
1339 }
1340
1341 if (call.hasSentRttUpgradeRequest()) {
1342 LogUtil.i("InCallActivity.getShouldShowRttUi", "upgrading to rtt");
1343 return new ShouldShowUiResult(true, call);
1344 }
1345
1346 return new ShouldShowUiResult(false, null);
1347 }
1348
Eric Erfanianccca3152017-02-22 16:32:36 -08001349 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
1350 // When rejecting a call the active call can become null in which case we should continue
1351 // showing the answer screen.
1352 if (didShowAnswerScreen && call == null) {
1353 return false;
1354 }
1355
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001356 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
1357
1358 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -08001359
1360 // Check if we're already showing an answer screen for this call.
1361 if (didShowAnswerScreen) {
1362 AnswerScreen answerScreen = getAnswerScreen();
1363 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001364 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -07001365 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
1366 && !answerScreen.isActionTimeout()) {
1367 LogUtil.d(
1368 "InCallActivity.showAnswerScreenFragment",
1369 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -08001370 return false;
1371 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001372 if (answerScreen.isActionTimeout()) {
1373 LogUtil.i(
1374 "InCallActivity.showAnswerScreenFragment",
1375 "answer fragment exists but has been accepted/rejected and timed out");
1376 } else {
1377 LogUtil.i(
1378 "InCallActivity.showAnswerScreenFragment",
1379 "answer fragment exists but arguments do not match");
1380 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001381 hideAnswerScreenFragment(transaction);
1382 }
1383
1384 // Show a new answer screen.
1385 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -07001386 AnswerBindings.createAnswerScreen(
1387 call.getId(),
wangqi219b8702018-02-13 09:34:41 -08001388 call.isRttCall(),
Eric Erfanianfc37b022017-03-21 10:11:17 -07001389 call.isVideoCall(),
1390 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -07001391 call.getVideoTech().isSelfManagedCamera(),
1392 shouldAllowAnswerAndRelease(call),
1393 CallList.getInstance().getBackgroundCall() != null);
linyuhc3968e62017-11-20 17:40:50 -08001394 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), Tags.ANSWER_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001395
1396 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
1397 didShowAnswerScreen = true;
1398 return true;
1399 }
1400
Eric Erfanian90508232017-03-24 09:31:16 -07001401 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
1402 if (CallList.getInstance().getActiveCall() == null) {
1403 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
1404 return false;
1405 }
1406 if (getSystemService(TelephonyManager.class).getPhoneType()
1407 == TelephonyManager.PHONE_TYPE_CDMA) {
1408 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
1409 return false;
1410 }
1411 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
1412 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
1413 return false;
1414 }
linyuhc3968e62017-11-20 17:40:50 -08001415 if (!ConfigProviderBindings.get(this)
1416 .getBoolean(ConfigNames.ANSWER_AND_RELEASE_ENABLED, true)) {
Eric Erfanian90508232017-03-24 09:31:16 -07001417 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
1418 return false;
1419 }
1420
1421 return true;
1422 }
1423
Eric Erfanianccca3152017-02-22 16:32:36 -08001424 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
1425 if (!didShowAnswerScreen) {
1426 return false;
1427 }
1428 AnswerScreen answerScreen = getAnswerScreen();
1429 if (answerScreen != null) {
1430 transaction.remove(answerScreen.getAnswerScreenFragment());
1431 }
1432
1433 didShowAnswerScreen = false;
1434 return true;
1435 }
1436
1437 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
1438 if (didShowInCallScreen) {
1439 return false;
1440 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001441 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
linyuhc3968e62017-11-20 17:40:50 -08001442 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), Tags.IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001443 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1444 didShowInCallScreen = true;
1445 return true;
1446 }
1447
1448 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
1449 if (!didShowInCallScreen) {
1450 return false;
1451 }
1452 InCallScreen inCallScreen = getInCallScreen();
1453 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001454 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -08001455 }
1456 didShowInCallScreen = false;
1457 return true;
1458 }
1459
wangqi219b8702018-02-13 09:34:41 -08001460 private boolean showRttCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
1461 if (didShowRttCallScreen) {
1462 // This shouldn't happen since only one RTT call is allow at same time.
1463 if (!getRttCallScreen().getCallId().equals(call.getId())) {
1464 LogUtil.e("InCallActivity.showRttCallScreenFragment", "RTT call id doesn't match");
1465 }
1466 return false;
1467 }
1468 RttCallScreen rttCallScreen = RttBindings.createRttCallScreen(call.getId());
1469 transaction.add(R.id.main, rttCallScreen.getRttCallScreenFragment(), Tags.RTT_CALL_SCREEN);
1470 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1471 didShowRttCallScreen = true;
1472 return true;
1473 }
1474
1475 private boolean hideRttCallScreenFragment(FragmentTransaction transaction) {
1476 if (!didShowRttCallScreen) {
1477 return false;
1478 }
1479 RttCallScreen rttCallScreen = getRttCallScreen();
1480 if (rttCallScreen != null) {
1481 transaction.remove(rttCallScreen.getRttCallScreenFragment());
1482 }
1483 didShowRttCallScreen = false;
1484 return true;
1485 }
1486
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001487 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001488 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001489 VideoCallScreen videoCallScreen = getVideoCallScreen();
1490 if (videoCallScreen.getCallId().equals(call.getId())) {
1491 return false;
1492 }
1493 LogUtil.i(
1494 "InCallActivity.showVideoCallScreenFragment",
1495 "video call fragment exists but arguments do not match");
1496 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -08001497 }
1498
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001499 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
1500
Eric Erfanian90508232017-03-24 09:31:16 -07001501 VideoCallScreen videoCallScreen =
1502 VideoBindings.createVideoCallScreen(
1503 call.getId(), call.getVideoTech().shouldUseSurfaceView());
linyuhc3968e62017-11-20 17:40:50 -08001504 transaction.add(
1505 R.id.main, videoCallScreen.getVideoCallScreenFragment(), Tags.VIDEO_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001506
1507 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1508 didShowVideoCallScreen = true;
1509 return true;
1510 }
1511
1512 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
1513 if (!didShowVideoCallScreen) {
1514 return false;
1515 }
1516 VideoCallScreen videoCallScreen = getVideoCallScreen();
1517 if (videoCallScreen != null) {
1518 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
1519 }
1520 didShowVideoCallScreen = false;
1521 return true;
1522 }
1523
linyuhc3968e62017-11-20 17:40:50 -08001524 private AnswerScreen getAnswerScreen() {
1525 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(Tags.ANSWER_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001526 }
1527
linyuhc3968e62017-11-20 17:40:50 -08001528 private InCallScreen getInCallScreen() {
1529 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001530 }
1531
linyuhc3968e62017-11-20 17:40:50 -08001532 private VideoCallScreen getVideoCallScreen() {
1533 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.VIDEO_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001534 }
1535
wangqi219b8702018-02-13 09:34:41 -08001536 private RttCallScreen getRttCallScreen() {
1537 return (RttCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.RTT_CALL_SCREEN);
1538 }
1539
Eric Erfanianccca3152017-02-22 16:32:36 -08001540 @Override
1541 public void onPseudoScreenStateChanged(boolean isOn) {
1542 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
1543 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
1544 }
1545
1546 /**
1547 * For some touch related issue, turning off the screen can be faked by drawing a black view over
1548 * the activity. All touch events started when the screen is "off" is rejected.
1549 *
1550 * @see PseudoScreenState
1551 */
1552 @Override
1553 public boolean dispatchTouchEvent(MotionEvent event) {
1554 // Reject any gesture that started when the screen is in the fake off state.
1555 if (touchDownWhenPseudoScreenOff) {
1556 if (event.getAction() == MotionEvent.ACTION_UP) {
1557 touchDownWhenPseudoScreenOff = false;
1558 }
1559 return true;
1560 }
1561 // Reject all touch event when the screen is in the fake off state.
1562 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
1563 if (event.getAction() == MotionEvent.ACTION_DOWN) {
1564 touchDownWhenPseudoScreenOff = true;
1565 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
1566 }
1567 return true;
1568 }
1569 return super.dispatchTouchEvent(event);
1570 }
1571
wangqi219b8702018-02-13 09:34:41 -08001572 @Override
1573 public RttCallScreenDelegate newRttCallScreenDelegate(RttCallScreen videoCallScreen) {
1574 return new RttCallPresenter();
1575 }
1576
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001577 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -08001578 public final boolean shouldShow;
1579 public final DialerCall call;
1580
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001581 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001582 this.shouldShow = shouldShow;
1583 this.call = call;
1584 }
1585 }
linyuhc3968e62017-11-20 17:40:50 -08001586
1587 private static final class IntentExtraNames {
1588 static final String FOR_FULL_SCREEN = "InCallActivity.for_full_screen_intent";
1589 static final String NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
1590 static final String SHOW_DIALPAD = "InCallActivity.show_dialpad";
1591 }
1592
1593 private static final class KeysForSavedInstance {
1594 static final String DIALPAD_TEXT = "InCallActivity.dialpad_text";
1595 static final String DID_SHOW_ANSWER_SCREEN = "did_show_answer_screen";
1596 static final String DID_SHOW_IN_CALL_SCREEN = "did_show_in_call_screen";
1597 static final String DID_SHOW_VIDEO_CALL_SCREEN = "did_show_video_call_screen";
wangqi153af2f2018-02-15 16:21:49 -08001598 static final String DID_SHOW_RTT_CALL_SCREEN = "did_show_rtt_call_screen";
linyuhc3968e62017-11-20 17:40:50 -08001599 }
1600
1601 /** Request codes for pending intents. */
1602 public static final class PendingIntentRequestCodes {
1603 static final int NON_FULL_SCREEN = 0;
1604 static final int FULL_SCREEN = 1;
1605 static final int BUBBLE = 2;
1606 }
1607
1608 private static final class Tags {
1609 static final String ANSWER_SCREEN = "tag_answer_screen";
1610 static final String DIALPAD_FRAGMENT = "tag_dialpad_fragment";
1611 static final String IN_CALL_SCREEN = "tag_in_call_screen";
1612 static final String INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi";
1613 static final String SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment";
1614 static final String VIDEO_CALL_SCREEN = "tag_video_call_screen";
wangqi219b8702018-02-13 09:34:41 -08001615 static final String RTT_CALL_SCREEN = "tag_rtt_call_screen";
linyuhc3968e62017-11-20 17:40:50 -08001616 static final String POST_CHAR_DIALOG_FRAGMENT = "tag_post_char_dialog_fragment";
1617 }
1618
1619 private static final class ConfigNames {
1620 static final String ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
1621 }
1622
1623 private static final class InternationalCallOnWifiCallback
1624 implements InternationalCallOnWifiDialogFragment.Callback {
1625 private static final String TAG = InternationalCallOnWifiCallback.class.getCanonicalName();
1626
1627 @Override
1628 public void continueCall(@NonNull String callId) {
1629 LogUtil.i(TAG, "Continuing call with ID: %s", callId);
1630 }
1631
1632 @Override
1633 public void cancelCall(@NonNull String callId) {
1634 DialerCall call = CallList.getInstance().getCallById(callId);
1635 if (call == null) {
1636 LogUtil.i(TAG, "Call destroyed before the dialog is closed");
1637 return;
1638 }
1639
1640 LogUtil.i(TAG, "Disconnecting international call on WiFi");
1641 call.disconnect();
1642 }
1643 }
1644
1645 private static final class SelectPhoneAccountListener
1646 extends SelectPhoneAccountDialogFragment.SelectPhoneAccountListener {
1647 private static final String TAG = SelectPhoneAccountListener.class.getCanonicalName();
1648
1649 @Override
1650 public void onPhoneAccountSelected(
1651 PhoneAccountHandle selectedAccountHandle, boolean setDefault, String callId) {
1652 DialerCall call = CallList.getInstance().getCallById(callId);
1653 LogUtil.i(TAG, "Phone account select with call:\n%s", call);
1654
1655 if (call != null) {
1656 call.phoneAccountSelected(selectedAccountHandle, setDefault);
1657 }
1658 }
1659
1660 @Override
1661 public void onDialogDismissed(String callId) {
1662 DialerCall call = CallList.getInstance().getCallById(callId);
1663 LogUtil.i(TAG, "Disconnecting call:\n%s" + call);
1664
1665 if (call != null) {
1666 call.disconnect();
1667 }
1668 }
1669 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001670}