blob: 47b5986a0c13673edbc971807259613233e74ece [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;
63import com.android.dialer.logging.DialerImpression;
Eric Erfanianccca3152017-02-22 16:32:36 -080064import com.android.dialer.logging.Logger;
weijiaxu94df7202017-10-25 18:21:41 -070065import com.android.dialer.logging.LoggingBindings;
Eric Erfanian8369df02017-05-03 10:27:13 -070066import com.android.dialer.logging.ScreenEvent;
linyuhc3968e62017-11-20 17:40:50 -080067import com.android.dialer.util.ViewUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080068import com.android.incallui.answer.bindings.AnswerBindings;
69import com.android.incallui.answer.protocol.AnswerScreen;
70import com.android.incallui.answer.protocol.AnswerScreenDelegate;
71import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
72import com.android.incallui.answerproximitysensor.PseudoScreenState;
linyuh57b093b2017-11-17 14:32:32 -080073import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080074import com.android.incallui.call.CallList;
75import com.android.incallui.call.DialerCall;
76import com.android.incallui.call.DialerCall.State;
linyuh57b093b2017-11-17 14:32:32 -080077import com.android.incallui.call.TelecomAdapter;
Eric Erfanian2ca43182017-08-31 06:57:16 -070078import com.android.incallui.callpending.CallPendingActivity;
79import com.android.incallui.disconnectdialog.DisconnectMessage;
Eric Erfanianccca3152017-02-22 16:32:36 -080080import com.android.incallui.incall.bindings.InCallBindings;
81import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
82import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
83import com.android.incallui.incall.protocol.InCallScreen;
84import com.android.incallui.incall.protocol.InCallScreenDelegate;
85import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
linyuh7b86f562017-11-16 11:24:09 -080086import com.android.incallui.incalluilock.InCallUiLock;
linyuhf99f6302017-11-15 11:23:51 -080087import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -080088import com.android.incallui.video.bindings.VideoBindings;
89import com.android.incallui.video.protocol.VideoCallScreen;
90import com.android.incallui.video.protocol.VideoCallScreenDelegate;
91import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
linyuhc3968e62017-11-20 17:40:50 -080092import com.google.common.base.Optional;
93import java.lang.annotation.Retention;
94import java.lang.annotation.RetentionPolicy;
95import java.util.ArrayList;
96import java.util.List;
Eric Erfanianccca3152017-02-22 16:32:36 -080097
98/** Version of {@link InCallActivity} that shows the new UI */
99public class InCallActivity extends TransactionSafeFragmentActivity
100 implements AnswerScreenDelegateFactory,
101 InCallScreenDelegateFactory,
102 InCallButtonUiDelegateFactory,
103 VideoCallScreenDelegateFactory,
104 PseudoScreenState.StateChangedListener {
105
linyuhc3968e62017-11-20 17:40:50 -0800106 @Retention(RetentionPolicy.SOURCE)
107 @IntDef({
108 DIALPAD_REQUEST_NONE,
109 DIALPAD_REQUEST_SHOW,
110 DIALPAD_REQUEST_HIDE,
111 })
112 @interface DialpadRequestType {}
Eric Erfanian2ca43182017-08-31 06:57:16 -0700113
linyuhc3968e62017-11-20 17:40:50 -0800114 private static final int DIALPAD_REQUEST_NONE = 1;
115 private static final int DIALPAD_REQUEST_SHOW = 2;
116 private static final int DIALPAD_REQUEST_HIDE = 3;
linyuh57b093b2017-11-17 14:32:32 -0800117
linyuhc3968e62017-11-20 17:40:50 -0800118 private static Optional<Integer> audioRouteForTesting = Optional.absent();
linyuh57b093b2017-11-17 14:32:32 -0800119
linyuhc3968e62017-11-20 17:40:50 -0800120 private final InternationalCallOnWifiCallback internationalCallOnWifiCallback =
121 new InternationalCallOnWifiCallback();
122 private final SelectPhoneAccountListener selectPhoneAccountListener =
123 new SelectPhoneAccountListener();
Eric Erfanianccca3152017-02-22 16:32:36 -0800124
linyuhc3968e62017-11-20 17:40:50 -0800125 private Animation dialpadSlideInAnimation;
126 private Animation dialpadSlideOutAnimation;
127 private Dialog errorDialog;
128 private GradientDrawable backgroundDrawable;
linyuh69a25062017-11-15 16:18:51 -0800129 private InCallOrientationEventListener inCallOrientationEventListener;
linyuhc3968e62017-11-20 17:40:50 -0800130 private View pseudoBlackScreenOverlay;
131 private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment;
132 private String dtmfTextToPrepopulate;
133 private String showPostCharWaitDialogCallId;
134 private String showPostCharWaitDialogChars;
135 private boolean allowOrientationChange;
136 private boolean animateDialpadOnShow;
Eric Erfanianccca3152017-02-22 16:32:36 -0800137 private boolean didShowAnswerScreen;
138 private boolean didShowInCallScreen;
139 private boolean didShowVideoCallScreen;
linyuh9c327da2017-11-14 12:33:48 -0800140 private boolean dismissKeyguard;
Eric Erfanianccca3152017-02-22 16:32:36 -0800141 private boolean isInShowMainInCallFragment;
linyuhc3968e62017-11-20 17:40:50 -0800142 private boolean isRecreating; // whether the activity is going to be recreated
143 private boolean isVisible;
Eric Erfanianccca3152017-02-22 16:32:36 -0800144 private boolean needDismissPendingDialogs;
linyuhc3968e62017-11-20 17:40:50 -0800145 private boolean showPostCharWaitDialogOnResume;
146 private boolean touchDownWhenPseudoScreenOff;
147 private int[] backgroundDrawableColors;
148 @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
Eric Erfanianccca3152017-02-22 16:32:36 -0800149
150 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700151 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800152 Intent intent = new Intent(Intent.ACTION_MAIN, null);
153 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
154 intent.setClass(context, InCallActivity.class);
linyuhc3968e62017-11-20 17:40:50 -0800155 if (showDialpad) {
156 intent.putExtra(IntentExtraNames.SHOW_DIALPAD, true);
157 }
158 intent.putExtra(IntentExtraNames.NEW_OUTGOING_CALL, newOutgoingCall);
159 intent.putExtra(IntentExtraNames.FOR_FULL_SCREEN, isForFullScreen);
Eric Erfanianccca3152017-02-22 16:32:36 -0800160 return intent;
161 }
162
163 @Override
164 protected void onResumeFragments() {
165 super.onResumeFragments();
166 if (needDismissPendingDialogs) {
167 dismissPendingDialogs();
168 }
169 }
170
171 @Override
linyuhc3968e62017-11-20 17:40:50 -0800172 protected void onCreate(Bundle bundle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700173 Trace.beginSection("InCallActivity.onCreate");
linyuhc3968e62017-11-20 17:40:50 -0800174 super.onCreate(bundle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800175
Eric Erfanian2ca43182017-08-31 06:57:16 -0700176 if (getIntent().getBooleanExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY, false)) {
177 Logger.get(this).logImpression(DialerImpression.Type.BUBBLE_PRIMARY_BUTTON_RETURN_TO_CALL);
178 getIntent().removeExtra(ReturnToCallController.RETURN_TO_CALL_EXTRA_KEY);
179 }
180
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);
Eric Erfanianccca3152017-02-22 16:32:36 -0800185 }
186
linyuhc3968e62017-11-20 17:40:50 -0800187 setWindowFlags();
188 setContentView(R.layout.incall_screen);
189 internalResolveIntent(getIntent());
190
191 boolean isLandscape =
192 getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
193 boolean isRtl = ViewUtil.isRtl();
194 if (isLandscape) {
195 dialpadSlideInAnimation =
196 AnimationUtils.loadAnimation(
197 this, isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
198 dialpadSlideOutAnimation =
199 AnimationUtils.loadAnimation(
200 this, isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
201 } else {
202 dialpadSlideInAnimation = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
203 dialpadSlideOutAnimation =
204 AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
205 }
206 dialpadSlideInAnimation.setInterpolator(AnimUtils.EASE_IN);
207 dialpadSlideOutAnimation.setInterpolator(AnimUtils.EASE_OUT);
208 dialpadSlideOutAnimation.setAnimationListener(
209 new AnimationListenerAdapter() {
210 @Override
211 public void onAnimationEnd(Animation animation) {
212 hideDialpadFragment();
213 }
214 });
215
216 if (bundle != null && showDialpadRequest == DIALPAD_REQUEST_NONE) {
217 // If the dialpad was shown before, set related variables so that it can be shown and
218 // populated with the previous DTMF text during onResume().
219 if (bundle.containsKey(IntentExtraNames.SHOW_DIALPAD)) {
220 boolean showDialpad = bundle.getBoolean(IntentExtraNames.SHOW_DIALPAD);
221 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
222 animateDialpadOnShow = false;
223 }
224 dtmfTextToPrepopulate = bundle.getString(KeysForSavedInstance.DIALPAD_TEXT);
225
226 SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment =
227 (SelectPhoneAccountDialogFragment)
228 getFragmentManager().findFragmentByTag(Tags.SELECT_ACCOUNT_FRAGMENT);
229 if (selectPhoneAccountDialogFragment != null) {
230 selectPhoneAccountDialogFragment.setListener(selectPhoneAccountListener);
231 }
232 }
233
234 InternationalCallOnWifiDialogFragment existingInternationalCallOnWifiDialogFragment =
235 (InternationalCallOnWifiDialogFragment)
236 getSupportFragmentManager().findFragmentByTag(Tags.INTERNATIONAL_CALL_ON_WIFI);
237 if (existingInternationalCallOnWifiDialogFragment != null) {
238 existingInternationalCallOnWifiDialogFragment.setCallback(internationalCallOnWifiCallback);
239 }
240
linyuh69a25062017-11-15 16:18:51 -0800241 inCallOrientationEventListener = new InCallOrientationEventListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800242
243 getWindow()
244 .getDecorView()
245 .setSystemUiVisibility(
246 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
247
248 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700249 sendBroadcast(CallPendingActivity.getFinishBroadcast());
250 Trace.endSection();
weijiaxuc950a9b2017-11-06 16:39:04 -0800251 Logger.get(this)
252 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
253 Logger.get(this)
254 .logStopLatencyTimer(LoggingBindings.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
Eric Erfanianccca3152017-02-22 16:32:36 -0800255 }
256
linyuhc3968e62017-11-20 17:40:50 -0800257 private void setWindowFlags() {
258 // Allow the activity to be shown when the screen is locked and filter out touch events that are
259 // "too fat".
260 int flags =
261 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
262 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
263
264 // When the audio stream is not directed through Bluetooth, turn the screen on once the
265 // activity is shown.
266 final int audioRoute = getAudioRoute();
267 if (audioRoute != CallAudioState.ROUTE_BLUETOOTH) {
268 flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
269 }
270
271 getWindow().addFlags(flags);
272 }
273
274 private static int getAudioRoute() {
275 if (audioRouteForTesting.isPresent()) {
276 return audioRouteForTesting.get();
277 }
278
279 return AudioModeProvider.getInstance().getAudioState().getRoute();
280 }
281
282 @VisibleForTesting(otherwise = VisibleForTesting.NONE)
283 public static void setAudioRouteForTesting(int audioRoute) {
284 audioRouteForTesting = Optional.of(audioRoute);
285 }
286
287 private void internalResolveIntent(Intent intent) {
288 if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
289 return;
290 }
291
292 if (intent.hasExtra(IntentExtraNames.SHOW_DIALPAD)) {
293 // IntentExtraNames.SHOW_DIALPAD can be used to specify whether the DTMF dialpad should be
294 // initially visible. If the extra is absent, leave the dialpad in its previous state.
295 boolean showDialpad = intent.getBooleanExtra(IntentExtraNames.SHOW_DIALPAD, false);
296 relaunchedFromDialer(showDialpad);
297 }
298
299 DialerCall outgoingCall = CallList.getInstance().getOutgoingCall();
300 if (outgoingCall == null) {
301 outgoingCall = CallList.getInstance().getPendingOutgoingCall();
302 }
303 if (intent.getBooleanExtra(IntentExtraNames.NEW_OUTGOING_CALL, false)) {
304 intent.removeExtra(IntentExtraNames.NEW_OUTGOING_CALL);
305
306 // InCallActivity is responsible for disconnecting a new outgoing call if there is no way of
307 // making it (i.e. no valid call capable accounts).
308 // If the version is not MSIM compatible, ignore this code.
309 if (CompatUtils.isMSIMCompatible()
310 && InCallPresenter.isCallWithNoValidAccounts(outgoingCall)) {
311 LogUtil.i(
312 "InCallActivity.internalResolveIntent", "Call with no valid accounts, disconnecting");
313 outgoingCall.disconnect();
314 }
315
316 dismissKeyguard(true);
317 }
318
319 if (showPhoneAccountSelectionDialog()) {
320 hideMainInCallFragment();
321 }
322 }
323
324 /**
325 * When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad should
326 * be shown on launch.
327 *
328 * @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and {@code
329 * false} to indicate no change should be made to the dialpad visibility.
330 */
331 private void relaunchedFromDialer(boolean showDialpad) {
332 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
333 animateDialpadOnShow = true;
334
335 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
336 // If there's only one line in use, AND it's on hold, then we're sure the user
337 // wants to use the dialpad toward the exact line, so un-hold the holding line.
338 DialerCall call = CallList.getInstance().getActiveOrBackgroundCall();
339 if (call != null && call.getState() == State.ONHOLD) {
340 call.unhold();
341 }
342 }
343 }
344
345 /**
346 * Show a phone account selection dialog if there is a call waiting for phone account selection.
347 *
348 * @return true if the dialog was shown.
349 */
350 private boolean showPhoneAccountSelectionDialog() {
351 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
352 if (waitingForAccountCall == null) {
353 return false;
354 }
355
356 Bundle extras = waitingForAccountCall.getIntentExtras();
357 List<PhoneAccountHandle> phoneAccountHandles =
358 extras == null
359 ? new ArrayList<>()
360 : extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
361
362 selectPhoneAccountDialogFragment =
363 SelectPhoneAccountDialogFragment.newInstance(
364 R.string.select_phone_account_for_calls,
365 true /* canSetDefault */,
366 0 /* setDefaultResId */,
367 phoneAccountHandles,
368 selectPhoneAccountListener,
369 waitingForAccountCall.getId(),
370 null /* hints */);
371 selectPhoneAccountDialogFragment.show(getFragmentManager(), Tags.SELECT_ACCOUNT_FRAGMENT);
372 return true;
373 }
374
Eric Erfanianccca3152017-02-22 16:32:36 -0800375 @Override
376 protected void onSaveInstanceState(Bundle out) {
linyuh57b093b2017-11-17 14:32:32 -0800377 LogUtil.enterBlock("InCallActivity.onSaveInstanceState");
378
379 // TODO: DialpadFragment should handle this as part of its own state
linyuhc3968e62017-11-20 17:40:50 -0800380 out.putBoolean(IntentExtraNames.SHOW_DIALPAD, isDialpadVisible());
linyuh57b093b2017-11-17 14:32:32 -0800381 DialpadFragment dialpadFragment = getDialpadFragment();
382 if (dialpadFragment != null) {
linyuhc3968e62017-11-20 17:40:50 -0800383 out.putString(KeysForSavedInstance.DIALPAD_TEXT, dialpadFragment.getDtmfText());
linyuh57b093b2017-11-17 14:32:32 -0800384 }
385
linyuhc3968e62017-11-20 17:40:50 -0800386 out.putBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN, didShowAnswerScreen);
387 out.putBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN, didShowInCallScreen);
388 out.putBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN, didShowVideoCallScreen);
linyuh57b093b2017-11-17 14:32:32 -0800389
Eric Erfanianccca3152017-02-22 16:32:36 -0800390 super.onSaveInstanceState(out);
391 isVisible = false;
392 }
393
394 @Override
395 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700396 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800397 super.onStart();
linyuh57b093b2017-11-17 14:32:32 -0800398
Eric Erfanianccca3152017-02-22 16:32:36 -0800399 isVisible = true;
400 showMainInCallFragment();
linyuh57b093b2017-11-17 14:32:32 -0800401
402 InCallPresenter.getInstance().setActivity(this);
403 enableInCallOrientationEventListener(
404 getRequestedOrientation()
405 == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
406 InCallPresenter.getInstance().onActivityStarted();
407
Eric Erfanianccca3152017-02-22 16:32:36 -0800408 if (ActivityCompat.isInMultiWindowMode(this)
409 && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
410 // Hide the dialpad because there may not be enough room
411 showDialpadFragment(false, false);
412 }
linyuh57b093b2017-11-17 14:32:32 -0800413
Eric Erfanian2ca43182017-08-31 06:57:16 -0700414 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800415 }
416
417 @Override
418 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700419 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800420 super.onResume();
linyuhc3968e62017-11-20 17:40:50 -0800421
422 if (!InCallPresenter.getInstance().isReadyForTearDown()) {
423 updateTaskDescription();
424 InCallPresenter.getInstance().onUiShowing(true);
425 }
426
427 // If there is a pending request to show or hide the dialpad, handle that now.
428 if (showDialpadRequest != DIALPAD_REQUEST_NONE) {
429 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
430 // Exit fullscreen so that the user has access to the dialpad hide/show button.
431 // This is important when showing the dialpad from within dialer.
432 InCallPresenter.getInstance().setFullScreen(false /* isFullScreen */, true /* force */);
433
434 showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
435 animateDialpadOnShow = false;
436
437 DialpadFragment dialpadFragment = getDialpadFragment();
438 if (dialpadFragment != null) {
439 dialpadFragment.setDtmfText(dtmfTextToPrepopulate);
440 dtmfTextToPrepopulate = null;
441 }
442 } else {
443 LogUtil.i("InCallActivity.onResume", "Force-hide the dialpad");
444 if (getDialpadFragment() != null) {
445 showDialpadFragment(false /* show */, false /* animate */);
446 }
447 }
448 showDialpadRequest = DIALPAD_REQUEST_NONE;
449 }
450 updateNavigationBar(isDialpadVisible());
451
452 if (showPostCharWaitDialogOnResume) {
453 showDialogForPostCharWait(showPostCharWaitDialogCallId, showPostCharWaitDialogChars);
454 }
455
456 CallList.getInstance()
457 .onInCallUiShown(getIntent().getBooleanExtra(IntentExtraNames.FOR_FULL_SCREEN, false));
458
Eric Erfanianccca3152017-02-22 16:32:36 -0800459 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
460 pseudoScreenState.addListener(this);
461 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700462 Trace.endSection();
weijiaxu650e7cc2017-10-31 12:38:54 -0700463 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
464 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800465 () ->
466 Logger.get(this)
467 .logRecordMemory(LoggingBindings.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700468 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800469 }
470
Eric Erfanianccca3152017-02-22 16:32:36 -0800471 @Override
472 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700473 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800474 super.onPause();
linyuh57b093b2017-11-17 14:32:32 -0800475
476 DialpadFragment dialpadFragment = getDialpadFragment();
477 if (dialpadFragment != null) {
478 dialpadFragment.onDialerKeyUp(null);
479 }
480
481 InCallPresenter.getInstance().onUiShowing(false);
482 if (isFinishing()) {
483 InCallPresenter.getInstance().unsetActivity(this);
484 }
485
Eric Erfanianccca3152017-02-22 16:32:36 -0800486 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700487 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800488 }
489
490 @Override
491 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700492 Trace.beginSection("InCallActivity.onStop");
wangqi4d705e52017-09-28 12:23:35 -0700493 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800494 super.onStop();
linyuh57b093b2017-11-17 14:32:32 -0800495
496 // Disconnects the call waiting for a phone account when the activity is hidden (e.g., after the
497 // user presses the home button).
498 // Without this the pending call will get stuck on phone account selection and new calls can't
499 // be created.
500 // Skip this when the screen is locked since the activity may complete its current life cycle
501 // and restart.
linyuhc3968e62017-11-20 17:40:50 -0800502 if (!isRecreating && !getSystemService(KeyguardManager.class).isKeyguardLocked()) {
linyuh57b093b2017-11-17 14:32:32 -0800503 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
504 if (waitingForAccountCall != null) {
505 waitingForAccountCall.disconnect();
506 }
507 }
508
509 enableInCallOrientationEventListener(false);
510 InCallPresenter.getInstance().updateIsChangingConfigurations();
511 InCallPresenter.getInstance().onActivityStopped();
linyuhc3968e62017-11-20 17:40:50 -0800512 if (!isRecreating) {
linyuh57b093b2017-11-17 14:32:32 -0800513 if (errorDialog != null) {
514 errorDialog.dismiss();
515 }
516 }
517
Eric Erfanian2ca43182017-08-31 06:57:16 -0700518 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800519 }
520
521 @Override
522 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700523 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800524 super.onDestroy();
linyuh57b093b2017-11-17 14:32:32 -0800525
526 InCallPresenter.getInstance().unsetActivity(this);
527 InCallPresenter.getInstance().updateIsChangingConfigurations();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700528 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800529 }
530
531 @Override
532 public void finish() {
533 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700534 // When user select incall ui from recents after the call is disconnected, it tries to launch
535 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
536 // crash.
537 // By calling finishAndRemoveTask() instead of finish() the task associated with
538 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
539 // this case.
540 //
541 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
542 // clear the task since there could be parent activity in the same task that's still alive.
543 // But InCallActivity is special since it's singleInstance which means it's root activity and
544 // only instance of activity in the task. So it should be safe to also remove task when
545 // finishing.
546 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
547 // finishes, the task should also be removed since it doesn't make sense to go back to it in
548 // anyway anymore.
549 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800550 }
551 }
552
553 private boolean shouldCloseActivityOnFinish() {
linyuhc3968e62017-11-20 17:40:50 -0800554 if (!isVisible) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800555 LogUtil.i(
556 "InCallActivity.shouldCloseActivityOnFinish",
557 "allowing activity to be closed because it's not visible");
558 return true;
559 }
560
twyen8efb4952017-10-06 16:35:54 -0700561 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800562 LogUtil.i(
563 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700564 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800565 return false;
566 }
567
568 LogUtil.i(
569 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700570 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800571 return true;
572 }
573
574 @Override
575 protected void onNewIntent(Intent intent) {
linyuhc3968e62017-11-20 17:40:50 -0800576 LogUtil.enterBlock("InCallActivity.onNewIntent");
Eric Erfanianccca3152017-02-22 16:32:36 -0800577
578 // If the screen is off, we need to make sure it gets turned on for incoming calls.
579 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
580 // when the activity is first created. Therefore, to ensure the screen is turned on
581 // for the call waiting case, we recreate() the current activity. There should be no jank from
582 // this since the screen is already off and will remain so until our new activity is up.
linyuhc3968e62017-11-20 17:40:50 -0800583 if (!isVisible) {
584 onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800585 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
586 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700587 } else {
linyuhc3968e62017-11-20 17:40:50 -0800588 onNewIntent(intent, false /* isRecreating */);
589 }
590 }
591
592 private void onNewIntent(Intent intent, boolean isRecreating) {
593 this.isRecreating = isRecreating;
594
595 // We're being re-launched with a new Intent. Since it's possible for a single InCallActivity
596 // instance to persist indefinitely (even if we finish() ourselves), this sequence can
597 // happen any time the InCallActivity needs to be displayed.
598
599 // Stash away the new intent so that we can get it in the future by calling getIntent().
600 // Otherwise getIntent() will return the original Intent from when we first got created.
601 setIntent(intent);
602
603 // Activities are always paused before receiving a new intent, so we can count on our onResume()
604 // method being called next.
605
606 // Just like in onCreate(), handle the intent.
607 // Skip if InCallActivity is going to be recreated since this will be called in onCreate().
608 if (!isRecreating) {
609 internalResolveIntent(intent);
Eric Erfanianccca3152017-02-22 16:32:36 -0800610 }
611 }
612
613 @Override
614 public void onBackPressed() {
linyuh57b093b2017-11-17 14:32:32 -0800615 LogUtil.enterBlock("InCallActivity.onBackPressed");
616
linyuhc3968e62017-11-20 17:40:50 -0800617 if (!isVisible) {
linyuh57b093b2017-11-17 14:32:32 -0800618 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800619 }
linyuh57b093b2017-11-17 14:32:32 -0800620
621 if (!getCallCardFragmentVisible()) {
622 return;
623 }
624
625 DialpadFragment dialpadFragment = getDialpadFragment();
626 if (dialpadFragment != null && dialpadFragment.isVisible()) {
627 showDialpadFragment(false /* show */, true /* animate */);
628 return;
629 }
630
631 if (CallList.getInstance().getIncomingCall() != null) {
632 LogUtil.i(
633 "InCallActivity.onBackPressed",
634 "Ignore the press of the back key when an incoming call is ringing");
635 return;
636 }
637
638 // Nothing special to do. Fall back to the default behavior.
639 super.onBackPressed();
Eric Erfanianccca3152017-02-22 16:32:36 -0800640 }
641
642 @Override
643 public boolean onOptionsItemSelected(MenuItem item) {
644 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
645 if (item.getItemId() == android.R.id.home) {
646 onBackPressed();
647 return true;
648 }
649 return super.onOptionsItemSelected(item);
650 }
651
652 @Override
653 public boolean onKeyUp(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800654 DialpadFragment dialpadFragment = getDialpadFragment();
655 if (dialpadFragment != null
656 && dialpadFragment.isVisible()
657 && dialpadFragment.onDialerKeyUp(event)) {
658 return true;
659 }
660
661 if (keyCode == KeyEvent.KEYCODE_CALL) {
662 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
663 return true;
664 }
665
666 return super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800667 }
668
669 @Override
670 public boolean onKeyDown(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800671 switch (keyCode) {
672 case KeyEvent.KEYCODE_CALL:
673 if (!InCallPresenter.getInstance().handleCallKey()) {
674 LogUtil.e(
675 "InCallActivity.onKeyDown",
676 "InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
677 }
678 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
679 return true;
680
681 // Note that KEYCODE_ENDCALL isn't handled here as the standard system-wide handling of it
682 // is exactly what's needed, namely
683 // (1) "hang up" if there's an active call, or
684 // (2) "don't answer" if there's an incoming call.
685 // (See PhoneWindowManager for implementation details.)
686
687 case KeyEvent.KEYCODE_CAMERA:
688 // Consume KEYCODE_CAMERA since it's easy to accidentally press the camera button.
689 return true;
690
691 case KeyEvent.KEYCODE_VOLUME_UP:
692 case KeyEvent.KEYCODE_VOLUME_DOWN:
693 case KeyEvent.KEYCODE_VOLUME_MUTE:
694 // Ringer silencing handled by PhoneWindowManager.
695 break;
696
697 case KeyEvent.KEYCODE_MUTE:
698 TelecomAdapter.getInstance()
699 .mute(!AudioModeProvider.getInstance().getAudioState().isMuted());
700 return true;
701
702 case KeyEvent.KEYCODE_SLASH:
703 // When verbose logging is enabled, dump the view for debugging/testing purposes.
704 if (LogUtil.isVerboseEnabled()) {
705 View decorView = getWindow().getDecorView();
706 LogUtil.v("InCallActivity.onKeyDown", "View dump:\n%s", decorView);
707 return true;
708 }
709 break;
710
711 case KeyEvent.KEYCODE_EQUALS:
712 break;
713
714 default: // fall out
715 }
716
717 // Pass other key events to DialpadFragment's "onDialerKeyDown" method in case the user types
718 // in DTMF (Dual-tone multi-frequency signaling) code.
719 DialpadFragment dialpadFragment = getDialpadFragment();
720 if (dialpadFragment != null
721 && dialpadFragment.isVisible()
722 && dialpadFragment.onDialerKeyDown(event)) {
723 return true;
724 }
725
726 return super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800727 }
728
729 public boolean isInCallScreenAnimating() {
730 return false;
731 }
732
733 public void showConferenceFragment(boolean show) {
734 if (show) {
735 startActivity(new Intent(this, ManageConferenceActivity.class));
736 }
737 }
738
linyuhc3968e62017-11-20 17:40:50 -0800739 public void showDialpadFragment(boolean show, boolean animate) {
740 if (show == isDialpadVisible()) {
741 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800742 }
linyuhc3968e62017-11-20 17:40:50 -0800743
744 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
745 if (dialpadFragmentManager == null) {
746 LogUtil.i("InCallActivity.showDialpadFragment", "Unable to obtain a FragmentManager");
747 return;
748 }
749
750 if (!animate) {
751 if (show) {
752 showDialpadFragment();
753 } else {
754 hideDialpadFragment();
755 }
756 } else {
757 if (show) {
758 showDialpadFragment();
759 getDialpadFragment().animateShowDialpad();
760 }
761 getDialpadFragment()
762 .getView()
763 .startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
764 }
765
766 ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
767 if (sensor != null) {
768 sensor.onDialpadVisible(show);
769 }
770 showDialpadRequest = DIALPAD_REQUEST_NONE;
771
772 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
773 // repositions itself.
774 getInCallScreen().onInCallScreenDialpadVisibilityChange(show);
775 }
776
777 private void showDialpadFragment() {
778 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
779 if (dialpadFragmentManager == null) {
780 return;
781 }
782
783 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
784 DialpadFragment dialpadFragment = getDialpadFragment();
785 if (dialpadFragment == null) {
786 transaction.add(getDialpadContainerId(), new DialpadFragment(), Tags.DIALPAD_FRAGMENT);
787 } else {
788 transaction.show(dialpadFragment);
calderwoodrad5883872017-12-12 15:29:12 -0800789 dialpadFragment.setUserVisibleHint(true);
linyuhc3968e62017-11-20 17:40:50 -0800790 }
791 transaction.commitAllowingStateLoss();
792 dialpadFragmentManager.executePendingTransactions();
793
794 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, this);
795 updateNavigationBar(true /* isDialpadVisible */);
796 }
797
798 private void hideDialpadFragment() {
799 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
800 if (dialpadFragmentManager == null) {
801 return;
802 }
803
calderwoodrad5883872017-12-12 15:29:12 -0800804 DialpadFragment dialpadFragment = getDialpadFragment();
linyuhc3968e62017-11-20 17:40:50 -0800805 if (dialpadFragment != null) {
806 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
807 transaction.hide(dialpadFragment);
808 transaction.commitAllowingStateLoss();
809 dialpadFragmentManager.executePendingTransactions();
calderwoodrad5883872017-12-12 15:29:12 -0800810 dialpadFragment.setUserVisibleHint(false);
linyuhc3968e62017-11-20 17:40:50 -0800811 }
812 updateNavigationBar(false /* isDialpadVisible */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800813 }
814
815 public boolean isDialpadVisible() {
linyuh69a25062017-11-15 16:18:51 -0800816 DialpadFragment dialpadFragment = getDialpadFragment();
calderwoodrad5883872017-12-12 15:29:12 -0800817 return dialpadFragment != null && dialpadFragment.getUserVisibleHint();
linyuh69a25062017-11-15 16:18:51 -0800818 }
819
linyuhc3968e62017-11-20 17:40:50 -0800820 /** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
linyuh69a25062017-11-15 16:18:51 -0800821 @Nullable
linyuhc3968e62017-11-20 17:40:50 -0800822 private DialpadFragment getDialpadFragment() {
linyuh69a25062017-11-15 16:18:51 -0800823 FragmentManager fragmentManager = getDialpadFragmentManager();
824 if (fragmentManager == null) {
825 return null;
826 }
linyuhc3968e62017-11-20 17:40:50 -0800827 return (DialpadFragment) fragmentManager.findFragmentByTag(Tags.DIALPAD_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800828 }
829
830 public void onForegroundCallChanged(DialerCall newForegroundCall) {
linyuh57b093b2017-11-17 14:32:32 -0800831 updateTaskDescription();
832
833 if (newForegroundCall == null || !didShowAnswerScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800834 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
linyuh57b093b2017-11-17 14:32:32 -0800835 updateWindowBackgroundColor(0 /* progress */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800836 }
837 }
838
linyuhc3968e62017-11-20 17:40:50 -0800839 private void updateTaskDescription() {
linyuh57b093b2017-11-17 14:32:32 -0800840 int color =
841 getResources().getBoolean(R.bool.is_layout_landscape)
842 ? ResourcesCompat.getColor(
843 getResources(), R.color.statusbar_background_color, getTheme())
844 : InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
845 setTaskDescription(
846 new TaskDescription(
847 getResources().getString(R.string.notification_ongoing_call), null /* icon */, color));
848 }
849
Eric Erfanianccca3152017-02-22 16:32:36 -0800850 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
851 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
852 @ColorInt int top;
853 @ColorInt int middle;
854 @ColorInt int bottom;
855 @ColorInt int gray = 0x66000000;
856
857 if (ActivityCompat.isInMultiWindowMode(this)) {
858 top = themeColorManager.getBackgroundColorSolid();
859 middle = themeColorManager.getBackgroundColorSolid();
860 bottom = themeColorManager.getBackgroundColorSolid();
861 } else {
862 top = themeColorManager.getBackgroundColorTop();
863 middle = themeColorManager.getBackgroundColorMiddle();
864 bottom = themeColorManager.getBackgroundColorBottom();
865 }
866
867 if (progress < 0) {
868 float correctedProgress = Math.abs(progress);
869 top = ColorUtils.blendARGB(top, gray, correctedProgress);
870 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
871 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
872 }
873
874 boolean backgroundDirty = false;
875 if (backgroundDrawable == null) {
876 backgroundDrawableColors = new int[] {top, middle, bottom};
877 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
878 backgroundDirty = true;
879 } else {
880 if (backgroundDrawableColors[0] != top) {
881 backgroundDrawableColors[0] = top;
882 backgroundDirty = true;
883 }
884 if (backgroundDrawableColors[1] != middle) {
885 backgroundDrawableColors[1] = middle;
886 backgroundDirty = true;
887 }
888 if (backgroundDrawableColors[2] != bottom) {
889 backgroundDrawableColors[2] = bottom;
890 backgroundDirty = true;
891 }
892 if (backgroundDirty) {
893 backgroundDrawable.setColors(backgroundDrawableColors);
894 }
895 }
896
897 if (backgroundDirty) {
898 getWindow().setBackgroundDrawable(backgroundDrawable);
899 }
900 }
901
902 public boolean isVisible() {
903 return isVisible;
904 }
905
906 public boolean getCallCardFragmentVisible() {
907 return didShowInCallScreen || didShowVideoCallScreen;
908 }
909
910 public void dismissKeyguard(boolean dismiss) {
linyuh9c327da2017-11-14 12:33:48 -0800911 if (dismissKeyguard == dismiss) {
912 return;
913 }
914
915 dismissKeyguard = dismiss;
916 if (dismiss) {
917 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
918 } else {
919 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
920 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800921 }
922
linyuhc3968e62017-11-20 17:40:50 -0800923 public void showDialogForPostCharWait(String callId, String chars) {
924 if (isVisible) {
925 PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
926 fragment.show(getSupportFragmentManager(), Tags.POST_CHAR_DIALOG_FRAGMENT);
927
928 showPostCharWaitDialogOnResume = false;
929 showPostCharWaitDialogCallId = null;
930 showPostCharWaitDialogChars = null;
931 } else {
932 showPostCharWaitDialogOnResume = true;
933 showPostCharWaitDialogCallId = callId;
934 showPostCharWaitDialogChars = chars;
935 }
Eric Erfanianccca3152017-02-22 16:32:36 -0800936 }
937
linyuh7b86f562017-11-16 11:24:09 -0800938 public void showDialogOrToastForDisconnectedCall(DisconnectMessage disconnectMessage) {
939 LogUtil.i(
940 "InCallActivity.showDialogOrToastForDisconnectedCall",
941 "disconnect cause: %s",
942 disconnectMessage);
943
944 if (disconnectMessage.dialog == null || isFinishing()) {
945 return;
946 }
947
948 dismissPendingDialogs();
949
950 // Show a toast if the app is in background when a dialog can't be visible.
951 if (!isVisible()) {
952 Toast.makeText(getApplicationContext(), disconnectMessage.toastMessage, Toast.LENGTH_LONG)
953 .show();
954 return;
955 }
956
957 // Show the dialog.
linyuhc3968e62017-11-20 17:40:50 -0800958 errorDialog = disconnectMessage.dialog;
linyuh7b86f562017-11-16 11:24:09 -0800959 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("showErrorDialog");
960 disconnectMessage.dialog.setOnDismissListener(
961 dialogInterface -> {
962 lock.release();
963 onDialogDismissed();
964 });
965 disconnectMessage.dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
966 disconnectMessage.dialog.show();
967 }
968
969 private void onDialogDismissed() {
linyuhc3968e62017-11-20 17:40:50 -0800970 errorDialog = null;
linyuh7b86f562017-11-16 11:24:09 -0800971 CallList.getInstance().onErrorDialogDismissed();
Eric Erfanianccca3152017-02-22 16:32:36 -0800972 }
973
974 public void dismissPendingDialogs() {
linyuhc3968e62017-11-20 17:40:50 -0800975 LogUtil.enterBlock("InCallActivity.dismissPendingDialogs");
linyuhf99f6302017-11-15 11:23:51 -0800976
977 if (!isVisible) {
978 // Defer the dismissing action as the activity is not visible and onSaveInstanceState may have
979 // been called.
Eric Erfanianccca3152017-02-22 16:32:36 -0800980 LogUtil.i(
981 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
982 needDismissPendingDialogs = true;
linyuhf99f6302017-11-15 11:23:51 -0800983 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800984 }
linyuhf99f6302017-11-15 11:23:51 -0800985
986 // Dismiss the error dialog
linyuhf99f6302017-11-15 11:23:51 -0800987 if (errorDialog != null) {
988 errorDialog.dismiss();
linyuhc3968e62017-11-20 17:40:50 -0800989 errorDialog = null;
linyuhf99f6302017-11-15 11:23:51 -0800990 }
991
992 // Dismiss the phone account selection dialog
linyuhf99f6302017-11-15 11:23:51 -0800993 if (selectPhoneAccountDialogFragment != null) {
994 selectPhoneAccountDialogFragment.dismiss();
linyuhc3968e62017-11-20 17:40:50 -0800995 selectPhoneAccountDialogFragment = null;
linyuhf99f6302017-11-15 11:23:51 -0800996 }
997
998 // Dismiss the dialog for international call on WiFi
999 InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
1000 (InternationalCallOnWifiDialogFragment)
linyuhc3968e62017-11-20 17:40:50 -08001001 getSupportFragmentManager().findFragmentByTag(Tags.INTERNATIONAL_CALL_ON_WIFI);
linyuhf99f6302017-11-15 11:23:51 -08001002 if (internationalCallOnWifiFragment != null) {
1003 internationalCallOnWifiFragment.dismiss();
1004 }
1005
1006 // Dismiss the answer screen
1007 AnswerScreen answerScreen = getAnswerScreen();
1008 if (answerScreen != null) {
1009 answerScreen.dismissPendingDialogs();
1010 }
1011
1012 needDismissPendingDialogs = false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001013 }
1014
linyuhc3968e62017-11-20 17:40:50 -08001015 private void enableInCallOrientationEventListener(boolean enable) {
linyuh69a25062017-11-15 16:18:51 -08001016 if (enable) {
1017 inCallOrientationEventListener.enable(true /* notifyDeviceOrientationChange */);
1018 } else {
1019 inCallOrientationEventListener.disable();
1020 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001021 }
1022
1023 public void setExcludeFromRecents(boolean exclude) {
linyuhc3968e62017-11-20 17:40:50 -08001024 int taskId = getTaskId();
1025
1026 List<AppTask> tasks = getSystemService(ActivityManager.class).getAppTasks();
1027 for (AppTask task : tasks) {
1028 try {
1029 if (task.getTaskInfo().id == taskId) {
1030 task.setExcludeFromRecents(exclude);
1031 }
1032 } catch (RuntimeException e) {
1033 LogUtil.e("InCallActivity.setExcludeFromRecents", "RuntimeException:\n%s", e);
1034 }
1035 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001036 }
1037
Eric Erfanianccca3152017-02-22 16:32:36 -08001038 @Nullable
1039 public FragmentManager getDialpadFragmentManager() {
1040 InCallScreen inCallScreen = getInCallScreen();
1041 if (inCallScreen != null) {
1042 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
1043 }
1044 return null;
1045 }
1046
1047 public int getDialpadContainerId() {
1048 return getInCallScreen().getAnswerAndDialpadContainerResourceId();
1049 }
1050
1051 @Override
1052 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
1053 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
1054 if (call == null) {
1055 // This is a work around for a bug where we attempt to create a new delegate after the call
1056 // has already been removed. An example of when this can happen is:
1057 // 1. incoming video call in landscape mode
1058 // 2. remote party hangs up
1059 // 3. activity switches from landscape to portrait
1060 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
1061 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
1062 // because this new state is transient and the activity will be destroyed soon.
1063 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
1064 return new AnswerScreenPresenterStub();
1065 } else {
1066 return new AnswerScreenPresenter(
1067 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
1068 }
1069 }
1070
1071 @Override
1072 public InCallScreenDelegate newInCallScreenDelegate() {
1073 return new CallCardPresenter(this);
1074 }
1075
1076 @Override
1077 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
1078 return new CallButtonPresenter(this);
1079 }
1080
1081 @Override
Eric Erfanian90508232017-03-24 09:31:16 -07001082 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
1083 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
1084 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
1085 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
1086 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001087 return new VideoCallPresenter();
1088 }
1089
1090 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001091 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -08001092 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001093 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001094 }
1095
linyuh7b86f562017-11-16 11:24:09 -08001096 public void showToastForWiFiToLteHandover(DialerCall call) {
1097 if (call.hasShownWiFiToLteHandoverToast()) {
1098 return;
1099 }
1100
1101 Toast.makeText(this, R.string.video_call_wifi_to_lte_handover_toast, Toast.LENGTH_LONG).show();
1102 call.setHasShownWiFiToLteHandoverToast();
Eric Erfanianccca3152017-02-22 16:32:36 -08001103 }
1104
linyuh7b86f562017-11-16 11:24:09 -08001105 public void showDialogOrToastForWifiHandoverFailure(DialerCall call) {
1106 if (call.showWifiHandoverAlertAsToast()) {
1107 Toast.makeText(this, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
1108 .show();
1109 return;
1110 }
1111
1112 dismissPendingDialogs();
1113
1114 AlertDialog.Builder builder =
1115 new AlertDialog.Builder(this).setTitle(R.string.video_call_lte_to_wifi_failed_title);
1116
1117 // This allows us to use the theme of the dialog instead of the activity
1118 View dialogCheckBoxView =
1119 View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null /* root */);
1120 CheckBox wifiHandoverFailureCheckbox =
1121 (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
1122 wifiHandoverFailureCheckbox.setChecked(false);
1123
1124 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("WifiFailedDialog");
linyuhc3968e62017-11-20 17:40:50 -08001125 errorDialog =
linyuh7b86f562017-11-16 11:24:09 -08001126 builder
1127 .setView(dialogCheckBoxView)
1128 .setMessage(R.string.video_call_lte_to_wifi_failed_message)
1129 .setOnCancelListener(dialogInterface -> onDialogDismissed())
1130 .setPositiveButton(
1131 android.R.string.ok,
1132 (dialogInterface, id) -> {
1133 call.setDoNotShowDialogForHandoffToWifiFailure(
1134 wifiHandoverFailureCheckbox.isChecked());
1135 dialogInterface.cancel();
1136 onDialogDismissed();
1137 })
1138 .setOnDismissListener(dialogInterface -> lock.release())
1139 .create();
linyuh7b86f562017-11-16 11:24:09 -08001140 errorDialog.show();
Eric Erfanianccca3152017-02-22 16:32:36 -08001141 }
1142
linyuh7b86f562017-11-16 11:24:09 -08001143 public void showDialogForInternationalCallOnWifi(@NonNull DialerCall call) {
1144 if (!InternationalCallOnWifiDialogFragment.shouldShow(this)) {
1145 LogUtil.i(
1146 "InCallActivity.showDialogForInternationalCallOnWifi",
1147 "InternationalCallOnWifiDialogFragment.shouldShow returned false");
1148 return;
1149 }
1150
1151 InternationalCallOnWifiDialogFragment fragment =
1152 InternationalCallOnWifiDialogFragment.newInstance(
linyuhc3968e62017-11-20 17:40:50 -08001153 call.getId(), internationalCallOnWifiCallback);
1154 fragment.show(getSupportFragmentManager(), Tags.INTERNATIONAL_CALL_ON_WIFI);
Eric Erfanianc857f902017-05-15 14:05:33 -07001155 }
1156
Eric Erfanian938468d2017-10-24 14:05:52 -07001157 @Override
1158 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
1159 super.onMultiWindowModeChanged(isInMultiWindowMode);
linyuh57b093b2017-11-17 14:32:32 -08001160 updateNavigationBar(isDialpadVisible());
1161 }
1162
linyuhc3968e62017-11-20 17:40:50 -08001163 private void updateNavigationBar(boolean isDialpadVisible) {
linyuh57b093b2017-11-17 14:32:32 -08001164 if (ActivityCompat.isInMultiWindowMode(this)) {
1165 return;
1166 }
1167
1168 View navigationBarBackground = getWindow().findViewById(R.id.navigation_bar_background);
1169 if (navigationBarBackground != null) {
1170 navigationBarBackground.setVisibility(isDialpadVisible ? View.VISIBLE : View.GONE);
Eric Erfanian938468d2017-10-24 14:05:52 -07001171 }
1172 }
1173
Eric Erfanianccca3152017-02-22 16:32:36 -08001174 public void setAllowOrientationChange(boolean allowOrientationChange) {
wangqi9982f0d2017-10-11 17:46:07 -07001175 if (this.allowOrientationChange == allowOrientationChange) {
1176 return;
1177 }
1178 this.allowOrientationChange = allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -08001179 if (!allowOrientationChange) {
1180 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
1181 } else {
1182 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
1183 }
1184 enableInCallOrientationEventListener(allowOrientationChange);
1185 }
1186
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001187 public void hideMainInCallFragment() {
linyuhc3968e62017-11-20 17:40:50 -08001188 LogUtil.enterBlock("InCallActivity.hideMainInCallFragment");
1189 if (getCallCardFragmentVisible()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001190 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1191 hideInCallScreenFragment(transaction);
1192 hideVideoCallScreenFragment(transaction);
1193 transaction.commitAllowingStateLoss();
1194 getSupportFragmentManager().executePendingTransactions();
1195 }
1196 }
1197
1198 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001199 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -08001200 // If the activity's onStart method hasn't been called yet then defer doing any work.
1201 if (!isVisible) {
1202 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001203 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001204 return;
1205 }
1206
1207 // Don't let this be reentrant.
1208 if (isInShowMainInCallFragment) {
1209 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001210 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001211 return;
1212 }
1213
1214 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001215 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
1216 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
Eric Erfanianccca3152017-02-22 16:32:36 -08001217 LogUtil.i(
1218 "InCallActivity.showMainInCallFragment",
1219 "shouldShowAnswerUi: %b, shouldShowVideoUi: %b, "
1220 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowVideoCallScreen: %b",
1221 shouldShowAnswerUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -07001222 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -08001223 didShowAnswerScreen,
1224 didShowInCallScreen,
1225 didShowVideoCallScreen);
1226 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001227 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -08001228
1229 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1230 boolean didChangeInCall;
1231 boolean didChangeVideo;
1232 boolean didChangeAnswer;
1233 if (shouldShowAnswerUi.shouldShow) {
1234 didChangeInCall = hideInCallScreenFragment(transaction);
1235 didChangeVideo = hideVideoCallScreenFragment(transaction);
1236 didChangeAnswer = showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001237 } else if (shouldShowVideoUi.shouldShow) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001238 didChangeInCall = hideInCallScreenFragment(transaction);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001239 didChangeVideo = showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001240 didChangeAnswer = hideAnswerScreenFragment(transaction);
1241 } else {
1242 didChangeInCall = showInCallScreenFragment(transaction);
1243 didChangeVideo = hideVideoCallScreenFragment(transaction);
1244 didChangeAnswer = hideAnswerScreenFragment(transaction);
1245 }
1246
1247 if (didChangeInCall || didChangeVideo || didChangeAnswer) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001248 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -08001249 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001250 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001251 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1252 }
1253 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001254 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001255 }
1256
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001257 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001258 DialerCall call = CallList.getInstance().getIncomingCall();
1259 if (call != null) {
1260 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001261 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001262 }
1263
1264 call = CallList.getInstance().getVideoUpgradeRequestCall();
1265 if (call != null) {
1266 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001267 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001268 }
1269
1270 // Check if we're showing the answer screen and the call is disconnected. If this condition is
1271 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
1272 // the user rejects an incoming call.
1273 call = CallList.getInstance().getFirstCall();
1274 if (call == null) {
1275 call = CallList.getInstance().getBackgroundCall();
1276 }
1277 if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
1278 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001279 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001280 }
1281
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001282 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001283 }
1284
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001285 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001286 DialerCall call = CallList.getInstance().getFirstCall();
1287 if (call == null) {
1288 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001289 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001290 }
1291
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001292 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001293 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001294 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001295 }
1296
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001297 if (call.hasSentVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001298 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001299 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001300 }
1301
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001302 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001303 }
1304
1305 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
1306 // When rejecting a call the active call can become null in which case we should continue
1307 // showing the answer screen.
1308 if (didShowAnswerScreen && call == null) {
1309 return false;
1310 }
1311
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001312 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
1313
1314 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -08001315
1316 // Check if we're already showing an answer screen for this call.
1317 if (didShowAnswerScreen) {
1318 AnswerScreen answerScreen = getAnswerScreen();
1319 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001320 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -07001321 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
1322 && !answerScreen.isActionTimeout()) {
1323 LogUtil.d(
1324 "InCallActivity.showAnswerScreenFragment",
1325 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -08001326 return false;
1327 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001328 if (answerScreen.isActionTimeout()) {
1329 LogUtil.i(
1330 "InCallActivity.showAnswerScreenFragment",
1331 "answer fragment exists but has been accepted/rejected and timed out");
1332 } else {
1333 LogUtil.i(
1334 "InCallActivity.showAnswerScreenFragment",
1335 "answer fragment exists but arguments do not match");
1336 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001337 hideAnswerScreenFragment(transaction);
1338 }
1339
1340 // Show a new answer screen.
1341 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -07001342 AnswerBindings.createAnswerScreen(
1343 call.getId(),
1344 call.isVideoCall(),
1345 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -07001346 call.getVideoTech().isSelfManagedCamera(),
1347 shouldAllowAnswerAndRelease(call),
1348 CallList.getInstance().getBackgroundCall() != null);
linyuhc3968e62017-11-20 17:40:50 -08001349 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), Tags.ANSWER_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001350
1351 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
1352 didShowAnswerScreen = true;
1353 return true;
1354 }
1355
Eric Erfanian90508232017-03-24 09:31:16 -07001356 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
1357 if (CallList.getInstance().getActiveCall() == null) {
1358 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
1359 return false;
1360 }
1361 if (getSystemService(TelephonyManager.class).getPhoneType()
1362 == TelephonyManager.PHONE_TYPE_CDMA) {
1363 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
1364 return false;
1365 }
1366 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
1367 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
1368 return false;
1369 }
linyuhc3968e62017-11-20 17:40:50 -08001370 if (!ConfigProviderBindings.get(this)
1371 .getBoolean(ConfigNames.ANSWER_AND_RELEASE_ENABLED, true)) {
Eric Erfanian90508232017-03-24 09:31:16 -07001372 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
1373 return false;
1374 }
1375
1376 return true;
1377 }
1378
Eric Erfanianccca3152017-02-22 16:32:36 -08001379 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
1380 if (!didShowAnswerScreen) {
1381 return false;
1382 }
1383 AnswerScreen answerScreen = getAnswerScreen();
1384 if (answerScreen != null) {
1385 transaction.remove(answerScreen.getAnswerScreenFragment());
1386 }
1387
1388 didShowAnswerScreen = false;
1389 return true;
1390 }
1391
1392 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
1393 if (didShowInCallScreen) {
1394 return false;
1395 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001396 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
linyuhc3968e62017-11-20 17:40:50 -08001397 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), Tags.IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001398 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1399 didShowInCallScreen = true;
1400 return true;
1401 }
1402
1403 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
1404 if (!didShowInCallScreen) {
1405 return false;
1406 }
1407 InCallScreen inCallScreen = getInCallScreen();
1408 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001409 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -08001410 }
1411 didShowInCallScreen = false;
1412 return true;
1413 }
1414
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001415 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001416 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001417 VideoCallScreen videoCallScreen = getVideoCallScreen();
1418 if (videoCallScreen.getCallId().equals(call.getId())) {
1419 return false;
1420 }
1421 LogUtil.i(
1422 "InCallActivity.showVideoCallScreenFragment",
1423 "video call fragment exists but arguments do not match");
1424 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -08001425 }
1426
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001427 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
1428
Eric Erfanian90508232017-03-24 09:31:16 -07001429 VideoCallScreen videoCallScreen =
1430 VideoBindings.createVideoCallScreen(
1431 call.getId(), call.getVideoTech().shouldUseSurfaceView());
linyuhc3968e62017-11-20 17:40:50 -08001432 transaction.add(
1433 R.id.main, videoCallScreen.getVideoCallScreenFragment(), Tags.VIDEO_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001434
1435 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1436 didShowVideoCallScreen = true;
1437 return true;
1438 }
1439
1440 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
1441 if (!didShowVideoCallScreen) {
1442 return false;
1443 }
1444 VideoCallScreen videoCallScreen = getVideoCallScreen();
1445 if (videoCallScreen != null) {
1446 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
1447 }
1448 didShowVideoCallScreen = false;
1449 return true;
1450 }
1451
linyuhc3968e62017-11-20 17:40:50 -08001452 private AnswerScreen getAnswerScreen() {
1453 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(Tags.ANSWER_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001454 }
1455
linyuhc3968e62017-11-20 17:40:50 -08001456 private InCallScreen getInCallScreen() {
1457 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001458 }
1459
linyuhc3968e62017-11-20 17:40:50 -08001460 private VideoCallScreen getVideoCallScreen() {
1461 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.VIDEO_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001462 }
1463
1464 @Override
1465 public void onPseudoScreenStateChanged(boolean isOn) {
1466 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
1467 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
1468 }
1469
1470 /**
1471 * For some touch related issue, turning off the screen can be faked by drawing a black view over
1472 * the activity. All touch events started when the screen is "off" is rejected.
1473 *
1474 * @see PseudoScreenState
1475 */
1476 @Override
1477 public boolean dispatchTouchEvent(MotionEvent event) {
1478 // Reject any gesture that started when the screen is in the fake off state.
1479 if (touchDownWhenPseudoScreenOff) {
1480 if (event.getAction() == MotionEvent.ACTION_UP) {
1481 touchDownWhenPseudoScreenOff = false;
1482 }
1483 return true;
1484 }
1485 // Reject all touch event when the screen is in the fake off state.
1486 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
1487 if (event.getAction() == MotionEvent.ACTION_DOWN) {
1488 touchDownWhenPseudoScreenOff = true;
1489 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
1490 }
1491 return true;
1492 }
1493 return super.dispatchTouchEvent(event);
1494 }
1495
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001496 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -08001497 public final boolean shouldShow;
1498 public final DialerCall call;
1499
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001500 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001501 this.shouldShow = shouldShow;
1502 this.call = call;
1503 }
1504 }
linyuhc3968e62017-11-20 17:40:50 -08001505
1506 private static final class IntentExtraNames {
1507 static final String FOR_FULL_SCREEN = "InCallActivity.for_full_screen_intent";
1508 static final String NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
1509 static final String SHOW_DIALPAD = "InCallActivity.show_dialpad";
1510 }
1511
1512 private static final class KeysForSavedInstance {
1513 static final String DIALPAD_TEXT = "InCallActivity.dialpad_text";
1514 static final String DID_SHOW_ANSWER_SCREEN = "did_show_answer_screen";
1515 static final String DID_SHOW_IN_CALL_SCREEN = "did_show_in_call_screen";
1516 static final String DID_SHOW_VIDEO_CALL_SCREEN = "did_show_video_call_screen";
1517 }
1518
1519 /** Request codes for pending intents. */
1520 public static final class PendingIntentRequestCodes {
1521 static final int NON_FULL_SCREEN = 0;
1522 static final int FULL_SCREEN = 1;
1523 static final int BUBBLE = 2;
1524 }
1525
1526 private static final class Tags {
1527 static final String ANSWER_SCREEN = "tag_answer_screen";
1528 static final String DIALPAD_FRAGMENT = "tag_dialpad_fragment";
1529 static final String IN_CALL_SCREEN = "tag_in_call_screen";
1530 static final String INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi";
1531 static final String SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment";
1532 static final String VIDEO_CALL_SCREEN = "tag_video_call_screen";
1533 static final String POST_CHAR_DIALOG_FRAGMENT = "tag_post_char_dialog_fragment";
1534 }
1535
1536 private static final class ConfigNames {
1537 static final String ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
1538 }
1539
1540 private static final class InternationalCallOnWifiCallback
1541 implements InternationalCallOnWifiDialogFragment.Callback {
1542 private static final String TAG = InternationalCallOnWifiCallback.class.getCanonicalName();
1543
1544 @Override
1545 public void continueCall(@NonNull String callId) {
1546 LogUtil.i(TAG, "Continuing call with ID: %s", callId);
1547 }
1548
1549 @Override
1550 public void cancelCall(@NonNull String callId) {
1551 DialerCall call = CallList.getInstance().getCallById(callId);
1552 if (call == null) {
1553 LogUtil.i(TAG, "Call destroyed before the dialog is closed");
1554 return;
1555 }
1556
1557 LogUtil.i(TAG, "Disconnecting international call on WiFi");
1558 call.disconnect();
1559 }
1560 }
1561
1562 private static final class SelectPhoneAccountListener
1563 extends SelectPhoneAccountDialogFragment.SelectPhoneAccountListener {
1564 private static final String TAG = SelectPhoneAccountListener.class.getCanonicalName();
1565
1566 @Override
1567 public void onPhoneAccountSelected(
1568 PhoneAccountHandle selectedAccountHandle, boolean setDefault, String callId) {
1569 DialerCall call = CallList.getInstance().getCallById(callId);
1570 LogUtil.i(TAG, "Phone account select with call:\n%s", call);
1571
1572 if (call != null) {
1573 call.phoneAccountSelected(selectedAccountHandle, setDefault);
1574 }
1575 }
1576
1577 @Override
1578 public void onDialogDismissed(String callId) {
1579 DialerCall call = CallList.getInstance().getCallById(callId);
1580 LogUtil.i(TAG, "Disconnecting call:\n%s" + call);
1581
1582 if (call != null) {
1583 call.disconnect();
1584 }
1585 }
1586 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001587}