blob: 0ee98c25c033bc23be5e09f31fe81f95b00bc2c7 [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;
linyuh57b093b2017-11-17 14:32:32 -080086import com.android.incallui.call.TelecomAdapter;
wangqibb94ca62018-04-27 14:34:04 -070087import com.android.incallui.call.state.DialerCallState;
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;
linyuhc3968e62017-11-20 17:40:50 -0800148 private boolean allowOrientationChange;
149 private boolean animateDialpadOnShow;
Eric Erfanianccca3152017-02-22 16:32:36 -0800150 private boolean didShowAnswerScreen;
151 private boolean didShowInCallScreen;
152 private boolean didShowVideoCallScreen;
wangqi219b8702018-02-13 09:34:41 -0800153 private boolean didShowRttCallScreen;
erfaniand05d8992018-03-20 19:42:26 -0700154 private boolean didShowSpeakEasyScreen;
erfanian612d13a2018-04-04 15:27:57 -0700155 private String lastShownSpeakEasyScreenUniqueCallid = "";
linyuh9c327da2017-11-14 12:33:48 -0800156 private boolean dismissKeyguard;
Eric Erfanianccca3152017-02-22 16:32:36 -0800157 private boolean isInShowMainInCallFragment;
linyuhc3968e62017-11-20 17:40:50 -0800158 private boolean isRecreating; // whether the activity is going to be recreated
159 private boolean isVisible;
Eric Erfanianccca3152017-02-22 16:32:36 -0800160 private boolean needDismissPendingDialogs;
linyuhc3968e62017-11-20 17:40:50 -0800161 private boolean touchDownWhenPseudoScreenOff;
162 private int[] backgroundDrawableColors;
163 @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
erfaniand05d8992018-03-20 19:42:26 -0700164 private SpeakEasyCallManager speakEasyCallManager;
Eric Erfanianccca3152017-02-22 16:32:36 -0800165
166 public static Intent getIntent(
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700167 Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800168 Intent intent = new Intent(Intent.ACTION_MAIN, null);
169 intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
170 intent.setClass(context, InCallActivity.class);
linyuhc3968e62017-11-20 17:40:50 -0800171 if (showDialpad) {
172 intent.putExtra(IntentExtraNames.SHOW_DIALPAD, true);
173 }
174 intent.putExtra(IntentExtraNames.NEW_OUTGOING_CALL, newOutgoingCall);
175 intent.putExtra(IntentExtraNames.FOR_FULL_SCREEN, isForFullScreen);
Eric Erfanianccca3152017-02-22 16:32:36 -0800176 return intent;
177 }
178
179 @Override
180 protected void onResumeFragments() {
181 super.onResumeFragments();
182 if (needDismissPendingDialogs) {
183 dismissPendingDialogs();
184 }
185 }
186
187 @Override
linyuhc3968e62017-11-20 17:40:50 -0800188 protected void onCreate(Bundle bundle) {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700189 Trace.beginSection("InCallActivity.onCreate");
linyuhc3968e62017-11-20 17:40:50 -0800190 super.onCreate(bundle);
Eric Erfanianccca3152017-02-22 16:32:36 -0800191
twyena1723252018-04-24 17:02:57 -0700192 preferredAccountWorkerResultListener =
193 DialerExecutorComponent.get(this)
194 .createUiListener(getFragmentManager(), "preferredAccountWorkerResultListener");
195
twyen73a74c32018-03-07 12:12:24 -0800196 selectPhoneAccountListener = new SelectPhoneAccountListener(getApplicationContext());
197
linyuhc3968e62017-11-20 17:40:50 -0800198 if (bundle != null) {
199 didShowAnswerScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN);
200 didShowInCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN);
201 didShowVideoCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN);
wangqi153af2f2018-02-15 16:21:49 -0800202 didShowRttCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -0800203 }
204
linyuhc3968e62017-11-20 17:40:50 -0800205 setWindowFlags();
206 setContentView(R.layout.incall_screen);
207 internalResolveIntent(getIntent());
208
209 boolean isLandscape =
210 getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
211 boolean isRtl = ViewUtil.isRtl();
212 if (isLandscape) {
213 dialpadSlideInAnimation =
214 AnimationUtils.loadAnimation(
215 this, isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
216 dialpadSlideOutAnimation =
217 AnimationUtils.loadAnimation(
218 this, isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
219 } else {
220 dialpadSlideInAnimation = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
221 dialpadSlideOutAnimation =
222 AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
223 }
224 dialpadSlideInAnimation.setInterpolator(AnimUtils.EASE_IN);
225 dialpadSlideOutAnimation.setInterpolator(AnimUtils.EASE_OUT);
226 dialpadSlideOutAnimation.setAnimationListener(
227 new AnimationListenerAdapter() {
228 @Override
229 public void onAnimationEnd(Animation animation) {
230 hideDialpadFragment();
231 }
232 });
233
234 if (bundle != null && showDialpadRequest == DIALPAD_REQUEST_NONE) {
235 // If the dialpad was shown before, set related variables so that it can be shown and
236 // populated with the previous DTMF text during onResume().
237 if (bundle.containsKey(IntentExtraNames.SHOW_DIALPAD)) {
238 boolean showDialpad = bundle.getBoolean(IntentExtraNames.SHOW_DIALPAD);
239 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
240 animateDialpadOnShow = false;
241 }
242 dtmfTextToPrepopulate = bundle.getString(KeysForSavedInstance.DIALPAD_TEXT);
243
244 SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment =
245 (SelectPhoneAccountDialogFragment)
246 getFragmentManager().findFragmentByTag(Tags.SELECT_ACCOUNT_FRAGMENT);
247 if (selectPhoneAccountDialogFragment != null) {
248 selectPhoneAccountDialogFragment.setListener(selectPhoneAccountListener);
249 }
250 }
251
linyuh69a25062017-11-15 16:18:51 -0800252 inCallOrientationEventListener = new InCallOrientationEventListener(this);
Eric Erfanianccca3152017-02-22 16:32:36 -0800253
254 getWindow()
255 .getDecorView()
256 .setSystemUiVisibility(
257 View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
258
259 pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700260 sendBroadcast(CallPendingActivity.getFinishBroadcast());
261 Trace.endSection();
zachh7a96dc72018-02-20 22:16:03 -0800262 MetricsComponent.get(this)
263 .metrics()
264 .stopTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
265 MetricsComponent.get(this)
266 .metrics()
267 .stopTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
Eric Erfanianccca3152017-02-22 16:32:36 -0800268 }
269
linyuhc3968e62017-11-20 17:40:50 -0800270 private void setWindowFlags() {
271 // Allow the activity to be shown when the screen is locked and filter out touch events that are
272 // "too fat".
273 int flags =
274 WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
275 | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
276
linyuhf79d1cb2017-12-15 17:49:56 -0800277 // When the audio stream is not via Bluetooth, turn on the screen once the activity is shown.
278 // When the audio stream is via Bluetooth, turn on the screen only for an incoming call.
linyuhc3968e62017-11-20 17:40:50 -0800279 final int audioRoute = getAudioRoute();
linyuhf79d1cb2017-12-15 17:49:56 -0800280 if (audioRoute != CallAudioState.ROUTE_BLUETOOTH
281 || CallList.getInstance().getIncomingCall() != null) {
linyuhc3968e62017-11-20 17:40:50 -0800282 flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
283 }
284
285 getWindow().addFlags(flags);
286 }
287
288 private static int getAudioRoute() {
289 if (audioRouteForTesting.isPresent()) {
290 return audioRouteForTesting.get();
291 }
292
293 return AudioModeProvider.getInstance().getAudioState().getRoute();
294 }
295
296 @VisibleForTesting(otherwise = VisibleForTesting.NONE)
297 public static void setAudioRouteForTesting(int audioRoute) {
298 audioRouteForTesting = Optional.of(audioRoute);
299 }
300
301 private void internalResolveIntent(Intent intent) {
302 if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
303 return;
304 }
305
306 if (intent.hasExtra(IntentExtraNames.SHOW_DIALPAD)) {
307 // IntentExtraNames.SHOW_DIALPAD can be used to specify whether the DTMF dialpad should be
308 // initially visible. If the extra is absent, leave the dialpad in its previous state.
309 boolean showDialpad = intent.getBooleanExtra(IntentExtraNames.SHOW_DIALPAD, false);
310 relaunchedFromDialer(showDialpad);
311 }
312
313 DialerCall outgoingCall = CallList.getInstance().getOutgoingCall();
314 if (outgoingCall == null) {
315 outgoingCall = CallList.getInstance().getPendingOutgoingCall();
316 }
317 if (intent.getBooleanExtra(IntentExtraNames.NEW_OUTGOING_CALL, false)) {
318 intent.removeExtra(IntentExtraNames.NEW_OUTGOING_CALL);
319
320 // InCallActivity is responsible for disconnecting a new outgoing call if there is no way of
321 // making it (i.e. no valid call capable accounts).
linyuh122fb0b2018-03-26 13:35:32 -0700322 if (InCallPresenter.isCallWithNoValidAccounts(outgoingCall)) {
linyuhc3968e62017-11-20 17:40:50 -0800323 LogUtil.i(
324 "InCallActivity.internalResolveIntent", "Call with no valid accounts, disconnecting");
325 outgoingCall.disconnect();
326 }
327
328 dismissKeyguard(true);
329 }
330
331 if (showPhoneAccountSelectionDialog()) {
332 hideMainInCallFragment();
333 }
334 }
335
336 /**
337 * When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad should
338 * be shown on launch.
339 *
340 * @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and {@code
341 * false} to indicate no change should be made to the dialpad visibility.
342 */
343 private void relaunchedFromDialer(boolean showDialpad) {
344 showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
345 animateDialpadOnShow = true;
346
347 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
348 // If there's only one line in use, AND it's on hold, then we're sure the user
349 // wants to use the dialpad toward the exact line, so un-hold the holding line.
350 DialerCall call = CallList.getInstance().getActiveOrBackgroundCall();
wangqibb94ca62018-04-27 14:34:04 -0700351 if (call != null && call.getState() == DialerCallState.ONHOLD) {
linyuhc3968e62017-11-20 17:40:50 -0800352 call.unhold();
353 }
354 }
355 }
356
357 /**
358 * Show a phone account selection dialog if there is a call waiting for phone account selection.
359 *
360 * @return true if the dialog was shown.
361 */
362 private boolean showPhoneAccountSelectionDialog() {
363 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
364 if (waitingForAccountCall == null) {
365 return false;
366 }
367
twyena1723252018-04-24 17:02:57 -0700368 ListenableFuture<PreferredAccountWorker.Result> preferredAccountFuture =
369 DialerExecutorComponent.get(this)
370 .backgroundExecutor()
371 .submit(
372 () -> {
373 try {
374 return new PreferredAccountWorker(waitingForAccountCall.getNumber())
375 .doInBackground(getApplicationContext());
376 } catch (Throwable throwable) {
377 throw new Exception(throwable);
378 }
379 });
linyuhc3968e62017-11-20 17:40:50 -0800380
twyena1723252018-04-24 17:02:57 -0700381 preferredAccountWorkerResultListener.listen(
382 this,
383 preferredAccountFuture,
384 result -> {
twyen3b2c7812018-04-30 12:13:06 -0700385 if (!isVisible()) {
386 LogUtil.i(
387 "CallingAccountSelector.showPhoneAccountSelectionDialog",
388 "activity ended before result returned");
389 return;
390 }
twyena1723252018-04-24 17:02:57 -0700391 if (result.getPhoneAccountHandle().isPresent()) {
392 Logger.get(this).logImpression(Type.DUAL_SIM_SELECTION_PREFERRED_USED);
393 selectPhoneAccountListener.onPhoneAccountSelected(
394 result.getPhoneAccountHandle().get(), false, waitingForAccountCall.getId());
395 return;
396 }
397 if (result.getSuggestion().isPresent()) {
398 LogUtil.i(
399 "CallingAccountSelector.processPreferredAccount",
400 "SIM suggested: " + result.getSuggestion().get().reason);
401 if (result.getSuggestion().get().shouldAutoSelect) {
402 Logger.get(this).logImpression(Type.DUAL_SIM_SELECTION_SUGGESTION_AUTO_SELECTED);
403 LogUtil.i(
404 "CallingAccountSelector.processPreferredAccount", "Auto selected suggestion");
405 selectPhoneAccountListener.onPhoneAccountSelected(
406 result.getSuggestion().get().phoneAccountHandle,
407 false,
408 waitingForAccountCall.getId());
409 return;
410 }
411 }
412 Bundle extras = waitingForAccountCall.getIntentExtras();
413 List<PhoneAccountHandle> phoneAccountHandles =
414 extras == null
415 ? new ArrayList<>()
416 : extras.getParcelableArrayList(Call.AVAILABLE_PHONE_ACCOUNTS);
twyen66adad02018-04-24 13:51:08 -0700417
twyena1723252018-04-24 17:02:57 -0700418 waitingForAccountCall.setPreferredAccountRecorder(
419 new PreferredAccountRecorder(
420 waitingForAccountCall.getNumber(),
421 result.getSuggestion().orNull(),
422 result.getDataId().orNull()));
423 SelectPhoneAccountDialogOptions.Builder optionsBuilder =
424 SelectPhoneAccountDialogOptions.newBuilder()
425 .setTitle(R.string.select_phone_account_for_calls)
426 .setCanSetDefault(result.getDataId().isPresent())
427 .setSetDefaultLabel(R.string.select_phone_account_for_calls_remember)
428 .setCallId(waitingForAccountCall.getId());
twyen66adad02018-04-24 13:51:08 -0700429
twyena1723252018-04-24 17:02:57 -0700430 for (PhoneAccountHandle phoneAccountHandle : phoneAccountHandles) {
431 SelectPhoneAccountDialogOptions.Entry.Builder entryBuilder =
432 SelectPhoneAccountDialogOptions.Entry.newBuilder();
433 SelectPhoneAccountDialogOptionsUtil.setPhoneAccountHandle(
434 entryBuilder, phoneAccountHandle);
435 Optional<String> hint =
436 SuggestionProvider.getHint(
437 this, phoneAccountHandle, result.getSuggestion().orNull());
438 if (hint.isPresent()) {
439 entryBuilder.setHint(hint.get());
440 }
441 optionsBuilder.addEntries(entryBuilder);
442 }
443
444 selectPhoneAccountDialogFragment =
445 SelectPhoneAccountDialogFragment.newInstance(
446 optionsBuilder.build(), selectPhoneAccountListener);
447 selectPhoneAccountDialogFragment.show(getFragmentManager(), Tags.SELECT_ACCOUNT_FRAGMENT);
448 },
449 throwable -> {
450 throw new RuntimeException(throwable);
451 });
twyen73a74c32018-03-07 12:12:24 -0800452
linyuhc3968e62017-11-20 17:40:50 -0800453 return true;
454 }
455
Eric Erfanianccca3152017-02-22 16:32:36 -0800456 @Override
457 protected void onSaveInstanceState(Bundle out) {
linyuh57b093b2017-11-17 14:32:32 -0800458 LogUtil.enterBlock("InCallActivity.onSaveInstanceState");
459
460 // TODO: DialpadFragment should handle this as part of its own state
linyuhc3968e62017-11-20 17:40:50 -0800461 out.putBoolean(IntentExtraNames.SHOW_DIALPAD, isDialpadVisible());
linyuh57b093b2017-11-17 14:32:32 -0800462 DialpadFragment dialpadFragment = getDialpadFragment();
463 if (dialpadFragment != null) {
linyuhc3968e62017-11-20 17:40:50 -0800464 out.putString(KeysForSavedInstance.DIALPAD_TEXT, dialpadFragment.getDtmfText());
linyuh57b093b2017-11-17 14:32:32 -0800465 }
466
linyuhc3968e62017-11-20 17:40:50 -0800467 out.putBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN, didShowAnswerScreen);
468 out.putBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN, didShowInCallScreen);
469 out.putBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN, didShowVideoCallScreen);
wangqi153af2f2018-02-15 16:21:49 -0800470 out.putBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN, didShowRttCallScreen);
erfaniand05d8992018-03-20 19:42:26 -0700471 out.putBoolean(KeysForSavedInstance.DID_SHOW_SPEAK_EASY_SCREEN, didShowSpeakEasyScreen);
linyuh57b093b2017-11-17 14:32:32 -0800472
Eric Erfanianccca3152017-02-22 16:32:36 -0800473 super.onSaveInstanceState(out);
474 isVisible = false;
475 }
476
477 @Override
478 protected void onStart() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700479 Trace.beginSection("InCallActivity.onStart");
Eric Erfanianccca3152017-02-22 16:32:36 -0800480 super.onStart();
linyuh57b093b2017-11-17 14:32:32 -0800481
Eric Erfanianccca3152017-02-22 16:32:36 -0800482 isVisible = true;
483 showMainInCallFragment();
linyuh57b093b2017-11-17 14:32:32 -0800484
485 InCallPresenter.getInstance().setActivity(this);
486 enableInCallOrientationEventListener(
487 getRequestedOrientation()
488 == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
489 InCallPresenter.getInstance().onActivityStarted();
490
yueg10f6e822018-01-17 15:32:18 -0800491 if (!isRecreating) {
492 InCallPresenter.getInstance().onUiShowing(true);
493 }
494
linyuh437ae952018-03-26 12:46:18 -0700495 if (isInMultiWindowMode() && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800496 // Hide the dialpad because there may not be enough room
497 showDialpadFragment(false, false);
498 }
linyuh57b093b2017-11-17 14:32:32 -0800499
Eric Erfanian2ca43182017-08-31 06:57:16 -0700500 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800501 }
502
503 @Override
504 protected void onResume() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700505 Trace.beginSection("InCallActivity.onResume");
Eric Erfanianccca3152017-02-22 16:32:36 -0800506 super.onResume();
linyuhc3968e62017-11-20 17:40:50 -0800507
508 if (!InCallPresenter.getInstance().isReadyForTearDown()) {
509 updateTaskDescription();
linyuhc3968e62017-11-20 17:40:50 -0800510 }
511
512 // If there is a pending request to show or hide the dialpad, handle that now.
513 if (showDialpadRequest != DIALPAD_REQUEST_NONE) {
514 if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
515 // Exit fullscreen so that the user has access to the dialpad hide/show button.
516 // This is important when showing the dialpad from within dialer.
517 InCallPresenter.getInstance().setFullScreen(false /* isFullScreen */, true /* force */);
518
519 showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
520 animateDialpadOnShow = false;
521
522 DialpadFragment dialpadFragment = getDialpadFragment();
523 if (dialpadFragment != null) {
524 dialpadFragment.setDtmfText(dtmfTextToPrepopulate);
525 dtmfTextToPrepopulate = null;
526 }
527 } else {
528 LogUtil.i("InCallActivity.onResume", "Force-hide the dialpad");
529 if (getDialpadFragment() != null) {
530 showDialpadFragment(false /* show */, false /* animate */);
531 }
532 }
533 showDialpadRequest = DIALPAD_REQUEST_NONE;
534 }
535 updateNavigationBar(isDialpadVisible());
536
linyuhc3968e62017-11-20 17:40:50 -0800537 CallList.getInstance()
538 .onInCallUiShown(getIntent().getBooleanExtra(IntentExtraNames.FOR_FULL_SCREEN, false));
539
Eric Erfanianccca3152017-02-22 16:32:36 -0800540 PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
541 pseudoScreenState.addListener(this);
542 onPseudoScreenStateChanged(pseudoScreenState.isOn());
Eric Erfanian2ca43182017-08-31 06:57:16 -0700543 Trace.endSection();
weijiaxu650e7cc2017-10-31 12:38:54 -0700544 // add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
545 ThreadUtil.postDelayedOnUiThread(
weijiaxuc950a9b2017-11-06 16:39:04 -0800546 () ->
zachh7a96dc72018-02-20 22:16:03 -0800547 MetricsComponent.get(this)
548 .metrics()
549 .recordMemory(Metrics.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
weijiaxu650e7cc2017-10-31 12:38:54 -0700550 1000);
Eric Erfanianccca3152017-02-22 16:32:36 -0800551 }
552
Eric Erfanianccca3152017-02-22 16:32:36 -0800553 @Override
554 protected void onPause() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700555 Trace.beginSection("InCallActivity.onPause");
Eric Erfanianccca3152017-02-22 16:32:36 -0800556 super.onPause();
linyuh57b093b2017-11-17 14:32:32 -0800557
558 DialpadFragment dialpadFragment = getDialpadFragment();
559 if (dialpadFragment != null) {
560 dialpadFragment.onDialerKeyUp(null);
561 }
562
Eric Erfanianccca3152017-02-22 16:32:36 -0800563 InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
Eric Erfanian2ca43182017-08-31 06:57:16 -0700564 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800565 }
566
567 @Override
568 protected void onStop() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700569 Trace.beginSection("InCallActivity.onStop");
wangqi4d705e52017-09-28 12:23:35 -0700570 isVisible = false;
Eric Erfanianccca3152017-02-22 16:32:36 -0800571 super.onStop();
linyuh57b093b2017-11-17 14:32:32 -0800572
573 // Disconnects the call waiting for a phone account when the activity is hidden (e.g., after the
574 // user presses the home button).
575 // Without this the pending call will get stuck on phone account selection and new calls can't
576 // be created.
577 // Skip this when the screen is locked since the activity may complete its current life cycle
578 // and restart.
linyuhc3968e62017-11-20 17:40:50 -0800579 if (!isRecreating && !getSystemService(KeyguardManager.class).isKeyguardLocked()) {
linyuh57b093b2017-11-17 14:32:32 -0800580 DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
581 if (waitingForAccountCall != null) {
582 waitingForAccountCall.disconnect();
583 }
584 }
585
586 enableInCallOrientationEventListener(false);
587 InCallPresenter.getInstance().updateIsChangingConfigurations();
588 InCallPresenter.getInstance().onActivityStopped();
linyuhc3968e62017-11-20 17:40:50 -0800589 if (!isRecreating) {
yueg10f6e822018-01-17 15:32:18 -0800590 InCallPresenter.getInstance().onUiShowing(false);
wangqie6060522018-04-05 11:47:15 -0700591 }
592 if (errorDialog != null) {
593 errorDialog.dismiss();
linyuh57b093b2017-11-17 14:32:32 -0800594 }
595
yueg10f6e822018-01-17 15:32:18 -0800596 if (isFinishing()) {
597 InCallPresenter.getInstance().unsetActivity(this);
598 }
599
Eric Erfanian2ca43182017-08-31 06:57:16 -0700600 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800601 }
602
603 @Override
604 protected void onDestroy() {
Eric Erfanian2ca43182017-08-31 06:57:16 -0700605 Trace.beginSection("InCallActivity.onDestroy");
Eric Erfanianccca3152017-02-22 16:32:36 -0800606 super.onDestroy();
linyuh57b093b2017-11-17 14:32:32 -0800607
608 InCallPresenter.getInstance().unsetActivity(this);
609 InCallPresenter.getInstance().updateIsChangingConfigurations();
Eric Erfanian2ca43182017-08-31 06:57:16 -0700610 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -0800611 }
612
613 @Override
614 public void finish() {
615 if (shouldCloseActivityOnFinish()) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -0700616 // When user select incall ui from recents after the call is disconnected, it tries to launch
617 // a new InCallActivity but InCallPresenter is already teared down at this point, which causes
618 // crash.
619 // By calling finishAndRemoveTask() instead of finish() the task associated with
620 // InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
621 // this case.
622 //
623 // Calling finish won't clear the task and normally when an activity finishes it shouldn't
624 // clear the task since there could be parent activity in the same task that's still alive.
625 // But InCallActivity is special since it's singleInstance which means it's root activity and
626 // only instance of activity in the task. So it should be safe to also remove task when
627 // finishing.
628 // It's also necessary in the sense of it's excluded from recents. So whenever the activity
629 // finishes, the task should also be removed since it doesn't make sense to go back to it in
630 // anyway anymore.
631 super.finishAndRemoveTask();
Eric Erfanianccca3152017-02-22 16:32:36 -0800632 }
633 }
634
635 private boolean shouldCloseActivityOnFinish() {
linyuhc3968e62017-11-20 17:40:50 -0800636 if (!isVisible) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800637 LogUtil.i(
638 "InCallActivity.shouldCloseActivityOnFinish",
639 "allowing activity to be closed because it's not visible");
640 return true;
641 }
642
twyen8efb4952017-10-06 16:35:54 -0700643 if (InCallPresenter.getInstance().isInCallUiLocked()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800644 LogUtil.i(
645 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700646 "in call ui is locked, not closing activity");
Eric Erfanianccca3152017-02-22 16:32:36 -0800647 return false;
648 }
649
650 LogUtil.i(
651 "InCallActivity.shouldCloseActivityOnFinish",
twyen8efb4952017-10-06 16:35:54 -0700652 "activity is visible and has no locks, allowing activity to close");
Eric Erfanianccca3152017-02-22 16:32:36 -0800653 return true;
654 }
655
656 @Override
657 protected void onNewIntent(Intent intent) {
linyuhc3968e62017-11-20 17:40:50 -0800658 LogUtil.enterBlock("InCallActivity.onNewIntent");
Eric Erfanianccca3152017-02-22 16:32:36 -0800659
660 // If the screen is off, we need to make sure it gets turned on for incoming calls.
661 // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
662 // when the activity is first created. Therefore, to ensure the screen is turned on
663 // for the call waiting case, we recreate() the current activity. There should be no jank from
664 // this since the screen is already off and will remain so until our new activity is up.
linyuhc3968e62017-11-20 17:40:50 -0800665 if (!isVisible) {
666 onNewIntent(intent, true /* isRecreating */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800667 LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
668 recreate();
Eric Erfanian10b34a52017-05-04 08:23:17 -0700669 } else {
linyuhc3968e62017-11-20 17:40:50 -0800670 onNewIntent(intent, false /* isRecreating */);
671 }
672 }
673
yuega3305352018-01-09 11:02:47 -0800674 @VisibleForTesting
675 void onNewIntent(Intent intent, boolean isRecreating) {
linyuhc3968e62017-11-20 17:40:50 -0800676 this.isRecreating = isRecreating;
677
678 // We're being re-launched with a new Intent. Since it's possible for a single InCallActivity
679 // instance to persist indefinitely (even if we finish() ourselves), this sequence can
680 // happen any time the InCallActivity needs to be displayed.
681
682 // Stash away the new intent so that we can get it in the future by calling getIntent().
683 // Otherwise getIntent() will return the original Intent from when we first got created.
684 setIntent(intent);
685
686 // Activities are always paused before receiving a new intent, so we can count on our onResume()
687 // method being called next.
688
689 // Just like in onCreate(), handle the intent.
690 // Skip if InCallActivity is going to be recreated since this will be called in onCreate().
691 if (!isRecreating) {
692 internalResolveIntent(intent);
Eric Erfanianccca3152017-02-22 16:32:36 -0800693 }
694 }
695
696 @Override
697 public void onBackPressed() {
linyuh57b093b2017-11-17 14:32:32 -0800698 LogUtil.enterBlock("InCallActivity.onBackPressed");
699
linyuhc3968e62017-11-20 17:40:50 -0800700 if (!isVisible) {
linyuh57b093b2017-11-17 14:32:32 -0800701 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800702 }
linyuh57b093b2017-11-17 14:32:32 -0800703
704 if (!getCallCardFragmentVisible()) {
705 return;
706 }
707
708 DialpadFragment dialpadFragment = getDialpadFragment();
709 if (dialpadFragment != null && dialpadFragment.isVisible()) {
710 showDialpadFragment(false /* show */, true /* animate */);
711 return;
712 }
713
714 if (CallList.getInstance().getIncomingCall() != null) {
715 LogUtil.i(
716 "InCallActivity.onBackPressed",
717 "Ignore the press of the back key when an incoming call is ringing");
718 return;
719 }
720
721 // Nothing special to do. Fall back to the default behavior.
722 super.onBackPressed();
Eric Erfanianccca3152017-02-22 16:32:36 -0800723 }
724
725 @Override
726 public boolean onOptionsItemSelected(MenuItem item) {
727 LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
728 if (item.getItemId() == android.R.id.home) {
729 onBackPressed();
730 return true;
731 }
732 return super.onOptionsItemSelected(item);
733 }
734
735 @Override
736 public boolean onKeyUp(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800737 DialpadFragment dialpadFragment = getDialpadFragment();
738 if (dialpadFragment != null
739 && dialpadFragment.isVisible()
740 && dialpadFragment.onDialerKeyUp(event)) {
741 return true;
742 }
743
744 if (keyCode == KeyEvent.KEYCODE_CALL) {
745 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
746 return true;
747 }
748
749 return super.onKeyUp(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800750 }
751
752 @Override
753 public boolean onKeyDown(int keyCode, KeyEvent event) {
linyuh57b093b2017-11-17 14:32:32 -0800754 switch (keyCode) {
755 case KeyEvent.KEYCODE_CALL:
756 if (!InCallPresenter.getInstance().handleCallKey()) {
757 LogUtil.e(
758 "InCallActivity.onKeyDown",
759 "InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
760 }
761 // Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
762 return true;
763
764 // Note that KEYCODE_ENDCALL isn't handled here as the standard system-wide handling of it
765 // is exactly what's needed, namely
766 // (1) "hang up" if there's an active call, or
767 // (2) "don't answer" if there's an incoming call.
768 // (See PhoneWindowManager for implementation details.)
769
770 case KeyEvent.KEYCODE_CAMERA:
771 // Consume KEYCODE_CAMERA since it's easy to accidentally press the camera button.
772 return true;
773
774 case KeyEvent.KEYCODE_VOLUME_UP:
775 case KeyEvent.KEYCODE_VOLUME_DOWN:
776 case KeyEvent.KEYCODE_VOLUME_MUTE:
777 // Ringer silencing handled by PhoneWindowManager.
778 break;
779
780 case KeyEvent.KEYCODE_MUTE:
781 TelecomAdapter.getInstance()
782 .mute(!AudioModeProvider.getInstance().getAudioState().isMuted());
783 return true;
784
785 case KeyEvent.KEYCODE_SLASH:
786 // When verbose logging is enabled, dump the view for debugging/testing purposes.
787 if (LogUtil.isVerboseEnabled()) {
788 View decorView = getWindow().getDecorView();
789 LogUtil.v("InCallActivity.onKeyDown", "View dump:\n%s", decorView);
790 return true;
791 }
792 break;
793
794 case KeyEvent.KEYCODE_EQUALS:
795 break;
796
797 default: // fall out
798 }
799
800 // Pass other key events to DialpadFragment's "onDialerKeyDown" method in case the user types
801 // in DTMF (Dual-tone multi-frequency signaling) code.
802 DialpadFragment dialpadFragment = getDialpadFragment();
803 if (dialpadFragment != null
804 && dialpadFragment.isVisible()
805 && dialpadFragment.onDialerKeyDown(event)) {
806 return true;
807 }
808
809 return super.onKeyDown(keyCode, event);
Eric Erfanianccca3152017-02-22 16:32:36 -0800810 }
811
812 public boolean isInCallScreenAnimating() {
813 return false;
814 }
815
816 public void showConferenceFragment(boolean show) {
817 if (show) {
818 startActivity(new Intent(this, ManageConferenceActivity.class));
819 }
820 }
821
linyuhc3968e62017-11-20 17:40:50 -0800822 public void showDialpadFragment(boolean show, boolean animate) {
823 if (show == isDialpadVisible()) {
824 return;
Eric Erfanianccca3152017-02-22 16:32:36 -0800825 }
linyuhc3968e62017-11-20 17:40:50 -0800826
827 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
828 if (dialpadFragmentManager == null) {
829 LogUtil.i("InCallActivity.showDialpadFragment", "Unable to obtain a FragmentManager");
830 return;
831 }
832
833 if (!animate) {
834 if (show) {
835 showDialpadFragment();
836 } else {
837 hideDialpadFragment();
838 }
839 } else {
840 if (show) {
841 showDialpadFragment();
842 getDialpadFragment().animateShowDialpad();
843 }
844 getDialpadFragment()
845 .getView()
846 .startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
847 }
848
849 ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
850 if (sensor != null) {
851 sensor.onDialpadVisible(show);
852 }
853 showDialpadRequest = DIALPAD_REQUEST_NONE;
854
855 // Note: onInCallScreenDialpadVisibilityChange is called here to ensure that the dialpad FAB
856 // repositions itself.
wangqifd4c9f72018-03-08 18:21:50 -0800857 getInCallOrRttCallScreen().onInCallScreenDialpadVisibilityChange(show);
linyuhc3968e62017-11-20 17:40:50 -0800858 }
859
860 private void showDialpadFragment() {
861 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
862 if (dialpadFragmentManager == null) {
863 return;
864 }
865
866 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
867 DialpadFragment dialpadFragment = getDialpadFragment();
868 if (dialpadFragment == null) {
wangqifd4c9f72018-03-08 18:21:50 -0800869 dialpadFragment = new DialpadFragment();
870 transaction.add(getDialpadContainerId(), dialpadFragment, Tags.DIALPAD_FRAGMENT);
linyuhc3968e62017-11-20 17:40:50 -0800871 } else {
872 transaction.show(dialpadFragment);
calderwoodrad5883872017-12-12 15:29:12 -0800873 dialpadFragment.setUserVisibleHint(true);
linyuhc3968e62017-11-20 17:40:50 -0800874 }
wangqifd4c9f72018-03-08 18:21:50 -0800875 // RTT call screen doesn't show end call button inside dialpad, thus the space reserved for end
876 // call button should be removed.
877 dialpadFragment.setShouldShowEndCallSpace(didShowInCallScreen);
linyuhc3968e62017-11-20 17:40:50 -0800878 transaction.commitAllowingStateLoss();
879 dialpadFragmentManager.executePendingTransactions();
880
881 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, this);
882 updateNavigationBar(true /* isDialpadVisible */);
883 }
884
885 private void hideDialpadFragment() {
886 FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
887 if (dialpadFragmentManager == null) {
888 return;
889 }
890
calderwoodrad5883872017-12-12 15:29:12 -0800891 DialpadFragment dialpadFragment = getDialpadFragment();
linyuhc3968e62017-11-20 17:40:50 -0800892 if (dialpadFragment != null) {
893 FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
894 transaction.hide(dialpadFragment);
895 transaction.commitAllowingStateLoss();
896 dialpadFragmentManager.executePendingTransactions();
calderwoodrad5883872017-12-12 15:29:12 -0800897 dialpadFragment.setUserVisibleHint(false);
linyuhc3968e62017-11-20 17:40:50 -0800898 }
899 updateNavigationBar(false /* isDialpadVisible */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800900 }
901
902 public boolean isDialpadVisible() {
linyuh69a25062017-11-15 16:18:51 -0800903 DialpadFragment dialpadFragment = getDialpadFragment();
calderwoodraa584bcd2018-01-24 12:19:56 -0800904 return dialpadFragment != null
905 && dialpadFragment.isAdded()
906 && !dialpadFragment.isHidden()
907 && dialpadFragment.getView() != null
908 && dialpadFragment.getUserVisibleHint();
linyuh69a25062017-11-15 16:18:51 -0800909 }
910
linyuhc3968e62017-11-20 17:40:50 -0800911 /** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
linyuh69a25062017-11-15 16:18:51 -0800912 @Nullable
linyuhc3968e62017-11-20 17:40:50 -0800913 private DialpadFragment getDialpadFragment() {
linyuh69a25062017-11-15 16:18:51 -0800914 FragmentManager fragmentManager = getDialpadFragmentManager();
915 if (fragmentManager == null) {
916 return null;
917 }
linyuhc3968e62017-11-20 17:40:50 -0800918 return (DialpadFragment) fragmentManager.findFragmentByTag(Tags.DIALPAD_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -0800919 }
920
921 public void onForegroundCallChanged(DialerCall newForegroundCall) {
linyuh57b093b2017-11-17 14:32:32 -0800922 updateTaskDescription();
923
924 if (newForegroundCall == null || !didShowAnswerScreen) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800925 LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
linyuh57b093b2017-11-17 14:32:32 -0800926 updateWindowBackgroundColor(0 /* progress */);
Eric Erfanianccca3152017-02-22 16:32:36 -0800927 }
928 }
929
linyuhc3968e62017-11-20 17:40:50 -0800930 private void updateTaskDescription() {
linyuh57b093b2017-11-17 14:32:32 -0800931 int color =
932 getResources().getBoolean(R.bool.is_layout_landscape)
933 ? ResourcesCompat.getColor(
934 getResources(), R.color.statusbar_background_color, getTheme())
935 : InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
936 setTaskDescription(
937 new TaskDescription(
938 getResources().getString(R.string.notification_ongoing_call), null /* icon */, color));
939 }
940
Eric Erfanianccca3152017-02-22 16:32:36 -0800941 public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
942 ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
943 @ColorInt int top;
944 @ColorInt int middle;
945 @ColorInt int bottom;
946 @ColorInt int gray = 0x66000000;
947
linyuh437ae952018-03-26 12:46:18 -0700948 if (isInMultiWindowMode()) {
Eric Erfanianccca3152017-02-22 16:32:36 -0800949 top = themeColorManager.getBackgroundColorSolid();
950 middle = themeColorManager.getBackgroundColorSolid();
951 bottom = themeColorManager.getBackgroundColorSolid();
952 } else {
953 top = themeColorManager.getBackgroundColorTop();
954 middle = themeColorManager.getBackgroundColorMiddle();
955 bottom = themeColorManager.getBackgroundColorBottom();
956 }
957
958 if (progress < 0) {
959 float correctedProgress = Math.abs(progress);
960 top = ColorUtils.blendARGB(top, gray, correctedProgress);
961 middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
962 bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
963 }
964
965 boolean backgroundDirty = false;
966 if (backgroundDrawable == null) {
967 backgroundDrawableColors = new int[] {top, middle, bottom};
968 backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
969 backgroundDirty = true;
970 } else {
971 if (backgroundDrawableColors[0] != top) {
972 backgroundDrawableColors[0] = top;
973 backgroundDirty = true;
974 }
975 if (backgroundDrawableColors[1] != middle) {
976 backgroundDrawableColors[1] = middle;
977 backgroundDirty = true;
978 }
979 if (backgroundDrawableColors[2] != bottom) {
980 backgroundDrawableColors[2] = bottom;
981 backgroundDirty = true;
982 }
983 if (backgroundDirty) {
984 backgroundDrawable.setColors(backgroundDrawableColors);
985 }
986 }
987
988 if (backgroundDirty) {
989 getWindow().setBackgroundDrawable(backgroundDrawable);
990 }
991 }
992
993 public boolean isVisible() {
994 return isVisible;
995 }
996
997 public boolean getCallCardFragmentVisible() {
erfaniand05d8992018-03-20 19:42:26 -0700998 return didShowInCallScreen
999 || didShowVideoCallScreen
1000 || didShowRttCallScreen
1001 || didShowSpeakEasyScreen;
Eric Erfanianccca3152017-02-22 16:32:36 -08001002 }
1003
1004 public void dismissKeyguard(boolean dismiss) {
linyuh9c327da2017-11-14 12:33:48 -08001005 if (dismissKeyguard == dismiss) {
1006 return;
1007 }
1008
1009 dismissKeyguard = dismiss;
1010 if (dismiss) {
1011 getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
1012 } else {
1013 getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
1014 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001015 }
1016
linyuhc3968e62017-11-20 17:40:50 -08001017 public void showDialogForPostCharWait(String callId, String chars) {
yuega489f512018-04-25 12:01:09 -07001018 PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
1019 fragment.show(getSupportFragmentManager(), Tags.POST_CHAR_DIALOG_FRAGMENT);
Eric Erfanianccca3152017-02-22 16:32:36 -08001020 }
1021
linyuh7b86f562017-11-16 11:24:09 -08001022 public void showDialogOrToastForDisconnectedCall(DisconnectMessage disconnectMessage) {
1023 LogUtil.i(
1024 "InCallActivity.showDialogOrToastForDisconnectedCall",
1025 "disconnect cause: %s",
1026 disconnectMessage);
1027
1028 if (disconnectMessage.dialog == null || isFinishing()) {
1029 return;
1030 }
1031
1032 dismissPendingDialogs();
1033
1034 // Show a toast if the app is in background when a dialog can't be visible.
1035 if (!isVisible()) {
1036 Toast.makeText(getApplicationContext(), disconnectMessage.toastMessage, Toast.LENGTH_LONG)
1037 .show();
1038 return;
1039 }
1040
1041 // Show the dialog.
linyuhc3968e62017-11-20 17:40:50 -08001042 errorDialog = disconnectMessage.dialog;
linyuh7b86f562017-11-16 11:24:09 -08001043 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("showErrorDialog");
1044 disconnectMessage.dialog.setOnDismissListener(
1045 dialogInterface -> {
1046 lock.release();
1047 onDialogDismissed();
1048 });
1049 disconnectMessage.dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
1050 disconnectMessage.dialog.show();
1051 }
1052
1053 private void onDialogDismissed() {
linyuhc3968e62017-11-20 17:40:50 -08001054 errorDialog = null;
linyuh7b86f562017-11-16 11:24:09 -08001055 CallList.getInstance().onErrorDialogDismissed();
Eric Erfanianccca3152017-02-22 16:32:36 -08001056 }
1057
1058 public void dismissPendingDialogs() {
linyuhc3968e62017-11-20 17:40:50 -08001059 LogUtil.enterBlock("InCallActivity.dismissPendingDialogs");
linyuhf99f6302017-11-15 11:23:51 -08001060
1061 if (!isVisible) {
1062 // Defer the dismissing action as the activity is not visible and onSaveInstanceState may have
1063 // been called.
Eric Erfanianccca3152017-02-22 16:32:36 -08001064 LogUtil.i(
1065 "InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
1066 needDismissPendingDialogs = true;
linyuhf99f6302017-11-15 11:23:51 -08001067 return;
Eric Erfanianccca3152017-02-22 16:32:36 -08001068 }
linyuhf99f6302017-11-15 11:23:51 -08001069
1070 // Dismiss the error dialog
linyuhf99f6302017-11-15 11:23:51 -08001071 if (errorDialog != null) {
1072 errorDialog.dismiss();
linyuhc3968e62017-11-20 17:40:50 -08001073 errorDialog = null;
linyuhf99f6302017-11-15 11:23:51 -08001074 }
1075
1076 // Dismiss the phone account selection dialog
linyuhf99f6302017-11-15 11:23:51 -08001077 if (selectPhoneAccountDialogFragment != null) {
1078 selectPhoneAccountDialogFragment.dismiss();
linyuhc3968e62017-11-20 17:40:50 -08001079 selectPhoneAccountDialogFragment = null;
linyuhf99f6302017-11-15 11:23:51 -08001080 }
1081
1082 // Dismiss the dialog for international call on WiFi
1083 InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
1084 (InternationalCallOnWifiDialogFragment)
linyuhc3968e62017-11-20 17:40:50 -08001085 getSupportFragmentManager().findFragmentByTag(Tags.INTERNATIONAL_CALL_ON_WIFI);
linyuhf99f6302017-11-15 11:23:51 -08001086 if (internationalCallOnWifiFragment != null) {
1087 internationalCallOnWifiFragment.dismiss();
1088 }
1089
1090 // Dismiss the answer screen
1091 AnswerScreen answerScreen = getAnswerScreen();
1092 if (answerScreen != null) {
1093 answerScreen.dismissPendingDialogs();
1094 }
1095
1096 needDismissPendingDialogs = false;
Eric Erfanianccca3152017-02-22 16:32:36 -08001097 }
1098
linyuhc3968e62017-11-20 17:40:50 -08001099 private void enableInCallOrientationEventListener(boolean enable) {
linyuh69a25062017-11-15 16:18:51 -08001100 if (enable) {
1101 inCallOrientationEventListener.enable(true /* notifyDeviceOrientationChange */);
1102 } else {
1103 inCallOrientationEventListener.disable();
1104 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001105 }
1106
1107 public void setExcludeFromRecents(boolean exclude) {
linyuhc3968e62017-11-20 17:40:50 -08001108 int taskId = getTaskId();
1109
1110 List<AppTask> tasks = getSystemService(ActivityManager.class).getAppTasks();
1111 for (AppTask task : tasks) {
1112 try {
1113 if (task.getTaskInfo().id == taskId) {
1114 task.setExcludeFromRecents(exclude);
1115 }
1116 } catch (RuntimeException e) {
1117 LogUtil.e("InCallActivity.setExcludeFromRecents", "RuntimeException:\n%s", e);
1118 }
1119 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001120 }
1121
Eric Erfanianccca3152017-02-22 16:32:36 -08001122 @Nullable
1123 public FragmentManager getDialpadFragmentManager() {
wangqifd4c9f72018-03-08 18:21:50 -08001124 InCallScreen inCallScreen = getInCallOrRttCallScreen();
Eric Erfanianccca3152017-02-22 16:32:36 -08001125 if (inCallScreen != null) {
1126 return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
1127 }
1128 return null;
1129 }
1130
1131 public int getDialpadContainerId() {
wangqifd4c9f72018-03-08 18:21:50 -08001132 return getInCallOrRttCallScreen().getAnswerAndDialpadContainerResourceId();
Eric Erfanianccca3152017-02-22 16:32:36 -08001133 }
1134
1135 @Override
1136 public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
1137 DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
1138 if (call == null) {
1139 // This is a work around for a bug where we attempt to create a new delegate after the call
1140 // has already been removed. An example of when this can happen is:
1141 // 1. incoming video call in landscape mode
1142 // 2. remote party hangs up
1143 // 3. activity switches from landscape to portrait
1144 // At step #3 the answer fragment will try to create a new answer delegate but the call won't
1145 // exist. In this case we'll simply return a stub delegate that does nothing. This is ok
1146 // because this new state is transient and the activity will be destroyed soon.
1147 LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
1148 return new AnswerScreenPresenterStub();
1149 } else {
1150 return new AnswerScreenPresenter(
1151 this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
1152 }
1153 }
1154
1155 @Override
1156 public InCallScreenDelegate newInCallScreenDelegate() {
1157 return new CallCardPresenter(this);
1158 }
1159
1160 @Override
1161 public InCallButtonUiDelegate newInCallButtonUiDelegate() {
1162 return new CallButtonPresenter(this);
1163 }
1164
1165 @Override
Eric Erfanian90508232017-03-24 09:31:16 -07001166 public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
1167 DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
1168 if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
1169 return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
1170 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001171 return new VideoCallPresenter();
1172 }
1173
1174 public void onPrimaryCallStateChanged() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001175 Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
Eric Erfanianccca3152017-02-22 16:32:36 -08001176 showMainInCallFragment();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001177 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001178 }
1179
linyuh7b86f562017-11-16 11:24:09 -08001180 public void showDialogOrToastForWifiHandoverFailure(DialerCall call) {
1181 if (call.showWifiHandoverAlertAsToast()) {
1182 Toast.makeText(this, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
1183 .show();
1184 return;
1185 }
1186
1187 dismissPendingDialogs();
1188
1189 AlertDialog.Builder builder =
1190 new AlertDialog.Builder(this).setTitle(R.string.video_call_lte_to_wifi_failed_title);
1191
1192 // This allows us to use the theme of the dialog instead of the activity
1193 View dialogCheckBoxView =
1194 View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null /* root */);
1195 CheckBox wifiHandoverFailureCheckbox =
1196 (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
1197 wifiHandoverFailureCheckbox.setChecked(false);
1198
1199 InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("WifiFailedDialog");
linyuhc3968e62017-11-20 17:40:50 -08001200 errorDialog =
linyuh7b86f562017-11-16 11:24:09 -08001201 builder
1202 .setView(dialogCheckBoxView)
1203 .setMessage(R.string.video_call_lte_to_wifi_failed_message)
1204 .setOnCancelListener(dialogInterface -> onDialogDismissed())
1205 .setPositiveButton(
1206 android.R.string.ok,
1207 (dialogInterface, id) -> {
1208 call.setDoNotShowDialogForHandoffToWifiFailure(
1209 wifiHandoverFailureCheckbox.isChecked());
1210 dialogInterface.cancel();
1211 onDialogDismissed();
1212 })
1213 .setOnDismissListener(dialogInterface -> lock.release())
1214 .create();
linyuh7b86f562017-11-16 11:24:09 -08001215 errorDialog.show();
Eric Erfanianccca3152017-02-22 16:32:36 -08001216 }
1217
linyuh7b86f562017-11-16 11:24:09 -08001218 public void showDialogForInternationalCallOnWifi(@NonNull DialerCall call) {
linyuh7b86f562017-11-16 11:24:09 -08001219 InternationalCallOnWifiDialogFragment fragment =
yuegb47528e2018-04-24 12:12:57 -07001220 InternationalCallOnWifiDialogFragment.newInstance(call.getId());
linyuhc3968e62017-11-20 17:40:50 -08001221 fragment.show(getSupportFragmentManager(), Tags.INTERNATIONAL_CALL_ON_WIFI);
Eric Erfanianc857f902017-05-15 14:05:33 -07001222 }
1223
wangqibc28ea72018-04-02 16:23:00 -07001224 public void showDialogForRttRequest(DialerCall call, int rttRequestId) {
1225 LogUtil.enterBlock("InCallActivity.showDialogForRttRequest");
1226 DialogFragment fragment = RttRequestDialogFragment.newInstance(call.getId(), rttRequestId);
1227 fragment.show(getSupportFragmentManager(), Tags.RTT_REQUEST_DIALOG);
1228 }
1229
Eric Erfanian938468d2017-10-24 14:05:52 -07001230 @Override
1231 public void onMultiWindowModeChanged(boolean isInMultiWindowMode) {
1232 super.onMultiWindowModeChanged(isInMultiWindowMode);
linyuh57b093b2017-11-17 14:32:32 -08001233 updateNavigationBar(isDialpadVisible());
1234 }
1235
linyuhc3968e62017-11-20 17:40:50 -08001236 private void updateNavigationBar(boolean isDialpadVisible) {
linyuh437ae952018-03-26 12:46:18 -07001237 if (isInMultiWindowMode()) {
linyuh57b093b2017-11-17 14:32:32 -08001238 return;
1239 }
1240
1241 View navigationBarBackground = getWindow().findViewById(R.id.navigation_bar_background);
1242 if (navigationBarBackground != null) {
1243 navigationBarBackground.setVisibility(isDialpadVisible ? View.VISIBLE : View.GONE);
Eric Erfanian938468d2017-10-24 14:05:52 -07001244 }
1245 }
1246
Eric Erfanianccca3152017-02-22 16:32:36 -08001247 public void setAllowOrientationChange(boolean allowOrientationChange) {
wangqi9982f0d2017-10-11 17:46:07 -07001248 if (this.allowOrientationChange == allowOrientationChange) {
1249 return;
1250 }
1251 this.allowOrientationChange = allowOrientationChange;
Eric Erfanianccca3152017-02-22 16:32:36 -08001252 if (!allowOrientationChange) {
1253 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
1254 } else {
1255 setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
1256 }
1257 enableInCallOrientationEventListener(allowOrientationChange);
1258 }
1259
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001260 public void hideMainInCallFragment() {
linyuhc3968e62017-11-20 17:40:50 -08001261 LogUtil.enterBlock("InCallActivity.hideMainInCallFragment");
1262 if (getCallCardFragmentVisible()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001263 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
1264 hideInCallScreenFragment(transaction);
1265 hideVideoCallScreenFragment(transaction);
1266 transaction.commitAllowingStateLoss();
1267 getSupportFragmentManager().executePendingTransactions();
1268 }
1269 }
1270
1271 private void showMainInCallFragment() {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001272 Trace.beginSection("InCallActivity.showMainInCallFragment");
Eric Erfanianccca3152017-02-22 16:32:36 -08001273 // If the activity's onStart method hasn't been called yet then defer doing any work.
1274 if (!isVisible) {
1275 LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001276 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001277 return;
1278 }
1279
1280 // Don't let this be reentrant.
1281 if (isInShowMainInCallFragment) {
1282 LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
Eric Erfanian2ca43182017-08-31 06:57:16 -07001283 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001284 return;
1285 }
1286
1287 isInShowMainInCallFragment = true;
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001288 ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
1289 ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
wangqi219b8702018-02-13 09:34:41 -08001290 ShouldShowUiResult shouldShowRttUi = getShouldShowRttUi();
erfaniand05d8992018-03-20 19:42:26 -07001291 ShouldShowUiResult shouldShowSpeakEasyUi = getShouldShowSpeakEasyUi();
Eric Erfanianccca3152017-02-22 16:32:36 -08001292 LogUtil.i(
1293 "InCallActivity.showMainInCallFragment",
wangqi219b8702018-02-13 09:34:41 -08001294 "shouldShowAnswerUi: %b, shouldShowRttUi: %b, shouldShowVideoUi: %b "
1295 + "didShowAnswerScreen: %b, didShowInCallScreen: %b, didShowRttCallScreen: %b, "
erfaniand05d8992018-03-20 19:42:26 -07001296 + "didShowVideoCallScreen: %b"
1297 + "didShowSpeakEasyScreen: %b",
Eric Erfanianccca3152017-02-22 16:32:36 -08001298 shouldShowAnswerUi.shouldShow,
wangqi219b8702018-02-13 09:34:41 -08001299 shouldShowRttUi.shouldShow,
Eric Erfanian10b34a52017-05-04 08:23:17 -07001300 shouldShowVideoUi.shouldShow,
Eric Erfanianccca3152017-02-22 16:32:36 -08001301 didShowAnswerScreen,
1302 didShowInCallScreen,
wangqi219b8702018-02-13 09:34:41 -08001303 didShowRttCallScreen,
erfaniand05d8992018-03-20 19:42:26 -07001304 didShowVideoCallScreen,
1305 didShowSpeakEasyScreen);
Eric Erfanianccca3152017-02-22 16:32:36 -08001306 // Only video call ui allows orientation change.
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001307 setAllowOrientationChange(shouldShowVideoUi.shouldShow);
Eric Erfanianccca3152017-02-22 16:32:36 -08001308
1309 FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
wangqi219b8702018-02-13 09:34:41 -08001310 boolean didChange;
Eric Erfanianccca3152017-02-22 16:32:36 -08001311 if (shouldShowAnswerUi.shouldShow) {
wangqi219b8702018-02-13 09:34:41 -08001312 didChange = hideInCallScreenFragment(transaction);
1313 didChange |= hideVideoCallScreenFragment(transaction);
1314 didChange |= hideRttCallScreenFragment(transaction);
erfaniand05d8992018-03-20 19:42:26 -07001315 didChange |= hideSpeakEasyFragment(transaction);
wangqi219b8702018-02-13 09:34:41 -08001316 didChange |= showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001317 } else if (shouldShowVideoUi.shouldShow) {
wangqi219b8702018-02-13 09:34:41 -08001318 didChange = hideInCallScreenFragment(transaction);
1319 didChange |= showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
1320 didChange |= hideRttCallScreenFragment(transaction);
erfaniand05d8992018-03-20 19:42:26 -07001321 didChange |= hideSpeakEasyFragment(transaction);
wangqi219b8702018-02-13 09:34:41 -08001322 didChange |= hideAnswerScreenFragment(transaction);
1323 } else if (shouldShowRttUi.shouldShow) {
1324 didChange = hideInCallScreenFragment(transaction);
1325 didChange |= hideVideoCallScreenFragment(transaction);
1326 didChange |= hideAnswerScreenFragment(transaction);
erfaniand05d8992018-03-20 19:42:26 -07001327 didChange |= hideSpeakEasyFragment(transaction);
wangqi219b8702018-02-13 09:34:41 -08001328 didChange |= showRttCallScreenFragment(transaction, shouldShowRttUi.call);
erfaniand05d8992018-03-20 19:42:26 -07001329 } else if (shouldShowSpeakEasyUi.shouldShow) {
1330 didChange = hideInCallScreenFragment(transaction);
1331 didChange |= hideVideoCallScreenFragment(transaction);
1332 didChange |= hideAnswerScreenFragment(transaction);
1333 didChange |= hideRttCallScreenFragment(transaction);
1334 didChange |= showSpeakEasyFragment(transaction, shouldShowSpeakEasyUi.call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001335 } else {
wangqi219b8702018-02-13 09:34:41 -08001336 didChange = showInCallScreenFragment(transaction);
1337 didChange |= hideVideoCallScreenFragment(transaction);
1338 didChange |= hideRttCallScreenFragment(transaction);
erfaniand05d8992018-03-20 19:42:26 -07001339 didChange |= hideSpeakEasyFragment(transaction);
wangqi219b8702018-02-13 09:34:41 -08001340 didChange |= hideAnswerScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -08001341 }
1342
wangqi219b8702018-02-13 09:34:41 -08001343 if (didChange) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001344 Trace.beginSection("InCallActivity.commitTransaction");
Eric Erfanianccca3152017-02-22 16:32:36 -08001345 transaction.commitNow();
Eric Erfanian2ca43182017-08-31 06:57:16 -07001346 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001347 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1348 }
1349 isInShowMainInCallFragment = false;
Eric Erfanian2ca43182017-08-31 06:57:16 -07001350 Trace.endSection();
Eric Erfanianccca3152017-02-22 16:32:36 -08001351 }
1352
erfaniand05d8992018-03-20 19:42:26 -07001353 private boolean showSpeakEasyFragment(FragmentTransaction transaction, DialerCall call) {
1354
erfaniand05d8992018-03-20 19:42:26 -07001355 if (didShowSpeakEasyScreen) {
erfanian612d13a2018-04-04 15:27:57 -07001356 if (lastShownSpeakEasyScreenUniqueCallid.equals(call.getUniqueCallId())) {
erfanian12243de2018-04-17 12:17:08 -07001357 LogUtil.i("InCallActivity.showSpeakEasyFragment", "found existing fragment");
erfanian612d13a2018-04-04 15:27:57 -07001358 return false;
1359 }
1360 hideSpeakEasyFragment(transaction);
erfanian12243de2018-04-17 12:17:08 -07001361 LogUtil.i("InCallActivity.showSpeakEasyFragment", "hid existing fragment");
erfaniand05d8992018-03-20 19:42:26 -07001362 }
1363
1364 Optional<Fragment> speakEasyFragment = speakEasyCallManager.getSpeakEasyFragment(call);
1365 if (speakEasyFragment.isPresent()) {
1366 transaction.add(R.id.main, speakEasyFragment.get(), Tags.SPEAK_EASY_SCREEN);
1367 didShowSpeakEasyScreen = true;
erfanian612d13a2018-04-04 15:27:57 -07001368 lastShownSpeakEasyScreenUniqueCallid = call.getUniqueCallId();
erfanian12243de2018-04-17 12:17:08 -07001369 LogUtil.i(
1370 "InCallActivity.showSpeakEasyFragment",
1371 "set fragment for call %s",
1372 lastShownSpeakEasyScreenUniqueCallid);
erfaniand05d8992018-03-20 19:42:26 -07001373 return true;
1374 }
1375 return false;
1376 }
1377
1378 private Fragment getSpeakEasyScreen() {
1379 return getSupportFragmentManager().findFragmentByTag(Tags.SPEAK_EASY_SCREEN);
1380 }
1381
1382 private boolean hideSpeakEasyFragment(FragmentTransaction transaction) {
1383 if (!didShowSpeakEasyScreen) {
1384 return false;
1385 }
1386
1387 Fragment speakEasyFragment = getSpeakEasyScreen();
1388
1389 if (speakEasyFragment != null) {
1390 transaction.remove(speakEasyFragment);
1391 didShowSpeakEasyScreen = false;
1392 return true;
1393 }
1394 return false;
1395 }
1396
erfanian12243de2018-04-17 12:17:08 -07001397 @VisibleForTesting
erfaniand05d8992018-03-20 19:42:26 -07001398 public void setSpeakEasyCallManager(SpeakEasyCallManager speakEasyCallManager) {
erfanian87dbb622018-04-04 15:43:00 -07001399 this.speakEasyCallManager = speakEasyCallManager;
erfaniand05d8992018-03-20 19:42:26 -07001400 }
1401
erfanian12243de2018-04-17 12:17:08 -07001402 @Nullable
erfaniand05d8992018-03-20 19:42:26 -07001403 public SpeakEasyCallManager getSpeakEasyCallManager() {
erfanian12243de2018-04-17 12:17:08 -07001404 if (this.speakEasyCallManager == null) {
1405 this.speakEasyCallManager = InCallPresenter.getInstance().getSpeakEasyCallManager();
1406 }
erfaniand05d8992018-03-20 19:42:26 -07001407 return speakEasyCallManager;
1408 }
1409
1410 private ShouldShowUiResult getShouldShowSpeakEasyUi() {
1411 SpeakEasyCallManager speakEasyCallManager = getSpeakEasyCallManager();
1412
1413 if (speakEasyCallManager == null) {
1414 return new ShouldShowUiResult(false, null);
1415 }
1416
erfanian3bb7cb62018-04-11 09:01:15 -07001417 DialerCall call =
1418 CallList.getInstance().getIncomingCall() != null
1419 ? CallList.getInstance().getIncomingCall()
1420 : CallList.getInstance().getActiveCall();
erfaniand05d8992018-03-20 19:42:26 -07001421
1422 if (call == null) {
1423 return new ShouldShowUiResult(false, call);
1424 }
1425
1426 if (!call.isSpeakEasyCall() || !call.isSpeakEasyEligible()) {
1427 return new ShouldShowUiResult(false, call);
1428 }
1429
1430 Optional<Fragment> speakEasyFragment = speakEasyCallManager.getSpeakEasyFragment(call);
1431
1432 if (!speakEasyFragment.isPresent()) {
1433 return new ShouldShowUiResult(false, call);
1434 }
1435 return new ShouldShowUiResult(true, call);
1436 }
1437
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001438 private ShouldShowUiResult getShouldShowAnswerUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001439 DialerCall call = CallList.getInstance().getIncomingCall();
erfanian3bb7cb62018-04-11 09:01:15 -07001440 if (call != null && !call.isSpeakEasyCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001441 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001442 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001443 }
1444
1445 call = CallList.getInstance().getVideoUpgradeRequestCall();
1446 if (call != null) {
1447 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001448 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001449 }
1450
1451 // Check if we're showing the answer screen and the call is disconnected. If this condition is
1452 // true then we won't switch from the answer UI to the in call UI. This prevents flicker when
1453 // the user rejects an incoming call.
1454 call = CallList.getInstance().getFirstCall();
1455 if (call == null) {
1456 call = CallList.getInstance().getBackgroundCall();
1457 }
wangqibb94ca62018-04-27 14:34:04 -07001458 if (didShowAnswerScreen && (call == null || call.getState() == DialerCallState.DISCONNECTED)) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001459 LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001460 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001461 }
1462
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001463 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001464 }
1465
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001466 private static ShouldShowUiResult getShouldShowVideoUi() {
Eric Erfanianccca3152017-02-22 16:32:36 -08001467 DialerCall call = CallList.getInstance().getFirstCall();
1468 if (call == null) {
1469 LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001470 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001471 }
1472
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001473 if (call.isVideoCall()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001474 LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001475 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001476 }
1477
linyuh8fbecce2017-12-18 13:53:09 -08001478 if (call.hasSentVideoUpgradeRequest() || call.hasReceivedVideoUpgradeRequest()) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001479 LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001480 return new ShouldShowUiResult(true, call);
Eric Erfanianccca3152017-02-22 16:32:36 -08001481 }
1482
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001483 return new ShouldShowUiResult(false, null);
Eric Erfanianccca3152017-02-22 16:32:36 -08001484 }
1485
wangqi219b8702018-02-13 09:34:41 -08001486 private static ShouldShowUiResult getShouldShowRttUi() {
1487 DialerCall call = CallList.getInstance().getFirstCall();
1488 if (call == null) {
1489 LogUtil.i("InCallActivity.getShouldShowRttUi", "null call");
1490 return new ShouldShowUiResult(false, null);
1491 }
1492
wangqif6be6172018-03-30 15:57:56 -07001493 if (call.isActiveRttCall()) {
wangqi219b8702018-02-13 09:34:41 -08001494 LogUtil.i("InCallActivity.getShouldShowRttUi", "found rtt call");
1495 return new ShouldShowUiResult(true, call);
1496 }
1497
1498 if (call.hasSentRttUpgradeRequest()) {
1499 LogUtil.i("InCallActivity.getShouldShowRttUi", "upgrading to rtt");
1500 return new ShouldShowUiResult(true, call);
1501 }
1502
1503 return new ShouldShowUiResult(false, null);
1504 }
1505
Eric Erfanianccca3152017-02-22 16:32:36 -08001506 private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
1507 // When rejecting a call the active call can become null in which case we should continue
1508 // showing the answer screen.
1509 if (didShowAnswerScreen && call == null) {
1510 return false;
1511 }
1512
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001513 Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
1514
1515 boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
Eric Erfanianccca3152017-02-22 16:32:36 -08001516
1517 // Check if we're already showing an answer screen for this call.
1518 if (didShowAnswerScreen) {
1519 AnswerScreen answerScreen = getAnswerScreen();
1520 if (answerScreen.getCallId().equals(call.getId())
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001521 && answerScreen.isVideoCall() == call.isVideoCall()
Eric Erfanian2ca43182017-08-31 06:57:16 -07001522 && answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
1523 && !answerScreen.isActionTimeout()) {
1524 LogUtil.d(
1525 "InCallActivity.showAnswerScreenFragment",
1526 "answer fragment exists for same call and has NOT been accepted/rejected/timed out");
Eric Erfanianccca3152017-02-22 16:32:36 -08001527 return false;
1528 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001529 if (answerScreen.isActionTimeout()) {
1530 LogUtil.i(
1531 "InCallActivity.showAnswerScreenFragment",
1532 "answer fragment exists but has been accepted/rejected and timed out");
1533 } else {
1534 LogUtil.i(
1535 "InCallActivity.showAnswerScreenFragment",
1536 "answer fragment exists but arguments do not match");
1537 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001538 hideAnswerScreenFragment(transaction);
1539 }
1540
1541 // Show a new answer screen.
1542 AnswerScreen answerScreen =
Eric Erfanianfc37b022017-03-21 10:11:17 -07001543 AnswerBindings.createAnswerScreen(
1544 call.getId(),
wangqif6be6172018-03-30 15:57:56 -07001545 call.isActiveRttCall(),
Eric Erfanianfc37b022017-03-21 10:11:17 -07001546 call.isVideoCall(),
1547 isVideoUpgradeRequest,
Eric Erfanian90508232017-03-24 09:31:16 -07001548 call.getVideoTech().isSelfManagedCamera(),
1549 shouldAllowAnswerAndRelease(call),
erfaniand05d8992018-03-20 19:42:26 -07001550 CallList.getInstance().getBackgroundCall() != null,
1551 call.isSpeakEasyEligible());
linyuhc3968e62017-11-20 17:40:50 -08001552 transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), Tags.ANSWER_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001553
1554 Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
1555 didShowAnswerScreen = true;
1556 return true;
1557 }
1558
Eric Erfanian90508232017-03-24 09:31:16 -07001559 private boolean shouldAllowAnswerAndRelease(DialerCall call) {
1560 if (CallList.getInstance().getActiveCall() == null) {
1561 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
1562 return false;
1563 }
1564 if (getSystemService(TelephonyManager.class).getPhoneType()
1565 == TelephonyManager.PHONE_TYPE_CDMA) {
1566 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
1567 return false;
1568 }
1569 if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
1570 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
1571 return false;
1572 }
linyuhc3968e62017-11-20 17:40:50 -08001573 if (!ConfigProviderBindings.get(this)
1574 .getBoolean(ConfigNames.ANSWER_AND_RELEASE_ENABLED, true)) {
Eric Erfanian90508232017-03-24 09:31:16 -07001575 LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
1576 return false;
1577 }
1578
1579 return true;
1580 }
1581
Eric Erfanianccca3152017-02-22 16:32:36 -08001582 private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
1583 if (!didShowAnswerScreen) {
1584 return false;
1585 }
1586 AnswerScreen answerScreen = getAnswerScreen();
1587 if (answerScreen != null) {
1588 transaction.remove(answerScreen.getAnswerScreenFragment());
1589 }
1590
1591 didShowAnswerScreen = false;
1592 return true;
1593 }
1594
1595 private boolean showInCallScreenFragment(FragmentTransaction transaction) {
1596 if (didShowInCallScreen) {
1597 return false;
1598 }
Eric Erfanian2ca43182017-08-31 06:57:16 -07001599 InCallScreen inCallScreen = InCallBindings.createInCallScreen();
linyuhc3968e62017-11-20 17:40:50 -08001600 transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), Tags.IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001601 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1602 didShowInCallScreen = true;
1603 return true;
1604 }
1605
1606 private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
1607 if (!didShowInCallScreen) {
1608 return false;
1609 }
1610 InCallScreen inCallScreen = getInCallScreen();
1611 if (inCallScreen != null) {
Eric Erfanian2ca43182017-08-31 06:57:16 -07001612 transaction.remove(inCallScreen.getInCallScreenFragment());
Eric Erfanianccca3152017-02-22 16:32:36 -08001613 }
1614 didShowInCallScreen = false;
1615 return true;
1616 }
1617
wangqi219b8702018-02-13 09:34:41 -08001618 private boolean showRttCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
1619 if (didShowRttCallScreen) {
1620 // This shouldn't happen since only one RTT call is allow at same time.
1621 if (!getRttCallScreen().getCallId().equals(call.getId())) {
1622 LogUtil.e("InCallActivity.showRttCallScreenFragment", "RTT call id doesn't match");
1623 }
1624 return false;
1625 }
1626 RttCallScreen rttCallScreen = RttBindings.createRttCallScreen(call.getId());
1627 transaction.add(R.id.main, rttCallScreen.getRttCallScreenFragment(), Tags.RTT_CALL_SCREEN);
1628 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1629 didShowRttCallScreen = true;
1630 return true;
1631 }
1632
1633 private boolean hideRttCallScreenFragment(FragmentTransaction transaction) {
1634 if (!didShowRttCallScreen) {
1635 return false;
1636 }
1637 RttCallScreen rttCallScreen = getRttCallScreen();
1638 if (rttCallScreen != null) {
1639 transaction.remove(rttCallScreen.getRttCallScreenFragment());
1640 }
1641 didShowRttCallScreen = false;
1642 return true;
1643 }
1644
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001645 private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001646 if (didShowVideoCallScreen) {
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001647 VideoCallScreen videoCallScreen = getVideoCallScreen();
1648 if (videoCallScreen.getCallId().equals(call.getId())) {
1649 return false;
1650 }
1651 LogUtil.i(
1652 "InCallActivity.showVideoCallScreenFragment",
1653 "video call fragment exists but arguments do not match");
1654 hideVideoCallScreenFragment(transaction);
Eric Erfanianccca3152017-02-22 16:32:36 -08001655 }
1656
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001657 LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
1658
Eric Erfanian90508232017-03-24 09:31:16 -07001659 VideoCallScreen videoCallScreen =
1660 VideoBindings.createVideoCallScreen(
1661 call.getId(), call.getVideoTech().shouldUseSurfaceView());
linyuhc3968e62017-11-20 17:40:50 -08001662 transaction.add(
1663 R.id.main, videoCallScreen.getVideoCallScreenFragment(), Tags.VIDEO_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001664
1665 Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
1666 didShowVideoCallScreen = true;
1667 return true;
1668 }
1669
1670 private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
1671 if (!didShowVideoCallScreen) {
1672 return false;
1673 }
1674 VideoCallScreen videoCallScreen = getVideoCallScreen();
1675 if (videoCallScreen != null) {
1676 transaction.remove(videoCallScreen.getVideoCallScreenFragment());
1677 }
1678 didShowVideoCallScreen = false;
1679 return true;
1680 }
1681
linyuhc3968e62017-11-20 17:40:50 -08001682 private AnswerScreen getAnswerScreen() {
1683 return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(Tags.ANSWER_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001684 }
1685
linyuhc3968e62017-11-20 17:40:50 -08001686 private InCallScreen getInCallScreen() {
1687 return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.IN_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001688 }
1689
linyuhc3968e62017-11-20 17:40:50 -08001690 private VideoCallScreen getVideoCallScreen() {
1691 return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.VIDEO_CALL_SCREEN);
Eric Erfanianccca3152017-02-22 16:32:36 -08001692 }
1693
wangqi219b8702018-02-13 09:34:41 -08001694 private RttCallScreen getRttCallScreen() {
1695 return (RttCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.RTT_CALL_SCREEN);
1696 }
1697
wangqifd4c9f72018-03-08 18:21:50 -08001698 private InCallScreen getInCallOrRttCallScreen() {
1699 InCallScreen inCallScreen = null;
1700 if (didShowInCallScreen) {
1701 inCallScreen = getInCallScreen();
1702 }
1703 if (didShowRttCallScreen) {
1704 inCallScreen = getRttCallScreen();
1705 }
1706 return inCallScreen;
1707 }
1708
Eric Erfanianccca3152017-02-22 16:32:36 -08001709 @Override
1710 public void onPseudoScreenStateChanged(boolean isOn) {
1711 LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
1712 pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
1713 }
1714
1715 /**
1716 * For some touch related issue, turning off the screen can be faked by drawing a black view over
1717 * the activity. All touch events started when the screen is "off" is rejected.
1718 *
1719 * @see PseudoScreenState
1720 */
1721 @Override
1722 public boolean dispatchTouchEvent(MotionEvent event) {
1723 // Reject any gesture that started when the screen is in the fake off state.
1724 if (touchDownWhenPseudoScreenOff) {
1725 if (event.getAction() == MotionEvent.ACTION_UP) {
1726 touchDownWhenPseudoScreenOff = false;
1727 }
1728 return true;
1729 }
1730 // Reject all touch event when the screen is in the fake off state.
1731 if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
1732 if (event.getAction() == MotionEvent.ACTION_DOWN) {
1733 touchDownWhenPseudoScreenOff = true;
1734 LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
1735 }
1736 return true;
1737 }
1738 return super.dispatchTouchEvent(event);
1739 }
1740
wangqi219b8702018-02-13 09:34:41 -08001741 @Override
1742 public RttCallScreenDelegate newRttCallScreenDelegate(RttCallScreen videoCallScreen) {
1743 return new RttCallPresenter();
1744 }
1745
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001746 private static class ShouldShowUiResult {
Eric Erfanianccca3152017-02-22 16:32:36 -08001747 public final boolean shouldShow;
1748 public final DialerCall call;
1749
Eric Erfaniand5e47f62017-03-15 14:41:07 -07001750 ShouldShowUiResult(boolean shouldShow, DialerCall call) {
Eric Erfanianccca3152017-02-22 16:32:36 -08001751 this.shouldShow = shouldShow;
1752 this.call = call;
1753 }
1754 }
linyuhc3968e62017-11-20 17:40:50 -08001755
1756 private static final class IntentExtraNames {
1757 static final String FOR_FULL_SCREEN = "InCallActivity.for_full_screen_intent";
1758 static final String NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
1759 static final String SHOW_DIALPAD = "InCallActivity.show_dialpad";
1760 }
1761
1762 private static final class KeysForSavedInstance {
1763 static final String DIALPAD_TEXT = "InCallActivity.dialpad_text";
1764 static final String DID_SHOW_ANSWER_SCREEN = "did_show_answer_screen";
1765 static final String DID_SHOW_IN_CALL_SCREEN = "did_show_in_call_screen";
1766 static final String DID_SHOW_VIDEO_CALL_SCREEN = "did_show_video_call_screen";
wangqi153af2f2018-02-15 16:21:49 -08001767 static final String DID_SHOW_RTT_CALL_SCREEN = "did_show_rtt_call_screen";
erfaniand05d8992018-03-20 19:42:26 -07001768 static final String DID_SHOW_SPEAK_EASY_SCREEN = "did_show_speak_easy_screen";
linyuhc3968e62017-11-20 17:40:50 -08001769 }
1770
1771 /** Request codes for pending intents. */
1772 public static final class PendingIntentRequestCodes {
1773 static final int NON_FULL_SCREEN = 0;
1774 static final int FULL_SCREEN = 1;
1775 static final int BUBBLE = 2;
1776 }
1777
1778 private static final class Tags {
1779 static final String ANSWER_SCREEN = "tag_answer_screen";
1780 static final String DIALPAD_FRAGMENT = "tag_dialpad_fragment";
1781 static final String IN_CALL_SCREEN = "tag_in_call_screen";
1782 static final String INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi";
1783 static final String SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment";
1784 static final String VIDEO_CALL_SCREEN = "tag_video_call_screen";
wangqi219b8702018-02-13 09:34:41 -08001785 static final String RTT_CALL_SCREEN = "tag_rtt_call_screen";
linyuhc3968e62017-11-20 17:40:50 -08001786 static final String POST_CHAR_DIALOG_FRAGMENT = "tag_post_char_dialog_fragment";
erfaniand05d8992018-03-20 19:42:26 -07001787 static final String SPEAK_EASY_SCREEN = "tag_speak_easy_screen";
wangqibc28ea72018-04-02 16:23:00 -07001788 static final String RTT_REQUEST_DIALOG = "tag_rtt_request_dialog";
linyuhc3968e62017-11-20 17:40:50 -08001789 }
1790
1791 private static final class ConfigNames {
1792 static final String ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
1793 }
1794
linyuhc3968e62017-11-20 17:40:50 -08001795 private static final class SelectPhoneAccountListener
1796 extends SelectPhoneAccountDialogFragment.SelectPhoneAccountListener {
1797 private static final String TAG = SelectPhoneAccountListener.class.getCanonicalName();
1798
twyen73a74c32018-03-07 12:12:24 -08001799 private final Context appContext;
1800
1801 SelectPhoneAccountListener(Context appContext) {
1802 this.appContext = appContext;
1803 }
1804
linyuhc3968e62017-11-20 17:40:50 -08001805 @Override
1806 public void onPhoneAccountSelected(
1807 PhoneAccountHandle selectedAccountHandle, boolean setDefault, String callId) {
1808 DialerCall call = CallList.getInstance().getCallById(callId);
1809 LogUtil.i(TAG, "Phone account select with call:\n%s", call);
1810
1811 if (call != null) {
twyen73a74c32018-03-07 12:12:24 -08001812 call.phoneAccountSelected(selectedAccountHandle, false);
1813 if (call.getPreferredAccountRecorder() != null) {
1814 call.getPreferredAccountRecorder().record(appContext, selectedAccountHandle, setDefault);
1815 }
linyuhc3968e62017-11-20 17:40:50 -08001816 }
1817 }
1818
1819 @Override
1820 public void onDialogDismissed(String callId) {
1821 DialerCall call = CallList.getInstance().getCallById(callId);
1822 LogUtil.i(TAG, "Disconnecting call:\n%s" + call);
1823
1824 if (call != null) {
1825 call.disconnect();
1826 }
1827 }
1828 }
Eric Erfanianccca3152017-02-22 16:32:36 -08001829}