blob: 2b6c2b49dc4d10c07bc8ba23d5daa0e20ea36fed [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;
wangqibc28ea72018-04-02 16:23:00 -070038import android.support.v4.app.DialogFragment;
erfaniand05d8992018-03-20 19:42:26 -070039import android.support.v4.app.Fragment;
Eric Erfanianccca3152017-02-22 16:32:36 -080040import android.support.v4.app.FragmentManager;
41import android.support.v4.app.FragmentTransaction;
linyuh57b093b2017-11-17 14:32:32 -080042import android.support.v4.content.res.ResourcesCompat;
Eric Erfanianccca3152017-02-22 16:32:36 -080043import android.support.v4.graphics.ColorUtils;
twyen73a74c32018-03-07 12:12:24 -080044import android.telecom.Call;
linyuhc3968e62017-11-20 17:40:50 -080045import android.telecom.CallAudioState;
46import android.telecom.PhoneAccountHandle;
Eric Erfanian90508232017-03-24 09:31:16 -070047import android.telephony.TelephonyManager;
Eric Erfanianccca3152017-02-22 16:32:36 -080048import android.view.KeyEvent;
49import android.view.MenuItem;
50import android.view.MotionEvent;
51import android.view.View;
linyuh9c327da2017-11-14 12:33:48 -080052import android.view.WindowManager;
linyuhc3968e62017-11-20 17:40:50 -080053import android.view.animation.Animation;
54import android.view.animation.AnimationUtils;
linyuh7b86f562017-11-16 11:24:09 -080055import android.widget.CheckBox;
56import android.widget.Toast;
linyuhf99f6302017-11-15 11:23:51 -080057import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
twyen66adad02018-04-24 13:51:08 -070058import com.android.contacts.common.widget.SelectPhoneAccountDialogOptions;
59import com.android.contacts.common.widget.SelectPhoneAccountDialogOptionsUtil;
linyuhc3968e62017-11-20 17:40:50 -080060import com.android.dialer.animation.AnimUtils;
61import com.android.dialer.animation.AnimationListenerAdapter;
Eric Erfaniand5e47f62017-03-15 14:41:07 -070062import com.android.dialer.common.Assert;
Eric Erfanianccca3152017-02-22 16:32:36 -080063import com.android.dialer.common.LogUtil;
twyen73a74c32018-03-07 12:12:24 -080064import com.android.dialer.common.concurrent.DialerExecutorComponent;
weijiaxu650e7cc2017-10-31 12:38:54 -070065import com.android.dialer.common.concurrent.ThreadUtil;
twyena1723252018-04-24 17:02:57 -070066import com.android.dialer.common.concurrent.UiListener;
Eric Erfanian2ca43182017-08-31 06:57:16 -070067import com.android.dialer.configprovider.ConfigProviderBindings;
twyen73a74c32018-03-07 12:12:24 -080068import com.android.dialer.logging.DialerImpression.Type;
Eric Erfanianccca3152017-02-22 16:32:36 -080069import com.android.dialer.logging.Logger;
Eric Erfanian8369df02017-05-03 10:27:13 -070070import com.android.dialer.logging.ScreenEvent;
zachh7a96dc72018-02-20 22:16:03 -080071import com.android.dialer.metrics.Metrics;
72import com.android.dialer.metrics.MetricsComponent;
twyen73a74c32018-03-07 12:12:24 -080073import com.android.dialer.preferredsim.PreferredAccountRecorder;
74import com.android.dialer.preferredsim.PreferredAccountWorker;
twyena1723252018-04-24 17:02:57 -070075import com.android.dialer.preferredsim.PreferredAccountWorker.Result;
twyen73a74c32018-03-07 12:12:24 -080076import com.android.dialer.preferredsim.suggestion.SuggestionProvider;
linyuhc3968e62017-11-20 17:40:50 -080077import com.android.dialer.util.ViewUtil;
Eric Erfanianccca3152017-02-22 16:32:36 -080078import com.android.incallui.answer.bindings.AnswerBindings;
79import com.android.incallui.answer.protocol.AnswerScreen;
80import com.android.incallui.answer.protocol.AnswerScreenDelegate;
81import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
82import com.android.incallui.answerproximitysensor.PseudoScreenState;
linyuh57b093b2017-11-17 14:32:32 -080083import com.android.incallui.audiomode.AudioModeProvider;
Eric Erfanianccca3152017-02-22 16:32:36 -080084import com.android.incallui.call.CallList;
85import com.android.incallui.call.DialerCall;
86import com.android.incallui.call.DialerCall.State;
linyuh57b093b2017-11-17 14:32:32 -080087import com.android.incallui.call.TelecomAdapter;
Eric Erfanian2ca43182017-08-31 06:57:16 -070088import com.android.incallui.callpending.CallPendingActivity;
89import com.android.incallui.disconnectdialog.DisconnectMessage;
Eric Erfanianccca3152017-02-22 16:32:36 -080090import com.android.incallui.incall.bindings.InCallBindings;
91import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
92import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
93import com.android.incallui.incall.protocol.InCallScreen;
94import com.android.incallui.incall.protocol.InCallScreenDelegate;
95import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
linyuh7b86f562017-11-16 11:24:09 -080096import com.android.incallui.incalluilock.InCallUiLock;
wangqi219b8702018-02-13 09:34:41 -080097import com.android.incallui.rtt.bindings.RttBindings;
98import com.android.incallui.rtt.protocol.RttCallScreen;
99import com.android.incallui.rtt.protocol.RttCallScreenDelegate;
100import com.android.incallui.rtt.protocol.RttCallScreenDelegateFactory;
erfaniand05d8992018-03-20 19:42:26 -0700101import com.android.incallui.speakeasy.SpeakEasyCallManager;
linyuhf99f6302017-11-15 11:23:51 -0800102import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
Eric Erfanianccca3152017-02-22 16:32:36 -0800103import com.android.incallui.video.bindings.VideoBindings;
104import com.android.incallui.video.protocol.VideoCallScreen;
105import com.android.incallui.video.protocol.VideoCallScreenDelegate;
106import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
linyuhc3968e62017-11-20 17:40:50 -0800107import com.google.common.base.Optional;
twyena1723252018-04-24 17:02:57 -0700108import com.google.common.util.concurrent.ListenableFuture;
linyuhc3968e62017-11-20 17:40:50 -0800109import java.lang.annotation.Retention;
110import java.lang.annotation.RetentionPolicy;
111import java.util.ArrayList;
112import java.util.List;
Eric Erfanianccca3152017-02-22 16:32:36 -0800113
114/** Version of {@link InCallActivity} that shows the new UI */
115public class InCallActivity extends TransactionSafeFragmentActivity
116 implements AnswerScreenDelegateFactory,
117 InCallScreenDelegateFactory,
118 InCallButtonUiDelegateFactory,
119 VideoCallScreenDelegateFactory,
wangqi219b8702018-02-13 09:34:41 -0800120 RttCallScreenDelegateFactory,
Eric Erfanianccca3152017-02-22 16:32:36 -0800121 PseudoScreenState.StateChangedListener {
122
linyuhc3968e62017-11-20 17:40:50 -0800123 @Retention(RetentionPolicy.SOURCE)
124 @IntDef({
125 DIALPAD_REQUEST_NONE,
126 DIALPAD_REQUEST_SHOW,
127 DIALPAD_REQUEST_HIDE,
128 })
129 @interface DialpadRequestType {}
Eric Erfanian2ca43182017-08-31 06:57:16 -0700130
linyuhc3968e62017-11-20 17:40:50 -0800131 private static final int DIALPAD_REQUEST_NONE = 1;
132 private static final int DIALPAD_REQUEST_SHOW = 2;
133 private static final int DIALPAD_REQUEST_HIDE = 3;
linyuh57b093b2017-11-17 14:32:32 -0800134
linyuhc3968e62017-11-20 17:40:50 -0800135 private static Optional<Integer> audioRouteForTesting = Optional.absent();
linyuh57b093b2017-11-17 14:32:32 -0800136
twyen73a74c32018-03-07 12:12:24 -0800137 private SelectPhoneAccountListener selectPhoneAccountListener;
twyena1723252018-04-24 17:02:57 -0700138 private UiListener<Result> preferredAccountWorkerResultListener;
Eric Erfanianccca3152017-02-22 16:32:36 -0800139
linyuhc3968e62017-11-20 17:40:50 -0800140 private Animation dialpadSlideInAnimation;
141 private Animation dialpadSlideOutAnimation;
142 private Dialog errorDialog;
143 private GradientDrawable backgroundDrawable;
linyuh69a25062017-11-15 16:18:51 -0800144 private InCallOrientationEventListener inCallOrientationEventListener;
linyuhc3968e62017-11-20 17:40:50 -0800145 private View pseudoBlackScreenOverlay;
146 private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment;
147 private String dtmfTextToPrepopulate;
148 private String showPostCharWaitDialogCallId;
149 private String showPostCharWaitDialogChars;
150 private boolean allowOrientationChange;
151 private boolean animateDialpadOnShow;
Eric Erfanianccca3152017-02-22 16:32:36 -0800152 private boolean didShowAnswerScreen;
153 private boolean didShowInCallScreen;
154 private boolean didShowVideoCallScreen;
wangqi219b8702018-02-13 09:34:41 -0800155 private boolean didShowRttCallScreen;
erfaniand05d8992018-03-20 19:42:26 -0700156 private boolean didShowSpeakEasyScreen;
erfanian612d13a2018-04-04 15:27:57 -0700157 private String lastShownSpeakEasyScreenUniqueCallid = "";
linyuh9c327da2017-11-14 12:33:48 -0800158 private boolean dismissKeyguard;
Eric Erfanianccca3152017-02-22 16:32:36 -0800159 private boolean isInShowMainInCallFragment;
linyuhc3968e62017-11-20 17:40:50 -0800160 private boolean isRecreating; // whether the activity is going to be recreated
161 private boolean isVisible;
Eric Erfanianccca3152017-02-22 16:32:36 -0800162 private boolean needDismissPendingDialogs;
linyuhc3968e62017-11-20 17:40:50 -0800163 private boolean showPostCharWaitDialogOnResume;
164 private boolean touchDownWhenPseudoScreenOff;
165 private int[] backgroundDrawableColors;
166 @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
erfaniand05d8992018-03-20 19:42:26 -0700167 private SpeakEasyCallManager speakEasyCallManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800168
169 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700170 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800171 Intent intent = new Intent(Intent.ACTION_MAIN, null);
172 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
173 intent.setClass(context, InCallActivity.class);
linyuhc3968e62017-11-20 17:40:50 -0800174 if (showDialpad) {
175 intent.putExtra(IntentExtraNames.SHOW_DIALPAD, true);
176 }
177 intent.putExtra(IntentExtraNames.NEW_OUTGOING_CALL, newOutgoingCall);
178 intent.putExtra(IntentExtraNames.FOR_FULL_SCREEN, isForFullScreen);
Eric Erfanianccca3152017-02-22 16:32:36 -0800179 return intent;
180 }
181
182 @Override
183 protected void onResumeFragments() {
184 super.onResumeFragments();
185 if (needDismissPendingDialogs) {
186 dismissPendingDialogs();
187 }
188 }
189
190 @Override
linyuhc3968e62017-11-20 17:40:50 -0800191 protected void onCreate(Bundle bundle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700192 Trace.beginSection("InCallActivity.onCreate");
linyuhc3968e62017-11-20 17:40:50 -0800193 super.onCreate(bundle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800194
twyena1723252018-04-24 17:02:57 -0700195 preferredAccountWorkerResultListener =
196 DialerExecutorComponent.get(this)
197 .createUiListener(getFragmentManager(), "preferredAccountWorkerResultListener");
198
twyen73a74c32018-03-07 12:12:24 -0800199 selectPhoneAccountListener = new SelectPhoneAccountListener(getApplicationContext());
200
linyuhc3968e62017-11-20 17:40:50 -0800201 if (bundle != null) {
202 didShowAnswerScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN);
203 didShowInCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN);
204 didShowVideoCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN);
wangqi153af2f2018-02-15 16:21:49 -0800205 didShowRttCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -0800206 }
207
linyuhc3968e62017-11-20 17:40:50 -0800208 setWindowFlags();
209 setContentView(R.layout.incall_screen);
210 internalResolveIntent(getIntent());
211
212 boolean isLandscape =
213 getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
214 boolean isRtl = ViewUtil.isRtl();
215 if (isLandscape) {
216 dialpadSlideInAnimation =
217 AnimationUtils.loadAnimation(
218 this, isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
219 dialpadSlideOutAnimation =
220 AnimationUtils.loadAnimation(
221 this, isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
222 } else {
223 dialpadSlideInAnimation = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
224 dialpadSlideOutAnimation =
225 AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
226 }
227 dialpadSlideInAnimation.setInterpolator(AnimUtils.EASE_IN);
228 dialpadSlideOutAnimation.setInterpolator(AnimUtils.EASE_OUT);
229 dialpadSlideOutAnimation.setAnimationListener(
230 new AnimationListenerAdapter() {
231 @Override
232 public void onAnimationEnd(Animation animation) {
233 hideDialpadFragment();
234 }
235 });
236
237 if (bundle != null && showDialpadRequest == DIALPAD_REQUEST_NONE) {
238 // If the dialpad was shown before, set related variables so that it can be shown and
239 // populated with the previous DTMF text during onResume().
240 if (bundle.containsKey(IntentExtraNames.SHOW_DIALPAD)) {
241 boolean showDialpad = bundle.getBoolean(IntentExtraNames.SHOW_DIALPAD);
242 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
243 animateDialpadOnShow = false;
244 }
245 dtmfTextToPrepopulate = bundle.getString(KeysForSavedInstance.DIALPAD_TEXT);
246
247 SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment =
248 (SelectPhoneAccountDialogFragment)
249 getFragmentManager().findFragmentByTag(Tags.SELECT_ACCOUNT_FRAGMENT);
250 if (selectPhoneAccountDialogFragment != null) {
251 selectPhoneAccountDialogFragment.setListener(selectPhoneAccountListener);
252 }
253 }
254
linyuh69a25062017-11-15 16:18:51 -0800255 inCallOrientationEventListener = new InCallOrientationEventListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800256
257 getWindow()
258 .getDecorView()
259 .setSystemUiVisibility(
260 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
261
262 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700263 sendBroadcast(CallPendingActivity.getFinishBroadcast());
264 Trace.endSection();
zachh7a96dc72018-02-20 22:16:03 -0800265 MetricsComponent.get(this)
266 .metrics()
267 .stopTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
268 MetricsComponent.get(this)
269 .metrics()
270 .stopTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
Eric Erfanianccca3152017-02-22 16:32:36 -0800271 }
272
linyuhc3968e62017-11-20 17:40:50 -0800273 private void setWindowFlags() {
274 // Allow the activity to be shown when the screen is locked and filter out touch events that are
275 // "too fat".
276 int flags =
277 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
278 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
279
linyuhf79d1cb2017-12-15 17:49:56 -0800280 // When the audio stream is not via Bluetooth, turn on the screen once the activity is shown.
281 // When the audio stream is via Bluetooth, turn on the screen only for an incoming call.
linyuhc3968e62017-11-20 17:40:50 -0800282 final int audioRoute = getAudioRoute();
linyuhf79d1cb2017-12-15 17:49:56 -0800283 if (audioRoute != CallAudioState.ROUTE_BLUETOOTH
284 || CallList.getInstance().getIncomingCall() != null) {
linyuhc3968e62017-11-20 17:40:50 -0800285 flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
286 }
287
288 getWindow().addFlags(flags);
289 }
290
291 private static int getAudioRoute() {
292 if (audioRouteForTesting.isPresent()) {
293 return audioRouteForTesting.get();
294 }
295
296 return AudioModeProvider.getInstance().getAudioState().getRoute();
297 }
298
299 @VisibleForTesting(otherwise = VisibleForTesting.NONE)
300 public static void setAudioRouteForTesting(int audioRoute) {
301 audioRouteForTesting = Optional.of(audioRoute);
302 }
303
304 private void internalResolveIntent(Intent intent) {
305 if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
306 return;
307 }
308
309 if (intent.hasExtra(IntentExtraNames.SHOW_DIALPAD)) {
310 // IntentExtraNames.SHOW_DIALPAD can be used to specify whether the DTMF dialpad should be
311 // initially visible. If the extra is absent, leave the dialpad in its previous state.
312 boolean showDialpad = intent.getBooleanExtra(IntentExtraNames.SHOW_DIALPAD, false);
313 relaunchedFromDialer(showDialpad);
314 }
315
316 DialerCall outgoingCall = CallList.getInstance().getOutgoingCall();
317 if (outgoingCall == null) {
318 outgoingCall = CallList.getInstance().getPendingOutgoingCall();
319 }
320 if (intent.getBooleanExtra(IntentExtraNames.NEW_OUTGOING_CALL, false)) {
321 intent.removeExtra(IntentExtraNames.NEW_OUTGOING_CALL);
322
323 // InCallActivity is responsible for disconnecting a new outgoing call if there is no way of
324 // making it (i.e. no valid call capable accounts).
linyuh122fb0b2018-03-26 13:35:32 -0700325 if (InCallPresenter.isCallWithNoValidAccounts(outgoingCall)) {
linyuhc3968e62017-11-20 17:40:50 -0800326 LogUtil.i(
327 "InCallActivity.internalResolveIntent", "Call with no valid accounts, disconnecting");
328 outgoingCall.disconnect();
329 }
330
331 dismissKeyguard(true);
332 }
333
334 if (showPhoneAccountSelectionDialog()) {
335 hideMainInCallFragment();
336 }
337 }
338
339 /**
340 * When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad should
341 * be shown on launch.
342 *
343 * @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and {@code
344 * false} to indicate no change should be made to the dialpad visibility.
345 */
346 private void relaunchedFromDialer(boolean showDialpad) {
347 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
348 animateDialpadOnShow = true;
349
350 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
351 // If there's only one line in use, AND it's on hold, then we're sure the user
352 // wants to use the dialpad toward the exact line, so un-hold the holding line.
353 DialerCall call = CallList.getInstance().getActiveOrBackgroundCall();
354 if (call != null && call.getState() == State.ONHOLD) {
355 call.unhold();
356 }
357 }
358 }
359
360 /**
361 * Show a phone account selection dialog if there is a call waiting for phone account selection.
362 *
363 * @return true if the dialog was shown.
364 */
365 private boolean showPhoneAccountSelectionDialog() {
366 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
367 if (waitingForAccountCall == null) {
368 return false;
369 }
370
twyena1723252018-04-24 17:02:57 -0700371 ListenableFuture<PreferredAccountWorker.Result> preferredAccountFuture =
372 DialerExecutorComponent.get(this)
373 .backgroundExecutor()
374 .submit(
375 () -> {
376 try {
377 return new PreferredAccountWorker(waitingForAccountCall.getNumber())
378 .doInBackground(getApplicationContext());
379 } catch (Throwable throwable) {
380 throw new Exception(throwable);
381 }
382 });
linyuhc3968e62017-11-20 17:40:50 -0800383
twyena1723252018-04-24 17:02:57 -0700384 preferredAccountWorkerResultListener.listen(
385 this,
386 preferredAccountFuture,
387 result -> {
388 if (result.getPhoneAccountHandle().isPresent()) {
389 Logger.get(this).logImpression(Type.DUAL_SIM_SELECTION_PREFERRED_USED);
390 selectPhoneAccountListener.onPhoneAccountSelected(
391 result.getPhoneAccountHandle().get(), false, waitingForAccountCall.getId());
392 return;
393 }
394 if (result.getSuggestion().isPresent()) {
395 LogUtil.i(
396 "CallingAccountSelector.processPreferredAccount",
397 "SIM suggested: " + result.getSuggestion().get().reason);
398 if (result.getSuggestion().get().shouldAutoSelect) {
399 Logger.get(this).logImpression(Type.DUAL_SIM_SELECTION_SUGGESTION_AUTO_SELECTED);
400 LogUtil.i(
401 "CallingAccountSelector.processPreferredAccount", "Auto selected suggestion");
402 selectPhoneAccountListener.onPhoneAccountSelected(
403 result.getSuggestion().get().phoneAccountHandle,
404 false,
405 waitingForAccountCall.getId());
406 return;
407 }
408 }
409 Bundle extras = waitingForAccountCall.getIntentExtras();
410 List<PhoneAccountHandle> phoneAccountHandles =
411 extras == null
412 ? new ArrayList<>()
413 : extras.getParcelableArrayList(Call.AVAILABLE_PHONE_ACCOUNTS);
twyen66adad02018-04-24 13:51:08 -0700414
twyena1723252018-04-24 17:02:57 -0700415 waitingForAccountCall.setPreferredAccountRecorder(
416 new PreferredAccountRecorder(
417 waitingForAccountCall.getNumber(),
418 result.getSuggestion().orNull(),
419 result.getDataId().orNull()));
420 SelectPhoneAccountDialogOptions.Builder optionsBuilder =
421 SelectPhoneAccountDialogOptions.newBuilder()
422 .setTitle(R.string.select_phone_account_for_calls)
423 .setCanSetDefault(result.getDataId().isPresent())
424 .setSetDefaultLabel(R.string.select_phone_account_for_calls_remember)
425 .setCallId(waitingForAccountCall.getId());
twyen66adad02018-04-24 13:51:08 -0700426
twyena1723252018-04-24 17:02:57 -0700427 for (PhoneAccountHandle phoneAccountHandle : phoneAccountHandles) {
428 SelectPhoneAccountDialogOptions.Entry.Builder entryBuilder =
429 SelectPhoneAccountDialogOptions.Entry.newBuilder();
430 SelectPhoneAccountDialogOptionsUtil.setPhoneAccountHandle(
431 entryBuilder, phoneAccountHandle);
432 Optional<String> hint =
433 SuggestionProvider.getHint(
434 this, phoneAccountHandle, result.getSuggestion().orNull());
435 if (hint.isPresent()) {
436 entryBuilder.setHint(hint.get());
437 }
438 optionsBuilder.addEntries(entryBuilder);
439 }
440
441 selectPhoneAccountDialogFragment =
442 SelectPhoneAccountDialogFragment.newInstance(
443 optionsBuilder.build(), selectPhoneAccountListener);
444 selectPhoneAccountDialogFragment.show(getFragmentManager(), Tags.SELECT_ACCOUNT_FRAGMENT);
445 },
446 throwable -> {
447 throw new RuntimeException(throwable);
448 });
twyen73a74c32018-03-07 12:12:24 -0800449
linyuhc3968e62017-11-20 17:40:50 -0800450 return true;
451 }
452
Eric Erfanianccca3152017-02-22 16:32:36 -0800453 @Override
454 protected void onSaveInstanceState(Bundle out) {
linyuh57b093b2017-11-17 14:32:32 -0800455 LogUtil.enterBlock("InCallActivity.onSaveInstanceState");
456
457 // TODO: DialpadFragment should handle this as part of its own state
linyuhc3968e62017-11-20 17:40:50 -0800458 out.putBoolean(IntentExtraNames.SHOW_DIALPAD, isDialpadVisible());
linyuh57b093b2017-11-17 14:32:32 -0800459 DialpadFragment dialpadFragment = getDialpadFragment();
460 if (dialpadFragment != null) {
linyuhc3968e62017-11-20 17:40:50 -0800461 out.putString(KeysForSavedInstance.DIALPAD_TEXT, dialpadFragment.getDtmfText());
linyuh57b093b2017-11-17 14:32:32 -0800462 }
463
linyuhc3968e62017-11-20 17:40:50 -0800464 out.putBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN, didShowAnswerScreen);
465 out.putBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN, didShowInCallScreen);
466 out.putBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN, didShowVideoCallScreen);
wangqi153af2f2018-02-15 16:21:49 -0800467 out.putBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN, didShowRttCallScreen);
erfaniand05d8992018-03-20 19:42:26 -0700468 out.putBoolean(KeysForSavedInstance.DID_SHOW_SPEAK_EASY_SCREEN, didShowSpeakEasyScreen);
linyuh57b093b2017-11-17 14:32:32 -0800469
Eric Erfanianccca3152017-02-22 16:32:36 -0800470 super.onSaveInstanceState(out);
471 isVisible = false;
472 }
473
474 @Override
475 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700476 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800477 super.onStart();
linyuh57b093b2017-11-17 14:32:32 -0800478
Eric Erfanianccca3152017-02-22 16:32:36 -0800479 isVisible = true;
480 showMainInCallFragment();
linyuh57b093b2017-11-17 14:32:32 -0800481
482 InCallPresenter.getInstance().setActivity(this);
483 enableInCallOrientationEventListener(
484 getRequestedOrientation()
485 == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
486 InCallPresenter.getInstance().onActivityStarted();
487
yueg10f6e822018-01-17 15:32:18 -0800488 if (!isRecreating) {
489 InCallPresenter.getInstance().onUiShowing(true);
490 }
491
linyuh437ae952018-03-26 12:46:18 -0700492 if (isInMultiWindowMode() && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800493 // Hide the dialpad because there may not be enough room
494 showDialpadFragment(false, false);
495 }
linyuh57b093b2017-11-17 14:32:32 -0800496
Eric Erfanian2ca43182017-08-31 06:57:16 -0700497 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800498 }
499
500 @Override
501 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700502 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800503 super.onResume();
linyuhc3968e62017-11-20 17:40:50 -0800504
505 if (!InCallPresenter.getInstance().isReadyForTearDown()) {
506 updateTaskDescription();
linyuhc3968e62017-11-20 17:40:50 -0800507 }
508
509 // If there is a pending request to show or hide the dialpad, handle that now.
510 if (showDialpadRequest != DIALPAD_REQUEST_NONE) {
511 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
512 // Exit fullscreen so that the user has access to the dialpad hide/show button.
513 // This is important when showing the dialpad from within dialer.
514 InCallPresenter.getInstance().setFullScreen(false /* isFullScreen */, true /* force */);
515
516 showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
517 animateDialpadOnShow = false;
518
519 DialpadFragment dialpadFragment = getDialpadFragment();
520 if (dialpadFragment != null) {
521 dialpadFragment.setDtmfText(dtmfTextToPrepopulate);
522 dtmfTextToPrepopulate = null;
523 }
524 } else {
525 LogUtil.i("InCallActivity.onResume", "Force-hide the dialpad");
526 if (getDialpadFragment() != null) {
527 showDialpadFragment(false /* show */, false /* animate */);
528 }
529 }
530 showDialpadRequest = DIALPAD_REQUEST_NONE;
531 }
532 updateNavigationBar(isDialpadVisible());
533
534 if (showPostCharWaitDialogOnResume) {
535 showDialogForPostCharWait(showPostCharWaitDialogCallId, showPostCharWaitDialogChars);
536 }
537
538 CallList.getInstance()
539 .onInCallUiShown(getIntent().getBooleanExtra(IntentExtraNames.FOR_FULL_SCREEN, false));
540
Eric Erfanianccca3152017-02-22 16:32:36 -0800541 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
542 pseudoScreenState.addListener(this);
543 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700544 Trace.endSection();
weijiaxu650e7cc2017-10-31 12:38:54 -0700545 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
546 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800547 () ->
zachh7a96dc72018-02-20 22:16:03 -0800548 MetricsComponent.get(this)
549 .metrics()
550 .recordMemory(Metrics.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700551 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800552 }
553
Eric Erfanianccca3152017-02-22 16:32:36 -0800554 @Override
555 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700556 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800557 super.onPause();
linyuh57b093b2017-11-17 14:32:32 -0800558
559 DialpadFragment dialpadFragment = getDialpadFragment();
560 if (dialpadFragment != null) {
561 dialpadFragment.onDialerKeyUp(null);
562 }
563
Eric Erfanianccca3152017-02-22 16:32:36 -0800564 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700565 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800566 }
567
568 @Override
569 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700570 Trace.beginSection("InCallActivity.onStop");
wangqi4d705e52017-09-28 12:23:35 -0700571 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800572 super.onStop();
linyuh57b093b2017-11-17 14:32:32 -0800573
574 // Disconnects the call waiting for a phone account when the activity is hidden (e.g., after the
575 // user presses the home button).
576 // Without this the pending call will get stuck on phone account selection and new calls can't
577 // be created.
578 // Skip this when the screen is locked since the activity may complete its current life cycle
579 // and restart.
linyuhc3968e62017-11-20 17:40:50 -0800580 if (!isRecreating && !getSystemService(KeyguardManager.class).isKeyguardLocked()) {
linyuh57b093b2017-11-17 14:32:32 -0800581 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
582 if (waitingForAccountCall != null) {
583 waitingForAccountCall.disconnect();
584 }
585 }
586
587 enableInCallOrientationEventListener(false);
588 InCallPresenter.getInstance().updateIsChangingConfigurations();
589 InCallPresenter.getInstance().onActivityStopped();
linyuhc3968e62017-11-20 17:40:50 -0800590 if (!isRecreating) {
yueg10f6e822018-01-17 15:32:18 -0800591 InCallPresenter.getInstance().onUiShowing(false);
wangqie6060522018-04-05 11:47:15 -0700592 }
593 if (errorDialog != null) {
594 errorDialog.dismiss();
linyuh57b093b2017-11-17 14:32:32 -0800595 }
596
yueg10f6e822018-01-17 15:32:18 -0800597 if (isFinishing()) {
598 InCallPresenter.getInstance().unsetActivity(this);
599 }
600
Eric Erfanian2ca43182017-08-31 06:57:16 -0700601 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800602 }
603
604 @Override
605 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700606 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800607 super.onDestroy();
linyuh57b093b2017-11-17 14:32:32 -0800608
609 InCallPresenter.getInstance().unsetActivity(this);
610 InCallPresenter.getInstance().updateIsChangingConfigurations();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700611 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800612 }
613
614 @Override
615 public void finish() {
616 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700617 // When user select incall ui from recents after the call is disconnected, it tries to launch
618 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
619 // crash.
620 // By calling finishAndRemoveTask() instead of finish() the task associated with
621 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
622 // this case.
623 //
624 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
625 // clear the task since there could be parent activity in the same task that's still alive.
626 // But InCallActivity is special since it's singleInstance which means it's root activity and
627 // only instance of activity in the task. So it should be safe to also remove task when
628 // finishing.
629 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
630 // finishes, the task should also be removed since it doesn't make sense to go back to it in
631 // anyway anymore.
632 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800633 }
634 }
635
636 private boolean shouldCloseActivityOnFinish() {
linyuhc3968e62017-11-20 17:40:50 -0800637 if (!isVisible) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800638 LogUtil.i(
639 "InCallActivity.shouldCloseActivityOnFinish",
640 "allowing activity to be closed because it's not visible");
641 return true;
642 }
643
twyen8efb4952017-10-06 16:35:54 -0700644 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800645 LogUtil.i(
646 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700647 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800648 return false;
649 }
650
651 LogUtil.i(
652 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700653 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800654 return true;
655 }
656
657 @Override
658 protected void onNewIntent(Intent intent) {
linyuhc3968e62017-11-20 17:40:50 -0800659 LogUtil.enterBlock("InCallActivity.onNewIntent");
Eric Erfanianccca3152017-02-22 16:32:36 -0800660
661 // If the screen is off, we need to make sure it gets turned on for incoming calls.
662 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
663 // when the activity is first created. Therefore, to ensure the screen is turned on
664 // for the call waiting case, we recreate() the current activity. There should be no jank from
665 // this since the screen is already off and will remain so until our new activity is up.
linyuhc3968e62017-11-20 17:40:50 -0800666 if (!isVisible) {
667 onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800668 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
669 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700670 } else {
linyuhc3968e62017-11-20 17:40:50 -0800671 onNewIntent(intent, false /* isRecreating */);
672 }
673 }
674
yuega3305352018-01-09 11:02:47 -0800675 @VisibleForTesting
676 void onNewIntent(Intent intent, boolean isRecreating) {
linyuhc3968e62017-11-20 17:40:50 -0800677 this.isRecreating = isRecreating;
678
679 // We're being re-launched with a new Intent. Since it's possible for a single InCallActivity
680 // instance to persist indefinitely (even if we finish() ourselves), this sequence can
681 // happen any time the InCallActivity needs to be displayed.
682
683 // Stash away the new intent so that we can get it in the future by calling getIntent().
684 // Otherwise getIntent() will return the original Intent from when we first got created.
685 setIntent(intent);
686
687 // Activities are always paused before receiving a new intent, so we can count on our onResume()
688 // method being called next.
689
690 // Just like in onCreate(), handle the intent.
691 // Skip if InCallActivity is going to be recreated since this will be called in onCreate().
692 if (!isRecreating) {
693 internalResolveIntent(intent);
Eric Erfanianccca3152017-02-22 16:32:36 -0800694 }
695 }
696
697 @Override
698 public void onBackPressed() {
linyuh57b093b2017-11-17 14:32:32 -0800699 LogUtil.enterBlock("InCallActivity.onBackPressed");
700
linyuhc3968e62017-11-20 17:40:50 -0800701 if (!isVisible) {
linyuh57b093b2017-11-17 14:32:32 -0800702 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800703 }
linyuh57b093b2017-11-17 14:32:32 -0800704
705 if (!getCallCardFragmentVisible()) {
706 return;
707 }
708
709 DialpadFragment dialpadFragment = getDialpadFragment();
710 if (dialpadFragment != null && dialpadFragment.isVisible()) {
711 showDialpadFragment(false /* show */, true /* animate */);
712 return;
713 }
714
715 if (CallList.getInstance().getIncomingCall() != null) {
716 LogUtil.i(
717 "InCallActivity.onBackPressed",
718 "Ignore the press of the back key when an incoming call is ringing");
719 return;
720 }
721
722 // Nothing special to do. Fall back to the default behavior.
723 super.onBackPressed();
Eric Erfanianccca3152017-02-22 16:32:36 -0800724 }
725
726 @Override
727 public boolean onOptionsItemSelected(MenuItem item) {
728 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
729 if (item.getItemId() == android.R.id.home) {
730 onBackPressed();
731 return true;
732 }
733 return super.onOptionsItemSelected(item);
734 }
735
736 @Override
737 public boolean onKeyUp(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800738 DialpadFragment dialpadFragment = getDialpadFragment();
739 if (dialpadFragment != null
740 && dialpadFragment.isVisible()
741 && dialpadFragment.onDialerKeyUp(event)) {
742 return true;
743 }
744
745 if (keyCode == KeyEvent.KEYCODE_CALL) {
746 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
747 return true;
748 }
749
750 return super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800751 }
752
753 @Override
754 public boolean onKeyDown(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800755 switch (keyCode) {
756 case KeyEvent.KEYCODE_CALL:
757 if (!InCallPresenter.getInstance().handleCallKey()) {
758 LogUtil.e(
759 "InCallActivity.onKeyDown",
760 "InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
761 }
762 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
763 return true;
764
765 // Note that KEYCODE_ENDCALL isn't handled here as the standard system-wide handling of it
766 // is exactly what's needed, namely
767 // (1) "hang up" if there's an active call, or
768 // (2) "don't answer" if there's an incoming call.
769 // (See PhoneWindowManager for implementation details.)
770
771 case KeyEvent.KEYCODE_CAMERA:
772 // Consume KEYCODE_CAMERA since it's easy to accidentally press the camera button.
773 return true;
774
775 case KeyEvent.KEYCODE_VOLUME_UP:
776 case KeyEvent.KEYCODE_VOLUME_DOWN:
777 case KeyEvent.KEYCODE_VOLUME_MUTE:
778 // Ringer silencing handled by PhoneWindowManager.
779 break;
780
781 case KeyEvent.KEYCODE_MUTE:
782 TelecomAdapter.getInstance()
783 .mute(!AudioModeProvider.getInstance().getAudioState().isMuted());
784 return true;
785
786 case KeyEvent.KEYCODE_SLASH:
787 // When verbose logging is enabled, dump the view for debugging/testing purposes.
788 if (LogUtil.isVerboseEnabled()) {
789 View decorView = getWindow().getDecorView();
790 LogUtil.v("InCallActivity.onKeyDown", "View dump:\n%s", decorView);
791 return true;
792 }
793 break;
794
795 case KeyEvent.KEYCODE_EQUALS:
796 break;
797
798 default: // fall out
799 }
800
801 // Pass other key events to DialpadFragment's "onDialerKeyDown" method in case the user types
802 // in DTMF (Dual-tone multi-frequency signaling) code.
803 DialpadFragment dialpadFragment = getDialpadFragment();
804 if (dialpadFragment != null
805 && dialpadFragment.isVisible()
806 && dialpadFragment.onDialerKeyDown(event)) {
807 return true;
808 }
809
810 return super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800811 }
812
813 public boolean isInCallScreenAnimating() {
814 return false;
815 }
816
817 public void showConferenceFragment(boolean show) {
818 if (show) {
819 startActivity(new Intent(this, ManageConferenceActivity.class));
820 }
821 }
822
linyuhc3968e62017-11-20 17:40:50 -0800823 public void showDialpadFragment(boolean show, boolean animate) {
824 if (show == isDialpadVisible()) {
825 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800826 }
linyuhc3968e62017-11-20 17:40:50 -0800827
828 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
829 if (dialpadFragmentManager == null) {
830 LogUtil.i("InCallActivity.showDialpadFragment", "Unable to obtain a FragmentManager");
831 return;
832 }
833
834 if (!animate) {
835 if (show) {
836 showDialpadFragment();
837 } else {
838 hideDialpadFragment();
839 }
840 } else {
841 if (show) {
842 showDialpadFragment();
843 getDialpadFragment().animateShowDialpad();
844 }
845 getDialpadFragment()
846 .getView()
847 .startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
848 }
849
850 ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
851 if (sensor != null) {
852 sensor.onDialpadVisible(show);
853 }
854 showDialpadRequest = DIALPAD_REQUEST_NONE;
855
856 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
857 // repositions itself.
wangqifd4c9f72018-03-08 18:21:50 -0800858 getInCallOrRttCallScreen().onInCallScreenDialpadVisibilityChange(show);
linyuhc3968e62017-11-20 17:40:50 -0800859 }
860
861 private void showDialpadFragment() {
862 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
863 if (dialpadFragmentManager == null) {
864 return;
865 }
866
867 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
868 DialpadFragment dialpadFragment = getDialpadFragment();
869 if (dialpadFragment == null) {
wangqifd4c9f72018-03-08 18:21:50 -0800870 dialpadFragment = new DialpadFragment();
871 transaction.add(getDialpadContainerId(), dialpadFragment, Tags.DIALPAD_FRAGMENT);
linyuhc3968e62017-11-20 17:40:50 -0800872 } else {
873 transaction.show(dialpadFragment);
calderwoodrad5883872017-12-12 15:29:12 -0800874 dialpadFragment.setUserVisibleHint(true);
linyuhc3968e62017-11-20 17:40:50 -0800875 }
wangqifd4c9f72018-03-08 18:21:50 -0800876 // RTT call screen doesn't show end call button inside dialpad, thus the space reserved for end
877 // call button should be removed.
878 dialpadFragment.setShouldShowEndCallSpace(didShowInCallScreen);
linyuhc3968e62017-11-20 17:40:50 -0800879 transaction.commitAllowingStateLoss();
880 dialpadFragmentManager.executePendingTransactions();
881
882 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, this);
883 updateNavigationBar(true /* isDialpadVisible */);
884 }
885
886 private void hideDialpadFragment() {
887 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
888 if (dialpadFragmentManager == null) {
889 return;
890 }
891
calderwoodrad5883872017-12-12 15:29:12 -0800892 DialpadFragment dialpadFragment = getDialpadFragment();
linyuhc3968e62017-11-20 17:40:50 -0800893 if (dialpadFragment != null) {
894 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
895 transaction.hide(dialpadFragment);
896 transaction.commitAllowingStateLoss();
897 dialpadFragmentManager.executePendingTransactions();
calderwoodrad5883872017-12-12 15:29:12 -0800898 dialpadFragment.setUserVisibleHint(false);
linyuhc3968e62017-11-20 17:40:50 -0800899 }
900 updateNavigationBar(false /* isDialpadVisible */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800901 }
902
903 public boolean isDialpadVisible() {
linyuh69a25062017-11-15 16:18:51 -0800904 DialpadFragment dialpadFragment = getDialpadFragment();
calderwoodraa584bcd2018-01-24 12:19:56 -0800905 return dialpadFragment != null
906 && dialpadFragment.isAdded()
907 && !dialpadFragment.isHidden()
908 && dialpadFragment.getView() != null
909 && dialpadFragment.getUserVisibleHint();
linyuh69a25062017-11-15 16:18:51 -0800910 }
911
linyuhc3968e62017-11-20 17:40:50 -0800912 /** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
linyuh69a25062017-11-15 16:18:51 -0800913 @Nullable
linyuhc3968e62017-11-20 17:40:50 -0800914 private DialpadFragment getDialpadFragment() {
linyuh69a25062017-11-15 16:18:51 -0800915 FragmentManager fragmentManager = getDialpadFragmentManager();
916 if (fragmentManager == null) {
917 return null;
918 }
linyuhc3968e62017-11-20 17:40:50 -0800919 return (DialpadFragment) fragmentManager.findFragmentByTag(Tags.DIALPAD_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800920 }
921
922 public void onForegroundCallChanged(DialerCall newForegroundCall) {
linyuh57b093b2017-11-17 14:32:32 -0800923 updateTaskDescription();
924
925 if (newForegroundCall == null || !didShowAnswerScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800926 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
linyuh57b093b2017-11-17 14:32:32 -0800927 updateWindowBackgroundColor(0 /* progress */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800928 }
929 }
930
linyuhc3968e62017-11-20 17:40:50 -0800931 private void updateTaskDescription() {
linyuh57b093b2017-11-17 14:32:32 -0800932 int color =
933 getResources().getBoolean(R.bool.is_layout_landscape)
934 ? ResourcesCompat.getColor(
935 getResources(), R.color.statusbar_background_color, getTheme())
936 : InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
937 setTaskDescription(
938 new TaskDescription(
939 getResources().getString(R.string.notification_ongoing_call), null /* icon */, color));
940 }
941
Eric Erfanianccca3152017-02-22 16:32:36 -0800942 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
943 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
944 @ColorInt int top;
945 @ColorInt int middle;
946 @ColorInt int bottom;
947 @ColorInt int gray = 0x66000000;
948
linyuh437ae952018-03-26 12:46:18 -0700949 if (isInMultiWindowMode()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800950 top = themeColorManager.getBackgroundColorSolid();
951 middle = themeColorManager.getBackgroundColorSolid();
952 bottom = themeColorManager.getBackgroundColorSolid();
953 } else {
954 top = themeColorManager.getBackgroundColorTop();
955 middle = themeColorManager.getBackgroundColorMiddle();
956 bottom = themeColorManager.getBackgroundColorBottom();
957 }
958
959 if (progress < 0) {
960 float correctedProgress = Math.abs(progress);
961 top = ColorUtils.blendARGB(top, gray, correctedProgress);
962 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
963 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
964 }
965
966 boolean backgroundDirty = false;
967 if (backgroundDrawable == null) {
968 backgroundDrawableColors = new int[] {top, middle, bottom};
969 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
970 backgroundDirty = true;
971 } else {
972 if (backgroundDrawableColors[0] != top) {
973 backgroundDrawableColors[0] = top;
974 backgroundDirty = true;
975 }
976 if (backgroundDrawableColors[1] != middle) {
977 backgroundDrawableColors[1] = middle;
978 backgroundDirty = true;
979 }
980 if (backgroundDrawableColors[2] != bottom) {
981 backgroundDrawableColors[2] = bottom;
982 backgroundDirty = true;
983 }
984 if (backgroundDirty) {
985 backgroundDrawable.setColors(backgroundDrawableColors);
986 }
987 }
988
989 if (backgroundDirty) {
990 getWindow().setBackgroundDrawable(backgroundDrawable);
991 }
992 }
993
994 public boolean isVisible() {
995 return isVisible;
996 }
997
998 public boolean getCallCardFragmentVisible() {
erfaniand05d8992018-03-20 19:42:26 -0700999 return didShowInCallScreen
1000 || didShowVideoCallScreen
1001 || didShowRttCallScreen
1002 || didShowSpeakEasyScreen;
Eric Erfanianccca3152017-02-22 16:32:36 -08001003 }
1004
1005 public void dismissKeyguard(boolean dismiss) {
linyuh9c327da2017-11-14 12:33:48 -08001006 if (dismissKeyguard == dismiss) {
1007 return;
1008 }
1009
1010 dismissKeyguard = dismiss;
1011 if (dismiss) {
1012 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
1013 } else {
1014 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
1015 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001016 }
1017
linyuhc3968e62017-11-20 17:40:50 -08001018 public void showDialogForPostCharWait(String callId, String chars) {
1019 if (isVisible) {
1020 PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
1021 fragment.show(getSupportFragmentManager(), Tags.POST_CHAR_DIALOG_FRAGMENT);
1022
1023 showPostCharWaitDialogOnResume = false;
1024 showPostCharWaitDialogCallId = null;
1025 showPostCharWaitDialogChars = null;
1026 } else {
1027 showPostCharWaitDialogOnResume = true;
1028 showPostCharWaitDialogCallId = callId;
1029 showPostCharWaitDialogChars = chars;
1030 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001031 }
1032
linyuh7b86f562017-11-16 11:24:09 -08001033 public void showDialogOrToastForDisconnectedCall(DisconnectMessage disconnectMessage) {
1034 LogUtil.i(
1035 "InCallActivity.showDialogOrToastForDisconnectedCall",
1036 "disconnect cause: %s",
1037 disconnectMessage);
1038
1039 if (disconnectMessage.dialog == null || isFinishing()) {
1040 return;
1041 }
1042
1043 dismissPendingDialogs();
1044
1045 // Show a toast if the app is in background when a dialog can't be visible.
1046 if (!isVisible()) {
1047 Toast.makeText(getApplicationContext(), disconnectMessage.toastMessage, Toast.LENGTH_LONG)
1048 .show();
1049 return;
1050 }
1051
1052 // Show the dialog.
linyuhc3968e62017-11-20 17:40:50 -08001053 errorDialog = disconnectMessage.dialog;
linyuh7b86f562017-11-16 11:24:09 -08001054 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("showErrorDialog");
1055 disconnectMessage.dialog.setOnDismissListener(
1056 dialogInterface -> {
1057 lock.release();
1058 onDialogDismissed();
1059 });
1060 disconnectMessage.dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
1061 disconnectMessage.dialog.show();
1062 }
1063
1064 private void onDialogDismissed() {
linyuhc3968e62017-11-20 17:40:50 -08001065 errorDialog = null;
linyuh7b86f562017-11-16 11:24:09 -08001066 CallList.getInstance().onErrorDialogDismissed();
Eric Erfanianccca3152017-02-22 16:32:36 -08001067 }
1068
1069 public void dismissPendingDialogs() {
linyuhc3968e62017-11-20 17:40:50 -08001070 LogUtil.enterBlock("InCallActivity.dismissPendingDialogs");
linyuhf99f6302017-11-15 11:23:51 -08001071
1072 if (!isVisible) {
1073 // Defer the dismissing action as the activity is not visible and onSaveInstanceState may have
1074 // been called.
Eric Erfanianccca3152017-02-22 16:32:36 -08001075 LogUtil.i(
1076 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
1077 needDismissPendingDialogs = true;
linyuhf99f6302017-11-15 11:23:51 -08001078 return;
Eric Erfanianccca3152017-02-22 16:32:36 -08001079 }
linyuhf99f6302017-11-15 11:23:51 -08001080
1081 // Dismiss the error dialog
linyuhf99f6302017-11-15 11:23:51 -08001082 if (errorDialog != null) {
1083 errorDialog.dismiss();
linyuhc3968e62017-11-20 17:40:50 -08001084 errorDialog = null;
linyuhf99f6302017-11-15 11:23:51 -08001085 }
1086
1087 // Dismiss the phone account selection dialog
linyuhf99f6302017-11-15 11:23:51 -08001088 if (selectPhoneAccountDialogFragment != null) {
1089 selectPhoneAccountDialogFragment.dismiss();
linyuhc3968e62017-11-20 17:40:50 -08001090 selectPhoneAccountDialogFragment = null;
linyuhf99f6302017-11-15 11:23:51 -08001091 }
1092
1093 // Dismiss the dialog for international call on WiFi
1094 InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
1095 (InternationalCallOnWifiDialogFragment)
linyuhc3968e62017-11-20 17:40:50 -08001096 getSupportFragmentManager().findFragmentByTag(Tags.INTERNATIONAL_CALL_ON_WIFI);
linyuhf99f6302017-11-15 11:23:51 -08001097 if (internationalCallOnWifiFragment != null) {
1098 internationalCallOnWifiFragment.dismiss();
1099 }
1100
1101 // Dismiss the answer screen
1102 AnswerScreen answerScreen = getAnswerScreen();
1103 if (answerScreen != null) {
1104 answerScreen.dismissPendingDialogs();
1105 }
1106
1107 needDismissPendingDialogs = false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001108 }
1109
linyuhc3968e62017-11-20 17:40:50 -08001110 private void enableInCallOrientationEventListener(boolean enable) {
linyuh69a25062017-11-15 16:18:51 -08001111 if (enable) {
1112 inCallOrientationEventListener.enable(true /* notifyDeviceOrientationChange */);
1113 } else {
1114 inCallOrientationEventListener.disable();
1115 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001116 }
1117
1118 public void setExcludeFromRecents(boolean exclude) {
linyuhc3968e62017-11-20 17:40:50 -08001119 int taskId = getTaskId();
1120
1121 List<AppTask> tasks = getSystemService(ActivityManager.class).getAppTasks();
1122 for (AppTask task : tasks) {
1123 try {
1124 if (task.getTaskInfo().id == taskId) {
1125 task.setExcludeFromRecents(exclude);
1126 }
1127 } catch (RuntimeException e) {
1128 LogUtil.e("InCallActivity.setExcludeFromRecents", "RuntimeException:\n%s", e);
1129 }
1130 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001131 }
1132
Eric Erfanianccca3152017-02-22 16:32:36 -08001133 @Nullable
1134 public FragmentManager getDialpadFragmentManager() {
wangqifd4c9f72018-03-08 18:21:50 -08001135 InCallScreen inCallScreen = getInCallOrRttCallScreen();
Eric Erfanianccca3152017-02-22 16:32:36 -08001136 if (inCallScreen != null) {
1137 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
1138 }
1139 return null;
1140 }
1141
1142 public int getDialpadContainerId() {
wangqifd4c9f72018-03-08 18:21:50 -08001143 return getInCallOrRttCallScreen().getAnswerAndDialpadContainerResourceId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001144 }
1145
1146 @Override
1147 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
1148 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
1149 if (call == null) {
1150 // This is a work around for a bug where we attempt to create a new delegate after the call
1151 // has already been removed. An example of when this can happen is:
1152 // 1. incoming video call in landscape mode
1153 // 2. remote party hangs up
1154 // 3. activity switches from landscape to portrait
1155 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
1156 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
1157 // because this new state is transient and the activity will be destroyed soon.
1158 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
1159 return new AnswerScreenPresenterStub();
1160 } else {
1161 return new AnswerScreenPresenter(
1162 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
1163 }
1164 }
1165
1166 @Override
1167 public InCallScreenDelegate newInCallScreenDelegate() {
1168 return new CallCardPresenter(this);
1169 }
1170
1171 @Override
1172 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
1173 return new CallButtonPresenter(this);
1174 }
1175
1176 @Override
Eric Erfanian90508232017-03-24 09:31:16 -07001177 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
1178 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
1179 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
1180 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
1181 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001182 return new VideoCallPresenter();
1183 }
1184
1185 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001186 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -08001187 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001188 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001189 }
1190
linyuh7b86f562017-11-16 11:24:09 -08001191 public void showDialogOrToastForWifiHandoverFailure(DialerCall call) {
1192 if (call.showWifiHandoverAlertAsToast()) {
1193 Toast.makeText(this, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
1194 .show();
1195 return;
1196 }
1197
1198 dismissPendingDialogs();
1199
1200 AlertDialog.Builder builder =
1201 new AlertDialog.Builder(this).setTitle(R.string.video_call_lte_to_wifi_failed_title);
1202
1203 // This allows us to use the theme of the dialog instead of the activity
1204 View dialogCheckBoxView =
1205 View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null /* root */);
1206 CheckBox wifiHandoverFailureCheckbox =
1207 (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
1208 wifiHandoverFailureCheckbox.setChecked(false);
1209
1210 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("WifiFailedDialog");
linyuhc3968e62017-11-20 17:40:50 -08001211 errorDialog =
linyuh7b86f562017-11-16 11:24:09 -08001212 builder
1213 .setView(dialogCheckBoxView)
1214 .setMessage(R.string.video_call_lte_to_wifi_failed_message)
1215 .setOnCancelListener(dialogInterface -> onDialogDismissed())
1216 .setPositiveButton(
1217 android.R.string.ok,
1218 (dialogInterface, id) -> {
1219 call.setDoNotShowDialogForHandoffToWifiFailure(
1220 wifiHandoverFailureCheckbox.isChecked());
1221 dialogInterface.cancel();
1222 onDialogDismissed();
1223 })
1224 .setOnDismissListener(dialogInterface -> lock.release())
1225 .create();
linyuh7b86f562017-11-16 11:24:09 -08001226 errorDialog.show();
Eric Erfanianccca3152017-02-22 16:32:36 -08001227 }
1228
linyuh7b86f562017-11-16 11:24:09 -08001229 public void showDialogForInternationalCallOnWifi(@NonNull DialerCall call) {
linyuh7b86f562017-11-16 11:24:09 -08001230 InternationalCallOnWifiDialogFragment fragment =
yuegb47528e2018-04-24 12:12:57 -07001231 InternationalCallOnWifiDialogFragment.newInstance(call.getId());
linyuhc3968e62017-11-20 17:40:50 -08001232 fragment.show(getSupportFragmentManager(), Tags.INTERNATIONAL_CALL_ON_WIFI);
Eric Erfanianc857f902017-05-15 14:05:33 -07001233 }
1234
wangqibc28ea72018-04-02 16:23:00 -07001235 public void showDialogForRttRequest(DialerCall call, int rttRequestId) {
1236 LogUtil.enterBlock("InCallActivity.showDialogForRttRequest");
1237 DialogFragment fragment = RttRequestDialogFragment.newInstance(call.getId(), rttRequestId);
1238 fragment.show(getSupportFragmentManager(), Tags.RTT_REQUEST_DIALOG);
1239 }
1240
Eric Erfanian938468d2017-10-24 14:05:52 -07001241 @Override
1242 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
1243 super.onMultiWindowModeChanged(isInMultiWindowMode);
linyuh57b093b2017-11-17 14:32:32 -08001244 updateNavigationBar(isDialpadVisible());
1245 }
1246
linyuhc3968e62017-11-20 17:40:50 -08001247 private void updateNavigationBar(boolean isDialpadVisible) {
linyuh437ae952018-03-26 12:46:18 -07001248 if (isInMultiWindowMode()) {
linyuh57b093b2017-11-17 14:32:32 -08001249 return;
1250 }
1251
1252 View navigationBarBackground = getWindow().findViewById(R.id.navigation_bar_background);
1253 if (navigationBarBackground != null) {
1254 navigationBarBackground.setVisibility(isDialpadVisible ? View.VISIBLE : View.GONE);
Eric Erfanian938468d2017-10-24 14:05:52 -07001255 }
1256 }
1257
Eric Erfanianccca3152017-02-22 16:32:36 -08001258 public void setAllowOrientationChange(boolean allowOrientationChange) {
wangqi9982f0d2017-10-11 17:46:07 -07001259 if (this.allowOrientationChange == allowOrientationChange) {
1260 return;
1261 }
1262 this.allowOrientationChange = allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -08001263 if (!allowOrientationChange) {
1264 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
1265 } else {
1266 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
1267 }
1268 enableInCallOrientationEventListener(allowOrientationChange);
1269 }
1270
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001271 public void hideMainInCallFragment() {
linyuhc3968e62017-11-20 17:40:50 -08001272 LogUtil.enterBlock("InCallActivity.hideMainInCallFragment");
1273 if (getCallCardFragmentVisible()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001274 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1275 hideInCallScreenFragment(transaction);
1276 hideVideoCallScreenFragment(transaction);
1277 transaction.commitAllowingStateLoss();
1278 getSupportFragmentManager().executePendingTransactions();
1279 }
1280 }
1281
1282 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001283 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -08001284 // If the activity's onStart method hasn't been called yet then defer doing any work.
1285 if (!isVisible) {
1286 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001287 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001288 return;
1289 }
1290
1291 // Don't let this be reentrant.
1292 if (isInShowMainInCallFragment) {
1293 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001294 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001295 return;
1296 }
1297
1298 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001299 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
1300 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
wangqi219b8702018-02-13 09:34:41 -08001301 ShouldShowUiResult shouldShowRttUi = getShouldShowRttUi();
erfaniand05d8992018-03-20 19:42:26 -07001302 ShouldShowUiResult shouldShowSpeakEasyUi = getShouldShowSpeakEasyUi();
Eric Erfanianccca3152017-02-22 16:32:36 -08001303 LogUtil.i(
1304 "InCallActivity.showMainInCallFragment",
wangqi219b8702018-02-13 09:34:41 -08001305 "shouldShowAnswerUi: %b, shouldShowRttUi: %b, shouldShowVideoUi: %b "
1306 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowRttCallScreen: %b, "
erfaniand05d8992018-03-20 19:42:26 -07001307 + "didShowVideoCallScreen: %b"
1308 + "didShowSpeakEasyScreen: %b",
Eric Erfanianccca3152017-02-22 16:32:36 -08001309 shouldShowAnswerUi.shouldShow,
wangqi219b8702018-02-13 09:34:41 -08001310 shouldShowRttUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -07001311 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -08001312 didShowAnswerScreen,
1313 didShowInCallScreen,
wangqi219b8702018-02-13 09:34:41 -08001314 didShowRttCallScreen,
erfaniand05d8992018-03-20 19:42:26 -07001315 didShowVideoCallScreen,
1316 didShowSpeakEasyScreen);
Eric Erfanianccca3152017-02-22 16:32:36 -08001317 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001318 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -08001319
1320 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
wangqi219b8702018-02-13 09:34:41 -08001321 boolean didChange;
Eric Erfanianccca3152017-02-22 16:32:36 -08001322 if (shouldShowAnswerUi.shouldShow) {
wangqi219b8702018-02-13 09:34:41 -08001323 didChange = hideInCallScreenFragment(transaction);
1324 didChange |= hideVideoCallScreenFragment(transaction);
1325 didChange |= hideRttCallScreenFragment(transaction);
erfaniand05d8992018-03-20 19:42:26 -07001326 didChange |= hideSpeakEasyFragment(transaction);
wangqi219b8702018-02-13 09:34:41 -08001327 didChange |= showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001328 } else if (shouldShowVideoUi.shouldShow) {
wangqi219b8702018-02-13 09:34:41 -08001329 didChange = hideInCallScreenFragment(transaction);
1330 didChange |= showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
1331 didChange |= hideRttCallScreenFragment(transaction);
erfaniand05d8992018-03-20 19:42:26 -07001332 didChange |= hideSpeakEasyFragment(transaction);
wangqi219b8702018-02-13 09:34:41 -08001333 didChange |= hideAnswerScreenFragment(transaction);
1334 } else if (shouldShowRttUi.shouldShow) {
1335 didChange = hideInCallScreenFragment(transaction);
1336 didChange |= hideVideoCallScreenFragment(transaction);
1337 didChange |= hideAnswerScreenFragment(transaction);
erfaniand05d8992018-03-20 19:42:26 -07001338 didChange |= hideSpeakEasyFragment(transaction);
wangqi219b8702018-02-13 09:34:41 -08001339 didChange |= showRttCallScreenFragment(transaction, shouldShowRttUi.call);
erfaniand05d8992018-03-20 19:42:26 -07001340 } else if (shouldShowSpeakEasyUi.shouldShow) {
1341 didChange = hideInCallScreenFragment(transaction);
1342 didChange |= hideVideoCallScreenFragment(transaction);
1343 didChange |= hideAnswerScreenFragment(transaction);
1344 didChange |= hideRttCallScreenFragment(transaction);
1345 didChange |= showSpeakEasyFragment(transaction, shouldShowSpeakEasyUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001346 } else {
wangqi219b8702018-02-13 09:34:41 -08001347 didChange = showInCallScreenFragment(transaction);
1348 didChange |= hideVideoCallScreenFragment(transaction);
1349 didChange |= hideRttCallScreenFragment(transaction);
erfaniand05d8992018-03-20 19:42:26 -07001350 didChange |= hideSpeakEasyFragment(transaction);
wangqi219b8702018-02-13 09:34:41 -08001351 didChange |= hideAnswerScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -08001352 }
1353
wangqi219b8702018-02-13 09:34:41 -08001354 if (didChange) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001355 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -08001356 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001357 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001358 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1359 }
1360 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001361 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001362 }
1363
erfaniand05d8992018-03-20 19:42:26 -07001364 private boolean showSpeakEasyFragment(FragmentTransaction transaction, DialerCall call) {
1365
erfaniand05d8992018-03-20 19:42:26 -07001366 if (didShowSpeakEasyScreen) {
erfanian612d13a2018-04-04 15:27:57 -07001367 if (lastShownSpeakEasyScreenUniqueCallid.equals(call.getUniqueCallId())) {
erfanian12243de2018-04-17 12:17:08 -07001368 LogUtil.i("InCallActivity.showSpeakEasyFragment", "found existing fragment");
erfanian612d13a2018-04-04 15:27:57 -07001369 return false;
1370 }
1371 hideSpeakEasyFragment(transaction);
erfanian12243de2018-04-17 12:17:08 -07001372 LogUtil.i("InCallActivity.showSpeakEasyFragment", "hid existing fragment");
erfaniand05d8992018-03-20 19:42:26 -07001373 }
1374
1375 Optional<Fragment> speakEasyFragment = speakEasyCallManager.getSpeakEasyFragment(call);
1376 if (speakEasyFragment.isPresent()) {
1377 transaction.add(R.id.main, speakEasyFragment.get(), Tags.SPEAK_EASY_SCREEN);
1378 didShowSpeakEasyScreen = true;
erfanian612d13a2018-04-04 15:27:57 -07001379 lastShownSpeakEasyScreenUniqueCallid = call.getUniqueCallId();
erfanian12243de2018-04-17 12:17:08 -07001380 LogUtil.i(
1381 "InCallActivity.showSpeakEasyFragment",
1382 "set fragment for call %s",
1383 lastShownSpeakEasyScreenUniqueCallid);
erfaniand05d8992018-03-20 19:42:26 -07001384 return true;
1385 }
1386 return false;
1387 }
1388
1389 private Fragment getSpeakEasyScreen() {
1390 return getSupportFragmentManager().findFragmentByTag(Tags.SPEAK_EASY_SCREEN);
1391 }
1392
1393 private boolean hideSpeakEasyFragment(FragmentTransaction transaction) {
1394 if (!didShowSpeakEasyScreen) {
1395 return false;
1396 }
1397
1398 Fragment speakEasyFragment = getSpeakEasyScreen();
1399
1400 if (speakEasyFragment != null) {
1401 transaction.remove(speakEasyFragment);
1402 didShowSpeakEasyScreen = false;
1403 return true;
1404 }
1405 return false;
1406 }
1407
erfanian12243de2018-04-17 12:17:08 -07001408 @VisibleForTesting
erfaniand05d8992018-03-20 19:42:26 -07001409 public void setSpeakEasyCallManager(SpeakEasyCallManager speakEasyCallManager) {
erfanian87dbb622018-04-04 15:43:00 -07001410 this.speakEasyCallManager = speakEasyCallManager;
erfaniand05d8992018-03-20 19:42:26 -07001411 }
1412
erfanian12243de2018-04-17 12:17:08 -07001413 @Nullable
erfaniand05d8992018-03-20 19:42:26 -07001414 public SpeakEasyCallManager getSpeakEasyCallManager() {
erfanian12243de2018-04-17 12:17:08 -07001415 if (this.speakEasyCallManager == null) {
1416 this.speakEasyCallManager = InCallPresenter.getInstance().getSpeakEasyCallManager();
1417 }
erfaniand05d8992018-03-20 19:42:26 -07001418 return speakEasyCallManager;
1419 }
1420
1421 private ShouldShowUiResult getShouldShowSpeakEasyUi() {
1422 SpeakEasyCallManager speakEasyCallManager = getSpeakEasyCallManager();
1423
1424 if (speakEasyCallManager == null) {
1425 return new ShouldShowUiResult(false, null);
1426 }
1427
erfanian3bb7cb62018-04-11 09:01:15 -07001428 DialerCall call =
1429 CallList.getInstance().getIncomingCall() != null
1430 ? CallList.getInstance().getIncomingCall()
1431 : CallList.getInstance().getActiveCall();
erfaniand05d8992018-03-20 19:42:26 -07001432
1433 if (call == null) {
1434 return new ShouldShowUiResult(false, call);
1435 }
1436
1437 if (!call.isSpeakEasyCall() || !call.isSpeakEasyEligible()) {
1438 return new ShouldShowUiResult(false, call);
1439 }
1440
1441 Optional<Fragment> speakEasyFragment = speakEasyCallManager.getSpeakEasyFragment(call);
1442
1443 if (!speakEasyFragment.isPresent()) {
1444 return new ShouldShowUiResult(false, call);
1445 }
1446 return new ShouldShowUiResult(true, call);
1447 }
1448
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001449 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001450 DialerCall call = CallList.getInstance().getIncomingCall();
erfanian3bb7cb62018-04-11 09:01:15 -07001451 if (call != null && !call.isSpeakEasyCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001452 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001453 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001454 }
1455
1456 call = CallList.getInstance().getVideoUpgradeRequestCall();
1457 if (call != null) {
1458 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001459 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001460 }
1461
1462 // Check if we're showing the answer screen and the call is disconnected. If this condition is
1463 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
1464 // the user rejects an incoming call.
1465 call = CallList.getInstance().getFirstCall();
1466 if (call == null) {
1467 call = CallList.getInstance().getBackgroundCall();
1468 }
1469 if (didShowAnswerScreen && (call == null || call.getState() == State.DISCONNECTED)) {
1470 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001471 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001472 }
1473
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001474 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001475 }
1476
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001477 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001478 DialerCall call = CallList.getInstance().getFirstCall();
1479 if (call == null) {
1480 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001481 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001482 }
1483
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001484 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001485 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001486 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001487 }
1488
linyuh8fbecce2017-12-18 13:53:09 -08001489 if (call.hasSentVideoUpgradeRequest() || call.hasReceivedVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001490 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001491 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001492 }
1493
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001494 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001495 }
1496
wangqi219b8702018-02-13 09:34:41 -08001497 private static ShouldShowUiResult getShouldShowRttUi() {
1498 DialerCall call = CallList.getInstance().getFirstCall();
1499 if (call == null) {
1500 LogUtil.i("InCallActivity.getShouldShowRttUi", "null call");
1501 return new ShouldShowUiResult(false, null);
1502 }
1503
wangqif6be6172018-03-30 15:57:56 -07001504 if (call.isActiveRttCall()) {
wangqi219b8702018-02-13 09:34:41 -08001505 LogUtil.i("InCallActivity.getShouldShowRttUi", "found rtt call");
1506 return new ShouldShowUiResult(true, call);
1507 }
1508
1509 if (call.hasSentRttUpgradeRequest()) {
1510 LogUtil.i("InCallActivity.getShouldShowRttUi", "upgrading to rtt");
1511 return new ShouldShowUiResult(true, call);
1512 }
1513
1514 return new ShouldShowUiResult(false, null);
1515 }
1516
Eric Erfanianccca3152017-02-22 16:32:36 -08001517 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
1518 // When rejecting a call the active call can become null in which case we should continue
1519 // showing the answer screen.
1520 if (didShowAnswerScreen && call == null) {
1521 return false;
1522 }
1523
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001524 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
1525
1526 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -08001527
1528 // Check if we're already showing an answer screen for this call.
1529 if (didShowAnswerScreen) {
1530 AnswerScreen answerScreen = getAnswerScreen();
1531 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001532 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -07001533 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
1534 && !answerScreen.isActionTimeout()) {
1535 LogUtil.d(
1536 "InCallActivity.showAnswerScreenFragment",
1537 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -08001538 return false;
1539 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001540 if (answerScreen.isActionTimeout()) {
1541 LogUtil.i(
1542 "InCallActivity.showAnswerScreenFragment",
1543 "answer fragment exists but has been accepted/rejected and timed out");
1544 } else {
1545 LogUtil.i(
1546 "InCallActivity.showAnswerScreenFragment",
1547 "answer fragment exists but arguments do not match");
1548 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001549 hideAnswerScreenFragment(transaction);
1550 }
1551
1552 // Show a new answer screen.
1553 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -07001554 AnswerBindings.createAnswerScreen(
1555 call.getId(),
wangqif6be6172018-03-30 15:57:56 -07001556 call.isActiveRttCall(),
Eric Erfanianfc37b022017-03-21 10:11:17 -07001557 call.isVideoCall(),
1558 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -07001559 call.getVideoTech().isSelfManagedCamera(),
1560 shouldAllowAnswerAndRelease(call),
erfaniand05d8992018-03-20 19:42:26 -07001561 CallList.getInstance().getBackgroundCall() != null,
1562 call.isSpeakEasyEligible());
linyuhc3968e62017-11-20 17:40:50 -08001563 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), Tags.ANSWER_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001564
1565 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
1566 didShowAnswerScreen = true;
1567 return true;
1568 }
1569
Eric Erfanian90508232017-03-24 09:31:16 -07001570 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
1571 if (CallList.getInstance().getActiveCall() == null) {
1572 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
1573 return false;
1574 }
1575 if (getSystemService(TelephonyManager.class).getPhoneType()
1576 == TelephonyManager.PHONE_TYPE_CDMA) {
1577 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
1578 return false;
1579 }
1580 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
1581 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
1582 return false;
1583 }
linyuhc3968e62017-11-20 17:40:50 -08001584 if (!ConfigProviderBindings.get(this)
1585 .getBoolean(ConfigNames.ANSWER_AND_RELEASE_ENABLED, true)) {
Eric Erfanian90508232017-03-24 09:31:16 -07001586 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
1587 return false;
1588 }
1589
1590 return true;
1591 }
1592
Eric Erfanianccca3152017-02-22 16:32:36 -08001593 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
1594 if (!didShowAnswerScreen) {
1595 return false;
1596 }
1597 AnswerScreen answerScreen = getAnswerScreen();
1598 if (answerScreen != null) {
1599 transaction.remove(answerScreen.getAnswerScreenFragment());
1600 }
1601
1602 didShowAnswerScreen = false;
1603 return true;
1604 }
1605
1606 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
1607 if (didShowInCallScreen) {
1608 return false;
1609 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001610 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
linyuhc3968e62017-11-20 17:40:50 -08001611 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), Tags.IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001612 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1613 didShowInCallScreen = true;
1614 return true;
1615 }
1616
1617 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
1618 if (!didShowInCallScreen) {
1619 return false;
1620 }
1621 InCallScreen inCallScreen = getInCallScreen();
1622 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001623 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -08001624 }
1625 didShowInCallScreen = false;
1626 return true;
1627 }
1628
wangqi219b8702018-02-13 09:34:41 -08001629 private boolean showRttCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
1630 if (didShowRttCallScreen) {
1631 // This shouldn't happen since only one RTT call is allow at same time.
1632 if (!getRttCallScreen().getCallId().equals(call.getId())) {
1633 LogUtil.e("InCallActivity.showRttCallScreenFragment", "RTT call id doesn't match");
1634 }
1635 return false;
1636 }
1637 RttCallScreen rttCallScreen = RttBindings.createRttCallScreen(call.getId());
1638 transaction.add(R.id.main, rttCallScreen.getRttCallScreenFragment(), Tags.RTT_CALL_SCREEN);
1639 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1640 didShowRttCallScreen = true;
1641 return true;
1642 }
1643
1644 private boolean hideRttCallScreenFragment(FragmentTransaction transaction) {
1645 if (!didShowRttCallScreen) {
1646 return false;
1647 }
1648 RttCallScreen rttCallScreen = getRttCallScreen();
1649 if (rttCallScreen != null) {
1650 transaction.remove(rttCallScreen.getRttCallScreenFragment());
1651 }
1652 didShowRttCallScreen = false;
1653 return true;
1654 }
1655
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001656 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001657 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001658 VideoCallScreen videoCallScreen = getVideoCallScreen();
1659 if (videoCallScreen.getCallId().equals(call.getId())) {
1660 return false;
1661 }
1662 LogUtil.i(
1663 "InCallActivity.showVideoCallScreenFragment",
1664 "video call fragment exists but arguments do not match");
1665 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -08001666 }
1667
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001668 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
1669
Eric Erfanian90508232017-03-24 09:31:16 -07001670 VideoCallScreen videoCallScreen =
1671 VideoBindings.createVideoCallScreen(
1672 call.getId(), call.getVideoTech().shouldUseSurfaceView());
linyuhc3968e62017-11-20 17:40:50 -08001673 transaction.add(
1674 R.id.main, videoCallScreen.getVideoCallScreenFragment(), Tags.VIDEO_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001675
1676 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1677 didShowVideoCallScreen = true;
1678 return true;
1679 }
1680
1681 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
1682 if (!didShowVideoCallScreen) {
1683 return false;
1684 }
1685 VideoCallScreen videoCallScreen = getVideoCallScreen();
1686 if (videoCallScreen != null) {
1687 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
1688 }
1689 didShowVideoCallScreen = false;
1690 return true;
1691 }
1692
linyuhc3968e62017-11-20 17:40:50 -08001693 private AnswerScreen getAnswerScreen() {
1694 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(Tags.ANSWER_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001695 }
1696
linyuhc3968e62017-11-20 17:40:50 -08001697 private InCallScreen getInCallScreen() {
1698 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001699 }
1700
linyuhc3968e62017-11-20 17:40:50 -08001701 private VideoCallScreen getVideoCallScreen() {
1702 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.VIDEO_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001703 }
1704
wangqi219b8702018-02-13 09:34:41 -08001705 private RttCallScreen getRttCallScreen() {
1706 return (RttCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.RTT_CALL_SCREEN);
1707 }
1708
wangqifd4c9f72018-03-08 18:21:50 -08001709 private InCallScreen getInCallOrRttCallScreen() {
1710 InCallScreen inCallScreen = null;
1711 if (didShowInCallScreen) {
1712 inCallScreen = getInCallScreen();
1713 }
1714 if (didShowRttCallScreen) {
1715 inCallScreen = getRttCallScreen();
1716 }
1717 return inCallScreen;
1718 }
1719
Eric Erfanianccca3152017-02-22 16:32:36 -08001720 @Override
1721 public void onPseudoScreenStateChanged(boolean isOn) {
1722 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
1723 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
1724 }
1725
1726 /**
1727 * For some touch related issue, turning off the screen can be faked by drawing a black view over
1728 * the activity. All touch events started when the screen is "off" is rejected.
1729 *
1730 * @see PseudoScreenState
1731 */
1732 @Override
1733 public boolean dispatchTouchEvent(MotionEvent event) {
1734 // Reject any gesture that started when the screen is in the fake off state.
1735 if (touchDownWhenPseudoScreenOff) {
1736 if (event.getAction() == MotionEvent.ACTION_UP) {
1737 touchDownWhenPseudoScreenOff = false;
1738 }
1739 return true;
1740 }
1741 // Reject all touch event when the screen is in the fake off state.
1742 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
1743 if (event.getAction() == MotionEvent.ACTION_DOWN) {
1744 touchDownWhenPseudoScreenOff = true;
1745 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
1746 }
1747 return true;
1748 }
1749 return super.dispatchTouchEvent(event);
1750 }
1751
wangqi219b8702018-02-13 09:34:41 -08001752 @Override
1753 public RttCallScreenDelegate newRttCallScreenDelegate(RttCallScreen videoCallScreen) {
1754 return new RttCallPresenter();
1755 }
1756
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001757 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -08001758 public final boolean shouldShow;
1759 public final DialerCall call;
1760
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001761 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001762 this.shouldShow = shouldShow;
1763 this.call = call;
1764 }
1765 }
linyuhc3968e62017-11-20 17:40:50 -08001766
1767 private static final class IntentExtraNames {
1768 static final String FOR_FULL_SCREEN = "InCallActivity.for_full_screen_intent";
1769 static final String NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
1770 static final String SHOW_DIALPAD = "InCallActivity.show_dialpad";
1771 }
1772
1773 private static final class KeysForSavedInstance {
1774 static final String DIALPAD_TEXT = "InCallActivity.dialpad_text";
1775 static final String DID_SHOW_ANSWER_SCREEN = "did_show_answer_screen";
1776 static final String DID_SHOW_IN_CALL_SCREEN = "did_show_in_call_screen";
1777 static final String DID_SHOW_VIDEO_CALL_SCREEN = "did_show_video_call_screen";
wangqi153af2f2018-02-15 16:21:49 -08001778 static final String DID_SHOW_RTT_CALL_SCREEN = "did_show_rtt_call_screen";
erfaniand05d8992018-03-20 19:42:26 -07001779 static final String DID_SHOW_SPEAK_EASY_SCREEN = "did_show_speak_easy_screen";
linyuhc3968e62017-11-20 17:40:50 -08001780 }
1781
1782 /** Request codes for pending intents. */
1783 public static final class PendingIntentRequestCodes {
1784 static final int NON_FULL_SCREEN = 0;
1785 static final int FULL_SCREEN = 1;
1786 static final int BUBBLE = 2;
1787 }
1788
1789 private static final class Tags {
1790 static final String ANSWER_SCREEN = "tag_answer_screen";
1791 static final String DIALPAD_FRAGMENT = "tag_dialpad_fragment";
1792 static final String IN_CALL_SCREEN = "tag_in_call_screen";
1793 static final String INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi";
1794 static final String SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment";
1795 static final String VIDEO_CALL_SCREEN = "tag_video_call_screen";
wangqi219b8702018-02-13 09:34:41 -08001796 static final String RTT_CALL_SCREEN = "tag_rtt_call_screen";
linyuhc3968e62017-11-20 17:40:50 -08001797 static final String POST_CHAR_DIALOG_FRAGMENT = "tag_post_char_dialog_fragment";
erfaniand05d8992018-03-20 19:42:26 -07001798 static final String SPEAK_EASY_SCREEN = "tag_speak_easy_screen";
wangqibc28ea72018-04-02 16:23:00 -07001799 static final String RTT_REQUEST_DIALOG = "tag_rtt_request_dialog";
linyuhc3968e62017-11-20 17:40:50 -08001800 }
1801
1802 private static final class ConfigNames {
1803 static final String ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
1804 }
1805
linyuhc3968e62017-11-20 17:40:50 -08001806 private static final class SelectPhoneAccountListener
1807 extends SelectPhoneAccountDialogFragment.SelectPhoneAccountListener {
1808 private static final String TAG = SelectPhoneAccountListener.class.getCanonicalName();
1809
twyen73a74c32018-03-07 12:12:24 -08001810 private final Context appContext;
1811
1812 SelectPhoneAccountListener(Context appContext) {
1813 this.appContext = appContext;
1814 }
1815
linyuhc3968e62017-11-20 17:40:50 -08001816 @Override
1817 public void onPhoneAccountSelected(
1818 PhoneAccountHandle selectedAccountHandle, boolean setDefault, String callId) {
1819 DialerCall call = CallList.getInstance().getCallById(callId);
1820 LogUtil.i(TAG, "Phone account select with call:\n%s", call);
1821
1822 if (call != null) {
twyen73a74c32018-03-07 12:12:24 -08001823 call.phoneAccountSelected(selectedAccountHandle, false);
1824 if (call.getPreferredAccountRecorder() != null) {
1825 call.getPreferredAccountRecorder().record(appContext, selectedAccountHandle, setDefault);
1826 }
linyuhc3968e62017-11-20 17:40:50 -08001827 }
1828 }
1829
1830 @Override
1831 public void onDialogDismissed(String callId) {
1832 DialerCall call = CallList.getInstance().getCallById(callId);
1833 LogUtil.i(TAG, "Disconnecting call:\n%s" + call);
1834
1835 if (call != null) {
1836 call.disconnect();
1837 }
1838 }
1839 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001840}