1786 lines
67 KiB
Java
1786 lines
67 KiB
Java
/*
|
|
* Copyright (C) 2016 The Android Open Source Project
|
|
*
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
* you may not use this file except in compliance with the License.
|
|
* You may obtain a copy of the License at
|
|
*
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
*
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
* See the License for the specific language governing permissions and
|
|
* limitations under the License.
|
|
*/
|
|
|
|
package com.android.incallui;
|
|
|
|
import android.app.ActivityManager;
|
|
import android.app.ActivityManager.AppTask;
|
|
import android.app.ActivityManager.TaskDescription;
|
|
import android.app.AlertDialog;
|
|
import android.app.Dialog;
|
|
import android.app.KeyguardManager;
|
|
import android.content.Context;
|
|
import android.content.Intent;
|
|
import android.content.res.Configuration;
|
|
import android.graphics.drawable.GradientDrawable;
|
|
import android.graphics.drawable.GradientDrawable.Orientation;
|
|
import android.os.Bundle;
|
|
import android.os.Trace;
|
|
import android.support.annotation.ColorInt;
|
|
import android.support.annotation.FloatRange;
|
|
import android.support.annotation.IntDef;
|
|
import android.support.annotation.NonNull;
|
|
import android.support.annotation.Nullable;
|
|
import android.support.annotation.VisibleForTesting;
|
|
import android.support.v4.app.DialogFragment;
|
|
import android.support.v4.app.Fragment;
|
|
import android.support.v4.app.FragmentManager;
|
|
import android.support.v4.app.FragmentTransaction;
|
|
import android.support.v4.content.res.ResourcesCompat;
|
|
import android.support.v4.graphics.ColorUtils;
|
|
import android.telecom.Call;
|
|
import android.telecom.CallAudioState;
|
|
import android.telecom.PhoneAccountHandle;
|
|
import android.telephony.TelephonyManager;
|
|
import android.view.KeyEvent;
|
|
import android.view.MenuItem;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
import android.view.WindowManager;
|
|
import android.view.animation.Animation;
|
|
import android.view.animation.AnimationUtils;
|
|
import android.widget.CheckBox;
|
|
import android.widget.Toast;
|
|
import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment;
|
|
import com.android.dialer.animation.AnimUtils;
|
|
import com.android.dialer.animation.AnimationListenerAdapter;
|
|
import com.android.dialer.common.Assert;
|
|
import com.android.dialer.common.LogUtil;
|
|
import com.android.dialer.common.concurrent.DialerExecutorComponent;
|
|
import com.android.dialer.common.concurrent.ThreadUtil;
|
|
import com.android.dialer.common.concurrent.UiListener;
|
|
import com.android.dialer.configprovider.ConfigProviderComponent;
|
|
import com.android.dialer.logging.Logger;
|
|
import com.android.dialer.logging.ScreenEvent;
|
|
import com.android.dialer.metrics.Metrics;
|
|
import com.android.dialer.metrics.MetricsComponent;
|
|
import com.android.dialer.preferredsim.PreferredAccountRecorder;
|
|
import com.android.dialer.preferredsim.PreferredAccountWorker;
|
|
import com.android.dialer.preferredsim.PreferredAccountWorker.Result;
|
|
import com.android.dialer.preferredsim.PreferredSimComponent;
|
|
import com.android.dialer.util.ViewUtil;
|
|
import com.android.incallui.answer.bindings.AnswerBindings;
|
|
import com.android.incallui.answer.protocol.AnswerScreen;
|
|
import com.android.incallui.answer.protocol.AnswerScreenDelegate;
|
|
import com.android.incallui.answer.protocol.AnswerScreenDelegateFactory;
|
|
import com.android.incallui.answerproximitysensor.PseudoScreenState;
|
|
import com.android.incallui.audiomode.AudioModeProvider;
|
|
import com.android.incallui.call.CallList;
|
|
import com.android.incallui.call.DialerCall;
|
|
import com.android.incallui.call.TelecomAdapter;
|
|
import com.android.incallui.call.state.DialerCallState;
|
|
import com.android.incallui.callpending.CallPendingActivity;
|
|
import com.android.incallui.disconnectdialog.DisconnectMessage;
|
|
import com.android.incallui.incall.bindings.InCallBindings;
|
|
import com.android.incallui.incall.protocol.InCallButtonUiDelegate;
|
|
import com.android.incallui.incall.protocol.InCallButtonUiDelegateFactory;
|
|
import com.android.incallui.incall.protocol.InCallScreen;
|
|
import com.android.incallui.incall.protocol.InCallScreenDelegate;
|
|
import com.android.incallui.incall.protocol.InCallScreenDelegateFactory;
|
|
import com.android.incallui.incalluilock.InCallUiLock;
|
|
import com.android.incallui.rtt.bindings.RttBindings;
|
|
import com.android.incallui.rtt.protocol.RttCallScreen;
|
|
import com.android.incallui.rtt.protocol.RttCallScreenDelegate;
|
|
import com.android.incallui.rtt.protocol.RttCallScreenDelegateFactory;
|
|
import com.android.incallui.speakeasy.SpeakEasyCallManager;
|
|
import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
|
|
import com.android.incallui.video.bindings.VideoBindings;
|
|
import com.android.incallui.video.protocol.VideoCallScreen;
|
|
import com.android.incallui.video.protocol.VideoCallScreenDelegate;
|
|
import com.android.incallui.video.protocol.VideoCallScreenDelegateFactory;
|
|
import com.google.common.util.concurrent.ListenableFuture;
|
|
import java.lang.annotation.Retention;
|
|
import java.lang.annotation.RetentionPolicy;
|
|
import java.util.ArrayList;
|
|
import java.util.List;
|
|
import java.util.Optional;
|
|
|
|
/** Version of {@link InCallActivity} that shows the new UI */
|
|
public class InCallActivity extends TransactionSafeFragmentActivity
|
|
implements AnswerScreenDelegateFactory,
|
|
InCallScreenDelegateFactory,
|
|
InCallButtonUiDelegateFactory,
|
|
VideoCallScreenDelegateFactory,
|
|
RttCallScreenDelegateFactory,
|
|
PseudoScreenState.StateChangedListener {
|
|
|
|
@Retention(RetentionPolicy.SOURCE)
|
|
@IntDef({
|
|
DIALPAD_REQUEST_NONE,
|
|
DIALPAD_REQUEST_SHOW,
|
|
DIALPAD_REQUEST_HIDE,
|
|
})
|
|
@interface DialpadRequestType {}
|
|
|
|
private static final int DIALPAD_REQUEST_NONE = 1;
|
|
private static final int DIALPAD_REQUEST_SHOW = 2;
|
|
private static final int DIALPAD_REQUEST_HIDE = 3;
|
|
|
|
private static Optional<Integer> audioRouteForTesting = Optional.empty();
|
|
|
|
private SelectPhoneAccountListener selectPhoneAccountListener;
|
|
private UiListener<Result> preferredAccountWorkerResultListener;
|
|
|
|
private Animation dialpadSlideInAnimation;
|
|
private Animation dialpadSlideOutAnimation;
|
|
private Dialog errorDialog;
|
|
private GradientDrawable backgroundDrawable;
|
|
private InCallOrientationEventListener inCallOrientationEventListener;
|
|
private View pseudoBlackScreenOverlay;
|
|
private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment;
|
|
private String dtmfTextToPrepopulate;
|
|
private boolean allowOrientationChange;
|
|
private boolean animateDialpadOnShow;
|
|
private boolean didShowAnswerScreen;
|
|
private boolean didShowInCallScreen;
|
|
private boolean didShowVideoCallScreen;
|
|
private boolean didShowRttCallScreen;
|
|
private boolean didShowSpeakEasyScreen;
|
|
private String lastShownSpeakEasyScreenUniqueCallid = "";
|
|
private boolean dismissKeyguard;
|
|
private boolean isInShowMainInCallFragment;
|
|
private boolean isRecreating; // whether the activity is going to be recreated
|
|
private boolean isVisible;
|
|
private boolean needDismissPendingDialogs;
|
|
private boolean touchDownWhenPseudoScreenOff;
|
|
private int[] backgroundDrawableColors;
|
|
@DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
|
|
private SpeakEasyCallManager speakEasyCallManager;
|
|
private DialogFragment rttRequestDialogFragment;
|
|
|
|
public static Intent getIntent(
|
|
Context context, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
|
|
Intent intent = new Intent(Intent.ACTION_MAIN, null);
|
|
intent.setFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION | Intent.FLAG_ACTIVITY_NEW_TASK);
|
|
intent.setClass(context, InCallActivity.class);
|
|
if (showDialpad) {
|
|
intent.putExtra(IntentExtraNames.SHOW_DIALPAD, true);
|
|
}
|
|
intent.putExtra(IntentExtraNames.NEW_OUTGOING_CALL, newOutgoingCall);
|
|
intent.putExtra(IntentExtraNames.FOR_FULL_SCREEN, isForFullScreen);
|
|
return intent;
|
|
}
|
|
|
|
@Override
|
|
protected void onResumeFragments() {
|
|
super.onResumeFragments();
|
|
if (needDismissPendingDialogs) {
|
|
dismissPendingDialogs();
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onCreate(Bundle bundle) {
|
|
Trace.beginSection("InCallActivity.onCreate");
|
|
super.onCreate(bundle);
|
|
|
|
preferredAccountWorkerResultListener =
|
|
DialerExecutorComponent.get(this)
|
|
.createUiListener(getFragmentManager(), "preferredAccountWorkerResultListener");
|
|
|
|
selectPhoneAccountListener = new SelectPhoneAccountListener(getApplicationContext());
|
|
|
|
if (bundle != null) {
|
|
didShowAnswerScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN);
|
|
didShowInCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN);
|
|
didShowVideoCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN);
|
|
didShowRttCallScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN);
|
|
didShowSpeakEasyScreen = bundle.getBoolean(KeysForSavedInstance.DID_SHOW_SPEAK_EASY_SCREEN);
|
|
}
|
|
|
|
setWindowFlags();
|
|
setContentView(R.layout.incall_screen);
|
|
internalResolveIntent(getIntent());
|
|
|
|
boolean isLandscape =
|
|
getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE;
|
|
boolean isRtl = ViewUtil.isRtl();
|
|
if (isLandscape) {
|
|
dialpadSlideInAnimation =
|
|
AnimationUtils.loadAnimation(
|
|
this, isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
|
|
dialpadSlideOutAnimation =
|
|
AnimationUtils.loadAnimation(
|
|
this, isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
|
|
} else {
|
|
dialpadSlideInAnimation = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom);
|
|
dialpadSlideOutAnimation =
|
|
AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom);
|
|
}
|
|
dialpadSlideInAnimation.setInterpolator(AnimUtils.EASE_IN);
|
|
dialpadSlideOutAnimation.setInterpolator(AnimUtils.EASE_OUT);
|
|
dialpadSlideOutAnimation.setAnimationListener(
|
|
new AnimationListenerAdapter() {
|
|
@Override
|
|
public void onAnimationEnd(Animation animation) {
|
|
hideDialpadFragment();
|
|
}
|
|
});
|
|
|
|
if (bundle != null && showDialpadRequest == DIALPAD_REQUEST_NONE) {
|
|
// If the dialpad was shown before, set related variables so that it can be shown and
|
|
// populated with the previous DTMF text during onResume().
|
|
if (bundle.containsKey(IntentExtraNames.SHOW_DIALPAD)) {
|
|
boolean showDialpad = bundle.getBoolean(IntentExtraNames.SHOW_DIALPAD);
|
|
showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
|
|
animateDialpadOnShow = false;
|
|
}
|
|
dtmfTextToPrepopulate = bundle.getString(KeysForSavedInstance.DIALPAD_TEXT);
|
|
|
|
SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment =
|
|
(SelectPhoneAccountDialogFragment)
|
|
getFragmentManager().findFragmentByTag(Tags.SELECT_ACCOUNT_FRAGMENT);
|
|
if (selectPhoneAccountDialogFragment != null) {
|
|
selectPhoneAccountDialogFragment.setListener(selectPhoneAccountListener);
|
|
}
|
|
}
|
|
|
|
inCallOrientationEventListener = new InCallOrientationEventListener(this);
|
|
|
|
getWindow()
|
|
.getDecorView()
|
|
.setSystemUiVisibility(
|
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION);
|
|
|
|
pseudoBlackScreenOverlay = findViewById(R.id.psuedo_black_screen_overlay);
|
|
sendBroadcast(CallPendingActivity.getFinishBroadcast());
|
|
Trace.endSection();
|
|
MetricsComponent.get(this)
|
|
.metrics()
|
|
.stopTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_INCOMING);
|
|
MetricsComponent.get(this)
|
|
.metrics()
|
|
.stopTimer(Metrics.ON_CALL_ADDED_TO_ON_INCALL_UI_SHOWN_OUTGOING);
|
|
}
|
|
|
|
private void setWindowFlags() {
|
|
// Allow the activity to be shown when the screen is locked and filter out touch events that are
|
|
// "too fat".
|
|
int flags =
|
|
WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
|
|
| WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
|
|
|
|
// When the audio stream is not via Bluetooth, turn on the screen once the activity is shown.
|
|
// When the audio stream is via Bluetooth, turn on the screen only for an incoming call.
|
|
final int audioRoute = getAudioRoute();
|
|
if (audioRoute != CallAudioState.ROUTE_BLUETOOTH
|
|
|| CallList.getInstance().getIncomingCall() != null) {
|
|
flags |= WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON;
|
|
}
|
|
|
|
getWindow().addFlags(flags);
|
|
}
|
|
|
|
private static int getAudioRoute() {
|
|
if (audioRouteForTesting.isPresent()) {
|
|
return audioRouteForTesting.get();
|
|
}
|
|
|
|
return AudioModeProvider.getInstance().getAudioState().getRoute();
|
|
}
|
|
|
|
@VisibleForTesting(otherwise = VisibleForTesting.NONE)
|
|
public static void setAudioRouteForTesting(int audioRoute) {
|
|
audioRouteForTesting = Optional.of(audioRoute);
|
|
}
|
|
|
|
private void internalResolveIntent(Intent intent) {
|
|
if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
|
|
return;
|
|
}
|
|
|
|
if (intent.hasExtra(IntentExtraNames.SHOW_DIALPAD)) {
|
|
// IntentExtraNames.SHOW_DIALPAD can be used to specify whether the DTMF dialpad should be
|
|
// initially visible. If the extra is absent, leave the dialpad in its previous state.
|
|
boolean showDialpad = intent.getBooleanExtra(IntentExtraNames.SHOW_DIALPAD, false);
|
|
relaunchedFromDialer(showDialpad);
|
|
}
|
|
|
|
DialerCall outgoingCall = CallList.getInstance().getOutgoingCall();
|
|
if (outgoingCall == null) {
|
|
outgoingCall = CallList.getInstance().getPendingOutgoingCall();
|
|
}
|
|
if (intent.getBooleanExtra(IntentExtraNames.NEW_OUTGOING_CALL, false)) {
|
|
intent.removeExtra(IntentExtraNames.NEW_OUTGOING_CALL);
|
|
|
|
// InCallActivity is responsible for disconnecting a new outgoing call if there is no way of
|
|
// making it (i.e. no valid call capable accounts).
|
|
if (InCallPresenter.isCallWithNoValidAccounts(outgoingCall)) {
|
|
LogUtil.i(
|
|
"InCallActivity.internalResolveIntent", "Call with no valid accounts, disconnecting");
|
|
outgoingCall.disconnect();
|
|
}
|
|
|
|
dismissKeyguard(true);
|
|
}
|
|
|
|
if (showPhoneAccountSelectionDialog()) {
|
|
hideMainInCallFragment();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* When relaunching from the dialer app, {@code showDialpad} indicates whether the dialpad should
|
|
* be shown on launch.
|
|
*
|
|
* @param showDialpad {@code true} to indicate the dialpad should be shown on launch, and {@code
|
|
* false} to indicate no change should be made to the dialpad visibility.
|
|
*/
|
|
private void relaunchedFromDialer(boolean showDialpad) {
|
|
showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_NONE;
|
|
animateDialpadOnShow = true;
|
|
|
|
if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
|
|
// If there's only one line in use, AND it's on hold, then we're sure the user
|
|
// wants to use the dialpad toward the exact line, so un-hold the holding line.
|
|
DialerCall call = CallList.getInstance().getActiveOrBackgroundCall();
|
|
if (call != null && call.getState() == DialerCallState.ONHOLD) {
|
|
call.unhold();
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Show a phone account selection dialog if there is a call waiting for phone account selection.
|
|
*
|
|
* @return true if the dialog was shown.
|
|
*/
|
|
private boolean showPhoneAccountSelectionDialog() {
|
|
DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
|
|
if (waitingForAccountCall == null) {
|
|
return false;
|
|
}
|
|
|
|
PreferredAccountWorker preferredAccountWorker =
|
|
PreferredSimComponent.get(this).preferredAccountWorker();
|
|
|
|
Bundle extras = waitingForAccountCall.getIntentExtras();
|
|
List<PhoneAccountHandle> phoneAccountHandles =
|
|
extras == null
|
|
? new ArrayList<>()
|
|
: extras.getParcelableArrayList(Call.AVAILABLE_PHONE_ACCOUNTS);
|
|
|
|
ListenableFuture<PreferredAccountWorker.Result> preferredAccountFuture =
|
|
preferredAccountWorker.selectAccount(
|
|
waitingForAccountCall.getNumber(), phoneAccountHandles);
|
|
preferredAccountWorkerResultListener.listen(
|
|
this,
|
|
preferredAccountFuture,
|
|
result -> {
|
|
String callId = waitingForAccountCall.getId();
|
|
if (result.getSelectedPhoneAccountHandle().isPresent()) {
|
|
selectPhoneAccountListener.onPhoneAccountSelected(
|
|
result.getSelectedPhoneAccountHandle().get(), false, callId);
|
|
return;
|
|
}
|
|
|
|
if (!isVisible()) {
|
|
LogUtil.i(
|
|
"InCallActivity.showPhoneAccountSelectionDialog",
|
|
"activity ended before result returned");
|
|
return;
|
|
}
|
|
|
|
waitingForAccountCall.setPreferredAccountRecorder(
|
|
new PreferredAccountRecorder(
|
|
waitingForAccountCall.getNumber(),
|
|
result.getSuggestion().orNull(),
|
|
result.getDataId().orNull()));
|
|
selectPhoneAccountDialogFragment =
|
|
SelectPhoneAccountDialogFragment.newInstance(
|
|
result.getDialogOptionsBuilder().get().setCallId(callId).build(),
|
|
selectPhoneAccountListener);
|
|
selectPhoneAccountDialogFragment.show(getFragmentManager(), Tags.SELECT_ACCOUNT_FRAGMENT);
|
|
},
|
|
throwable -> {
|
|
throw new RuntimeException(throwable);
|
|
});
|
|
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void onSaveInstanceState(Bundle out) {
|
|
LogUtil.enterBlock("InCallActivity.onSaveInstanceState");
|
|
|
|
// TODO: DialpadFragment should handle this as part of its own state
|
|
out.putBoolean(IntentExtraNames.SHOW_DIALPAD, isDialpadVisible());
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
if (dialpadFragment != null) {
|
|
out.putString(KeysForSavedInstance.DIALPAD_TEXT, dialpadFragment.getDtmfText());
|
|
}
|
|
|
|
out.putBoolean(KeysForSavedInstance.DID_SHOW_ANSWER_SCREEN, didShowAnswerScreen);
|
|
out.putBoolean(KeysForSavedInstance.DID_SHOW_IN_CALL_SCREEN, didShowInCallScreen);
|
|
out.putBoolean(KeysForSavedInstance.DID_SHOW_VIDEO_CALL_SCREEN, didShowVideoCallScreen);
|
|
out.putBoolean(KeysForSavedInstance.DID_SHOW_RTT_CALL_SCREEN, didShowRttCallScreen);
|
|
out.putBoolean(KeysForSavedInstance.DID_SHOW_SPEAK_EASY_SCREEN, didShowSpeakEasyScreen);
|
|
|
|
super.onSaveInstanceState(out);
|
|
isVisible = false;
|
|
}
|
|
|
|
@Override
|
|
protected void onStart() {
|
|
Trace.beginSection("InCallActivity.onStart");
|
|
super.onStart();
|
|
|
|
isVisible = true;
|
|
showMainInCallFragment();
|
|
|
|
InCallPresenter.getInstance().setActivity(this);
|
|
enableInCallOrientationEventListener(
|
|
getRequestedOrientation()
|
|
== InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
|
|
InCallPresenter.getInstance().onActivityStarted();
|
|
|
|
if (!isRecreating) {
|
|
InCallPresenter.getInstance().onUiShowing(true);
|
|
}
|
|
|
|
if (isInMultiWindowMode() && !getResources().getBoolean(R.bool.incall_dialpad_allowed)) {
|
|
// Hide the dialpad because there may not be enough room
|
|
showDialpadFragment(false, false);
|
|
}
|
|
|
|
Trace.endSection();
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
Trace.beginSection("InCallActivity.onResume");
|
|
super.onResume();
|
|
|
|
if (!InCallPresenter.getInstance().isReadyForTearDown()) {
|
|
updateTaskDescription();
|
|
}
|
|
|
|
// If there is a pending request to show or hide the dialpad, handle that now.
|
|
if (showDialpadRequest != DIALPAD_REQUEST_NONE) {
|
|
if (showDialpadRequest == DIALPAD_REQUEST_SHOW) {
|
|
// Exit fullscreen so that the user has access to the dialpad hide/show button.
|
|
// This is important when showing the dialpad from within dialer.
|
|
InCallPresenter.getInstance().setFullScreen(false /* isFullScreen */, true /* force */);
|
|
|
|
showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
|
|
animateDialpadOnShow = false;
|
|
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
if (dialpadFragment != null) {
|
|
dialpadFragment.setDtmfText(dtmfTextToPrepopulate);
|
|
dtmfTextToPrepopulate = null;
|
|
}
|
|
} else {
|
|
LogUtil.i("InCallActivity.onResume", "Force-hide the dialpad");
|
|
if (getDialpadFragment() != null) {
|
|
showDialpadFragment(false /* show */, false /* animate */);
|
|
}
|
|
}
|
|
showDialpadRequest = DIALPAD_REQUEST_NONE;
|
|
}
|
|
|
|
CallList.getInstance()
|
|
.onInCallUiShown(getIntent().getBooleanExtra(IntentExtraNames.FOR_FULL_SCREEN, false));
|
|
|
|
PseudoScreenState pseudoScreenState = InCallPresenter.getInstance().getPseudoScreenState();
|
|
pseudoScreenState.addListener(this);
|
|
onPseudoScreenStateChanged(pseudoScreenState.isOn());
|
|
Trace.endSection();
|
|
// add 1 sec delay to get memory snapshot so that dialer wont react slowly on resume.
|
|
ThreadUtil.postDelayedOnUiThread(
|
|
() ->
|
|
MetricsComponent.get(this)
|
|
.metrics()
|
|
.recordMemory(Metrics.INCALL_ACTIVITY_ON_RESUME_MEMORY_EVENT_NAME),
|
|
1000);
|
|
}
|
|
|
|
@Override
|
|
protected void onPause() {
|
|
Trace.beginSection("InCallActivity.onPause");
|
|
super.onPause();
|
|
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
if (dialpadFragment != null) {
|
|
dialpadFragment.onDialerKeyUp(null);
|
|
}
|
|
|
|
InCallPresenter.getInstance().getPseudoScreenState().removeListener(this);
|
|
Trace.endSection();
|
|
}
|
|
|
|
@Override
|
|
protected void onStop() {
|
|
Trace.beginSection("InCallActivity.onStop");
|
|
isVisible = false;
|
|
super.onStop();
|
|
|
|
// Disconnects the call waiting for a phone account when the activity is hidden (e.g., after the
|
|
// user presses the home button).
|
|
// Without this the pending call will get stuck on phone account selection and new calls can't
|
|
// be created.
|
|
// Skip this when the screen is locked since the activity may complete its current life cycle
|
|
// and restart.
|
|
if (!isRecreating && !getSystemService(KeyguardManager.class).isKeyguardLocked()) {
|
|
DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
|
|
if (waitingForAccountCall != null) {
|
|
waitingForAccountCall.disconnect();
|
|
}
|
|
}
|
|
|
|
enableInCallOrientationEventListener(false);
|
|
InCallPresenter.getInstance().updateIsChangingConfigurations();
|
|
InCallPresenter.getInstance().onActivityStopped();
|
|
if (!isRecreating) {
|
|
InCallPresenter.getInstance().onUiShowing(false);
|
|
}
|
|
if (errorDialog != null) {
|
|
errorDialog.dismiss();
|
|
}
|
|
|
|
if (isFinishing()) {
|
|
InCallPresenter.getInstance().unsetActivity(this);
|
|
}
|
|
|
|
Trace.endSection();
|
|
}
|
|
|
|
@Override
|
|
protected void onDestroy() {
|
|
Trace.beginSection("InCallActivity.onDestroy");
|
|
super.onDestroy();
|
|
|
|
InCallPresenter.getInstance().unsetActivity(this);
|
|
InCallPresenter.getInstance().updateIsChangingConfigurations();
|
|
Trace.endSection();
|
|
}
|
|
|
|
@Override
|
|
public void finish() {
|
|
if (shouldCloseActivityOnFinish()) {
|
|
// When user select incall ui from recents after the call is disconnected, it tries to launch
|
|
// a new InCallActivity but InCallPresenter is already teared down at this point, which causes
|
|
// crash.
|
|
// By calling finishAndRemoveTask() instead of finish() the task associated with
|
|
// InCallActivity is cleared completely. So system won't try to create a new InCallActivity in
|
|
// this case.
|
|
//
|
|
// Calling finish won't clear the task and normally when an activity finishes it shouldn't
|
|
// clear the task since there could be parent activity in the same task that's still alive.
|
|
// But InCallActivity is special since it's singleInstance which means it's root activity and
|
|
// only instance of activity in the task. So it should be safe to also remove task when
|
|
// finishing.
|
|
// It's also necessary in the sense of it's excluded from recents. So whenever the activity
|
|
// finishes, the task should also be removed since it doesn't make sense to go back to it in
|
|
// anyway anymore.
|
|
super.finishAndRemoveTask();
|
|
}
|
|
}
|
|
|
|
private boolean shouldCloseActivityOnFinish() {
|
|
if (!isVisible) {
|
|
LogUtil.i(
|
|
"InCallActivity.shouldCloseActivityOnFinish",
|
|
"allowing activity to be closed because it's not visible");
|
|
return true;
|
|
}
|
|
|
|
if (InCallPresenter.getInstance().isInCallUiLocked()) {
|
|
LogUtil.i(
|
|
"InCallActivity.shouldCloseActivityOnFinish",
|
|
"in call ui is locked, not closing activity");
|
|
return false;
|
|
}
|
|
|
|
LogUtil.i(
|
|
"InCallActivity.shouldCloseActivityOnFinish",
|
|
"activity is visible and has no locks, allowing activity to close");
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
protected void onNewIntent(Intent intent) {
|
|
LogUtil.enterBlock("InCallActivity.onNewIntent");
|
|
|
|
// If the screen is off, we need to make sure it gets turned on for incoming calls.
|
|
// This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works
|
|
// when the activity is first created. Therefore, to ensure the screen is turned on
|
|
// for the call waiting case, we recreate() the current activity. There should be no jank from
|
|
// this since the screen is already off and will remain so until our new activity is up.
|
|
if (!isVisible) {
|
|
onNewIntent(intent, true /* isRecreating */);
|
|
LogUtil.i("InCallActivity.onNewIntent", "Restarting InCallActivity to force screen on.");
|
|
recreate();
|
|
} else {
|
|
onNewIntent(intent, false /* isRecreating */);
|
|
}
|
|
}
|
|
|
|
@VisibleForTesting
|
|
void onNewIntent(Intent intent, boolean isRecreating) {
|
|
this.isRecreating = isRecreating;
|
|
|
|
// We're being re-launched with a new Intent. Since it's possible for a single InCallActivity
|
|
// instance to persist indefinitely (even if we finish() ourselves), this sequence can
|
|
// happen any time the InCallActivity needs to be displayed.
|
|
|
|
// Stash away the new intent so that we can get it in the future by calling getIntent().
|
|
// Otherwise getIntent() will return the original Intent from when we first got created.
|
|
setIntent(intent);
|
|
|
|
// Activities are always paused before receiving a new intent, so we can count on our onResume()
|
|
// method being called next.
|
|
|
|
// Just like in onCreate(), handle the intent.
|
|
// Skip if InCallActivity is going to be recreated since this will be called in onCreate().
|
|
if (!isRecreating) {
|
|
internalResolveIntent(intent);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onBackPressed() {
|
|
LogUtil.enterBlock("InCallActivity.onBackPressed");
|
|
|
|
if (!isVisible) {
|
|
return;
|
|
}
|
|
|
|
if (!getCallCardFragmentVisible()) {
|
|
return;
|
|
}
|
|
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
if (dialpadFragment != null && dialpadFragment.isVisible()) {
|
|
showDialpadFragment(false /* show */, true /* animate */);
|
|
return;
|
|
}
|
|
|
|
if (CallList.getInstance().getIncomingCall() != null) {
|
|
LogUtil.i(
|
|
"InCallActivity.onBackPressed",
|
|
"Ignore the press of the back key when an incoming call is ringing");
|
|
return;
|
|
}
|
|
|
|
// Nothing special to do. Fall back to the default behavior.
|
|
super.onBackPressed();
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
LogUtil.i("InCallActivity.onOptionsItemSelected", "item: " + item);
|
|
if (item.getItemId() == android.R.id.home) {
|
|
onBackPressed();
|
|
return true;
|
|
}
|
|
return super.onOptionsItemSelected(item);
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyUp(int keyCode, KeyEvent event) {
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
if (dialpadFragment != null
|
|
&& dialpadFragment.isVisible()
|
|
&& dialpadFragment.onDialerKeyUp(event)) {
|
|
return true;
|
|
}
|
|
|
|
if (keyCode == KeyEvent.KEYCODE_CALL) {
|
|
// Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
|
|
return true;
|
|
}
|
|
|
|
return super.onKeyUp(keyCode, event);
|
|
}
|
|
|
|
@Override
|
|
public boolean onKeyDown(int keyCode, KeyEvent event) {
|
|
switch (keyCode) {
|
|
case KeyEvent.KEYCODE_CALL:
|
|
if (!InCallPresenter.getInstance().handleCallKey()) {
|
|
LogUtil.e(
|
|
"InCallActivity.onKeyDown",
|
|
"InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
|
|
}
|
|
// Always consume KEYCODE_CALL to ensure the PhoneWindow won't do anything with it.
|
|
return true;
|
|
|
|
// Note that KEYCODE_ENDCALL isn't handled here as the standard system-wide handling of it
|
|
// is exactly what's needed, namely
|
|
// (1) "hang up" if there's an active call, or
|
|
// (2) "don't answer" if there's an incoming call.
|
|
// (See PhoneWindowManager for implementation details.)
|
|
|
|
case KeyEvent.KEYCODE_CAMERA:
|
|
// Consume KEYCODE_CAMERA since it's easy to accidentally press the camera button.
|
|
return true;
|
|
|
|
case KeyEvent.KEYCODE_VOLUME_UP:
|
|
case KeyEvent.KEYCODE_VOLUME_DOWN:
|
|
case KeyEvent.KEYCODE_VOLUME_MUTE:
|
|
// Ringer silencing handled by PhoneWindowManager.
|
|
break;
|
|
|
|
case KeyEvent.KEYCODE_MUTE:
|
|
TelecomAdapter.getInstance()
|
|
.mute(!AudioModeProvider.getInstance().getAudioState().isMuted());
|
|
return true;
|
|
|
|
case KeyEvent.KEYCODE_SLASH:
|
|
// When verbose logging is enabled, dump the view for debugging/testing purposes.
|
|
if (LogUtil.isVerboseEnabled()) {
|
|
View decorView = getWindow().getDecorView();
|
|
LogUtil.v("InCallActivity.onKeyDown", "View dump:\n%s", decorView);
|
|
return true;
|
|
}
|
|
break;
|
|
|
|
case KeyEvent.KEYCODE_EQUALS:
|
|
break;
|
|
|
|
default: // fall out
|
|
}
|
|
|
|
// Pass other key events to DialpadFragment's "onDialerKeyDown" method in case the user types
|
|
// in DTMF (Dual-tone multi-frequency signaling) code.
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
if (dialpadFragment != null
|
|
&& dialpadFragment.isVisible()
|
|
&& dialpadFragment.onDialerKeyDown(event)) {
|
|
return true;
|
|
}
|
|
|
|
return super.onKeyDown(keyCode, event);
|
|
}
|
|
|
|
public boolean isInCallScreenAnimating() {
|
|
return false;
|
|
}
|
|
|
|
public void showConferenceFragment(boolean show) {
|
|
if (show) {
|
|
startActivity(new Intent(this, ManageConferenceActivity.class));
|
|
}
|
|
}
|
|
|
|
public void showDialpadFragment(boolean show, boolean animate) {
|
|
if (show == isDialpadVisible()) {
|
|
return;
|
|
}
|
|
|
|
FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
|
|
if (dialpadFragmentManager == null) {
|
|
LogUtil.i("InCallActivity.showDialpadFragment", "Unable to obtain a FragmentManager");
|
|
return;
|
|
}
|
|
|
|
if (!animate) {
|
|
if (show) {
|
|
showDialpadFragment();
|
|
} else {
|
|
hideDialpadFragment();
|
|
}
|
|
} else {
|
|
if (show) {
|
|
showDialpadFragment();
|
|
getDialpadFragment().animateShowDialpad();
|
|
}
|
|
getDialpadFragment()
|
|
.getView()
|
|
.startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
|
|
}
|
|
|
|
ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
|
|
if (sensor != null) {
|
|
sensor.onDialpadVisible(show);
|
|
}
|
|
showDialpadRequest = DIALPAD_REQUEST_NONE;
|
|
}
|
|
|
|
private void showDialpadFragment() {
|
|
FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
|
|
if (dialpadFragmentManager == null) {
|
|
return;
|
|
}
|
|
|
|
FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
if (dialpadFragment == null) {
|
|
dialpadFragment = new DialpadFragment();
|
|
transaction.add(getDialpadContainerId(), dialpadFragment, Tags.DIALPAD_FRAGMENT);
|
|
} else {
|
|
transaction.show(dialpadFragment);
|
|
dialpadFragment.setUserVisibleHint(true);
|
|
}
|
|
// RTT call screen doesn't show end call button inside dialpad, thus the space reserved for end
|
|
// call button should be removed.
|
|
dialpadFragment.setShouldShowEndCallSpace(didShowInCallScreen);
|
|
transaction.commitAllowingStateLoss();
|
|
dialpadFragmentManager.executePendingTransactions();
|
|
|
|
Logger.get(this).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, this);
|
|
getInCallOrRttCallScreen().onInCallScreenDialpadVisibilityChange(true);
|
|
}
|
|
|
|
private void hideDialpadFragment() {
|
|
FragmentManager dialpadFragmentManager = getDialpadFragmentManager();
|
|
if (dialpadFragmentManager == null) {
|
|
return;
|
|
}
|
|
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
if (dialpadFragment != null) {
|
|
FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
|
|
transaction.hide(dialpadFragment);
|
|
transaction.commitAllowingStateLoss();
|
|
dialpadFragmentManager.executePendingTransactions();
|
|
dialpadFragment.setUserVisibleHint(false);
|
|
getInCallOrRttCallScreen().onInCallScreenDialpadVisibilityChange(false);
|
|
}
|
|
}
|
|
|
|
public boolean isDialpadVisible() {
|
|
DialpadFragment dialpadFragment = getDialpadFragment();
|
|
return dialpadFragment != null
|
|
&& dialpadFragment.isAdded()
|
|
&& !dialpadFragment.isHidden()
|
|
&& dialpadFragment.getView() != null
|
|
&& dialpadFragment.getUserVisibleHint();
|
|
}
|
|
|
|
/** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
|
|
@Nullable
|
|
private DialpadFragment getDialpadFragment() {
|
|
FragmentManager fragmentManager = getDialpadFragmentManager();
|
|
if (fragmentManager == null) {
|
|
return null;
|
|
}
|
|
return (DialpadFragment) fragmentManager.findFragmentByTag(Tags.DIALPAD_FRAGMENT);
|
|
}
|
|
|
|
public void onForegroundCallChanged(DialerCall newForegroundCall) {
|
|
updateTaskDescription();
|
|
|
|
if (newForegroundCall == null || !didShowAnswerScreen) {
|
|
LogUtil.v("InCallActivity.onForegroundCallChanged", "resetting background color");
|
|
updateWindowBackgroundColor(0 /* progress */);
|
|
}
|
|
}
|
|
|
|
private void updateTaskDescription() {
|
|
int color =
|
|
getResources().getBoolean(R.bool.is_layout_landscape)
|
|
? ResourcesCompat.getColor(
|
|
getResources(), R.color.statusbar_background_color, getTheme())
|
|
: InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
|
|
setTaskDescription(
|
|
new TaskDescription(
|
|
getResources().getString(R.string.notification_ongoing_call), null /* icon */, color));
|
|
}
|
|
|
|
public void updateWindowBackgroundColor(@FloatRange(from = -1f, to = 1.0f) float progress) {
|
|
ThemeColorManager themeColorManager = InCallPresenter.getInstance().getThemeColorManager();
|
|
@ColorInt int top;
|
|
@ColorInt int middle;
|
|
@ColorInt int bottom;
|
|
@ColorInt int gray = 0x66000000;
|
|
|
|
if (isInMultiWindowMode()) {
|
|
top = themeColorManager.getBackgroundColorSolid();
|
|
middle = themeColorManager.getBackgroundColorSolid();
|
|
bottom = themeColorManager.getBackgroundColorSolid();
|
|
} else {
|
|
top = themeColorManager.getBackgroundColorTop();
|
|
middle = themeColorManager.getBackgroundColorMiddle();
|
|
bottom = themeColorManager.getBackgroundColorBottom();
|
|
}
|
|
|
|
if (progress < 0) {
|
|
float correctedProgress = Math.abs(progress);
|
|
top = ColorUtils.blendARGB(top, gray, correctedProgress);
|
|
middle = ColorUtils.blendARGB(middle, gray, correctedProgress);
|
|
bottom = ColorUtils.blendARGB(bottom, gray, correctedProgress);
|
|
}
|
|
|
|
boolean backgroundDirty = false;
|
|
if (backgroundDrawable == null) {
|
|
backgroundDrawableColors = new int[] {top, middle, bottom};
|
|
backgroundDrawable = new GradientDrawable(Orientation.TOP_BOTTOM, backgroundDrawableColors);
|
|
backgroundDirty = true;
|
|
} else {
|
|
if (backgroundDrawableColors[0] != top) {
|
|
backgroundDrawableColors[0] = top;
|
|
backgroundDirty = true;
|
|
}
|
|
if (backgroundDrawableColors[1] != middle) {
|
|
backgroundDrawableColors[1] = middle;
|
|
backgroundDirty = true;
|
|
}
|
|
if (backgroundDrawableColors[2] != bottom) {
|
|
backgroundDrawableColors[2] = bottom;
|
|
backgroundDirty = true;
|
|
}
|
|
if (backgroundDirty) {
|
|
backgroundDrawable.setColors(backgroundDrawableColors);
|
|
}
|
|
}
|
|
|
|
if (backgroundDirty) {
|
|
getWindow().setBackgroundDrawable(backgroundDrawable);
|
|
}
|
|
}
|
|
|
|
public boolean isVisible() {
|
|
return isVisible;
|
|
}
|
|
|
|
public boolean getCallCardFragmentVisible() {
|
|
return didShowInCallScreen
|
|
|| didShowVideoCallScreen
|
|
|| didShowRttCallScreen
|
|
|| didShowSpeakEasyScreen;
|
|
}
|
|
|
|
public void dismissKeyguard(boolean dismiss) {
|
|
if (dismissKeyguard == dismiss) {
|
|
return;
|
|
}
|
|
|
|
dismissKeyguard = dismiss;
|
|
if (dismiss) {
|
|
getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
|
|
} else {
|
|
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
|
|
}
|
|
}
|
|
|
|
public void showDialogForPostCharWait(String callId, String chars) {
|
|
PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
|
|
fragment.show(getSupportFragmentManager(), Tags.POST_CHAR_DIALOG_FRAGMENT);
|
|
}
|
|
|
|
public void showDialogOrToastForDisconnectedCall(DisconnectMessage disconnectMessage) {
|
|
LogUtil.i(
|
|
"InCallActivity.showDialogOrToastForDisconnectedCall",
|
|
"disconnect cause: %s",
|
|
disconnectMessage);
|
|
|
|
if (disconnectMessage.dialog == null || isFinishing()) {
|
|
return;
|
|
}
|
|
|
|
dismissPendingDialogs();
|
|
|
|
// Show a toast if the app is in background when a dialog can't be visible.
|
|
if (!isVisible()) {
|
|
Toast.makeText(getApplicationContext(), disconnectMessage.toastMessage, Toast.LENGTH_LONG)
|
|
.show();
|
|
return;
|
|
}
|
|
|
|
// Show the dialog.
|
|
errorDialog = disconnectMessage.dialog;
|
|
InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("showErrorDialog");
|
|
disconnectMessage.dialog.setOnDismissListener(
|
|
dialogInterface -> {
|
|
lock.release();
|
|
onDialogDismissed();
|
|
});
|
|
disconnectMessage.dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
|
|
disconnectMessage.dialog.show();
|
|
}
|
|
|
|
private void onDialogDismissed() {
|
|
errorDialog = null;
|
|
CallList.getInstance().onErrorDialogDismissed();
|
|
}
|
|
|
|
public void dismissPendingDialogs() {
|
|
LogUtil.enterBlock("InCallActivity.dismissPendingDialogs");
|
|
|
|
if (!isVisible) {
|
|
// Defer the dismissing action as the activity is not visible and onSaveInstanceState may have
|
|
// been called.
|
|
LogUtil.i(
|
|
"InCallActivity.dismissPendingDialogs", "defer actions since activity is not visible");
|
|
needDismissPendingDialogs = true;
|
|
return;
|
|
}
|
|
|
|
// Dismiss the error dialog
|
|
if (errorDialog != null) {
|
|
errorDialog.dismiss();
|
|
errorDialog = null;
|
|
}
|
|
|
|
// Dismiss the phone account selection dialog
|
|
if (selectPhoneAccountDialogFragment != null) {
|
|
selectPhoneAccountDialogFragment.dismiss();
|
|
selectPhoneAccountDialogFragment = null;
|
|
}
|
|
|
|
// Dismiss the dialog for international call on WiFi
|
|
InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
|
|
(InternationalCallOnWifiDialogFragment)
|
|
getSupportFragmentManager().findFragmentByTag(Tags.INTERNATIONAL_CALL_ON_WIFI);
|
|
if (internationalCallOnWifiFragment != null) {
|
|
internationalCallOnWifiFragment.dismiss();
|
|
}
|
|
|
|
// Dismiss the answer screen
|
|
AnswerScreen answerScreen = getAnswerScreen();
|
|
if (answerScreen != null) {
|
|
answerScreen.dismissPendingDialogs();
|
|
}
|
|
|
|
needDismissPendingDialogs = false;
|
|
}
|
|
|
|
private void enableInCallOrientationEventListener(boolean enable) {
|
|
if (enable) {
|
|
inCallOrientationEventListener.enable(true /* notifyDeviceOrientationChange */);
|
|
} else {
|
|
inCallOrientationEventListener.disable();
|
|
}
|
|
}
|
|
|
|
public void setExcludeFromRecents(boolean exclude) {
|
|
int taskId = getTaskId();
|
|
|
|
List<AppTask> tasks = getSystemService(ActivityManager.class).getAppTasks();
|
|
for (AppTask task : tasks) {
|
|
try {
|
|
if (task.getTaskInfo().id == taskId) {
|
|
task.setExcludeFromRecents(exclude);
|
|
}
|
|
} catch (RuntimeException e) {
|
|
LogUtil.e("InCallActivity.setExcludeFromRecents", "RuntimeException:\n%s", e);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
public FragmentManager getDialpadFragmentManager() {
|
|
InCallScreen inCallScreen = getInCallOrRttCallScreen();
|
|
if (inCallScreen != null) {
|
|
return inCallScreen.getInCallScreenFragment().getChildFragmentManager();
|
|
}
|
|
return null;
|
|
}
|
|
|
|
public int getDialpadContainerId() {
|
|
return getInCallOrRttCallScreen().getAnswerAndDialpadContainerResourceId();
|
|
}
|
|
|
|
@Override
|
|
public AnswerScreenDelegate newAnswerScreenDelegate(AnswerScreen answerScreen) {
|
|
DialerCall call = CallList.getInstance().getCallById(answerScreen.getCallId());
|
|
if (call == null) {
|
|
// This is a work around for a bug where we attempt to create a new delegate after the call
|
|
// has already been removed. An example of when this can happen is:
|
|
// 1. incoming video call in landscape mode
|
|
// 2. remote party hangs up
|
|
// 3. activity switches from landscape to portrait
|
|
// At step #3 the answer fragment will try to create a new answer delegate but the call won't
|
|
// exist. In this case we'll simply return a stub delegate that does nothing. This is ok
|
|
// because this new state is transient and the activity will be destroyed soon.
|
|
LogUtil.i("InCallActivity.onPrimaryCallStateChanged", "call doesn't exist, using stub");
|
|
return new AnswerScreenPresenterStub();
|
|
} else {
|
|
return new AnswerScreenPresenter(
|
|
this, answerScreen, CallList.getInstance().getCallById(answerScreen.getCallId()));
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public InCallScreenDelegate newInCallScreenDelegate() {
|
|
return new CallCardPresenter(this);
|
|
}
|
|
|
|
@Override
|
|
public InCallButtonUiDelegate newInCallButtonUiDelegate() {
|
|
return new CallButtonPresenter(this);
|
|
}
|
|
|
|
@Override
|
|
public VideoCallScreenDelegate newVideoCallScreenDelegate(VideoCallScreen videoCallScreen) {
|
|
DialerCall dialerCall = CallList.getInstance().getCallById(videoCallScreen.getCallId());
|
|
if (dialerCall != null && dialerCall.getVideoTech().shouldUseSurfaceView()) {
|
|
return dialerCall.getVideoTech().createVideoCallScreenDelegate(this, videoCallScreen);
|
|
}
|
|
return new VideoCallPresenter();
|
|
}
|
|
|
|
public void onPrimaryCallStateChanged() {
|
|
Trace.beginSection("InCallActivity.onPrimaryCallStateChanged");
|
|
showMainInCallFragment();
|
|
Trace.endSection();
|
|
}
|
|
|
|
public void showDialogOrToastForWifiHandoverFailure(DialerCall call) {
|
|
if (call.showWifiHandoverAlertAsToast()) {
|
|
Toast.makeText(this, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
|
|
.show();
|
|
return;
|
|
}
|
|
|
|
dismissPendingDialogs();
|
|
|
|
AlertDialog.Builder builder =
|
|
new AlertDialog.Builder(this).setTitle(R.string.video_call_lte_to_wifi_failed_title);
|
|
|
|
// This allows us to use the theme of the dialog instead of the activity
|
|
View dialogCheckBoxView =
|
|
View.inflate(builder.getContext(), R.layout.video_call_lte_to_wifi_failed, null /* root */);
|
|
CheckBox wifiHandoverFailureCheckbox =
|
|
(CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
|
|
wifiHandoverFailureCheckbox.setChecked(false);
|
|
|
|
InCallUiLock lock = InCallPresenter.getInstance().acquireInCallUiLock("WifiFailedDialog");
|
|
errorDialog =
|
|
builder
|
|
.setView(dialogCheckBoxView)
|
|
.setMessage(R.string.video_call_lte_to_wifi_failed_message)
|
|
.setOnCancelListener(dialogInterface -> onDialogDismissed())
|
|
.setPositiveButton(
|
|
android.R.string.ok,
|
|
(dialogInterface, id) -> {
|
|
call.setDoNotShowDialogForHandoffToWifiFailure(
|
|
wifiHandoverFailureCheckbox.isChecked());
|
|
dialogInterface.cancel();
|
|
onDialogDismissed();
|
|
})
|
|
.setOnDismissListener(dialogInterface -> lock.release())
|
|
.create();
|
|
errorDialog.show();
|
|
}
|
|
|
|
public void showDialogForInternationalCallOnWifi(@NonNull DialerCall call) {
|
|
InternationalCallOnWifiDialogFragment fragment =
|
|
InternationalCallOnWifiDialogFragment.newInstance(call.getId());
|
|
fragment.show(getSupportFragmentManager(), Tags.INTERNATIONAL_CALL_ON_WIFI);
|
|
}
|
|
|
|
public void showDialogForRttRequest(DialerCall call, int rttRequestId) {
|
|
LogUtil.enterBlock("InCallActivity.showDialogForRttRequest");
|
|
rttRequestDialogFragment = RttRequestDialogFragment.newInstance(call.getId(), rttRequestId);
|
|
rttRequestDialogFragment.show(getSupportFragmentManager(), Tags.RTT_REQUEST_DIALOG);
|
|
}
|
|
|
|
public void setAllowOrientationChange(boolean allowOrientationChange) {
|
|
if (this.allowOrientationChange == allowOrientationChange) {
|
|
return;
|
|
}
|
|
this.allowOrientationChange = allowOrientationChange;
|
|
if (!allowOrientationChange) {
|
|
setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_DISALLOW_ROTATION);
|
|
} else {
|
|
setRequestedOrientation(InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
|
|
}
|
|
enableInCallOrientationEventListener(allowOrientationChange);
|
|
}
|
|
|
|
public void hideMainInCallFragment() {
|
|
LogUtil.enterBlock("InCallActivity.hideMainInCallFragment");
|
|
if (getCallCardFragmentVisible()) {
|
|
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
|
hideInCallScreenFragment(transaction);
|
|
hideVideoCallScreenFragment(transaction);
|
|
transaction.commitAllowingStateLoss();
|
|
getSupportFragmentManager().executePendingTransactions();
|
|
}
|
|
}
|
|
|
|
private void showMainInCallFragment() {
|
|
Trace.beginSection("InCallActivity.showMainInCallFragment");
|
|
// If the activity's onStart method hasn't been called yet then defer doing any work.
|
|
if (!isVisible) {
|
|
LogUtil.i("InCallActivity.showMainInCallFragment", "not visible yet/anymore");
|
|
Trace.endSection();
|
|
return;
|
|
}
|
|
|
|
// Don't let this be reentrant.
|
|
if (isInShowMainInCallFragment) {
|
|
LogUtil.i("InCallActivity.showMainInCallFragment", "already in method, bailing");
|
|
Trace.endSection();
|
|
return;
|
|
}
|
|
|
|
isInShowMainInCallFragment = true;
|
|
ShouldShowUiResult shouldShowAnswerUi = getShouldShowAnswerUi();
|
|
ShouldShowUiResult shouldShowVideoUi = getShouldShowVideoUi();
|
|
ShouldShowUiResult shouldShowRttUi = getShouldShowRttUi();
|
|
ShouldShowUiResult shouldShowSpeakEasyUi = getShouldShowSpeakEasyUi();
|
|
LogUtil.i(
|
|
"InCallActivity.showMainInCallFragment",
|
|
"shouldShowAnswerUi: %b, shouldShowRttUi: %b, shouldShowVideoUi: %b, "
|
|
+ "shouldShowSpeakEasyUi: %b, didShowAnswerScreen: %b, didShowInCallScreen: %b, "
|
|
+ "didShowRttCallScreen: %b, didShowVideoCallScreen: %b, didShowSpeakEasyScreen: %b",
|
|
shouldShowAnswerUi.shouldShow,
|
|
shouldShowRttUi.shouldShow,
|
|
shouldShowVideoUi.shouldShow,
|
|
shouldShowSpeakEasyUi.shouldShow,
|
|
didShowAnswerScreen,
|
|
didShowInCallScreen,
|
|
didShowRttCallScreen,
|
|
didShowVideoCallScreen,
|
|
didShowSpeakEasyScreen);
|
|
// Only video call ui allows orientation change.
|
|
setAllowOrientationChange(shouldShowVideoUi.shouldShow);
|
|
|
|
FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
|
|
boolean didChange;
|
|
if (shouldShowAnswerUi.shouldShow) {
|
|
didChange = hideInCallScreenFragment(transaction);
|
|
didChange |= hideVideoCallScreenFragment(transaction);
|
|
didChange |= hideRttCallScreenFragment(transaction);
|
|
didChange |= hideSpeakEasyFragment(transaction);
|
|
didChange |= showAnswerScreenFragment(transaction, shouldShowAnswerUi.call);
|
|
} else if (shouldShowVideoUi.shouldShow) {
|
|
didChange = hideInCallScreenFragment(transaction);
|
|
didChange |= showVideoCallScreenFragment(transaction, shouldShowVideoUi.call);
|
|
didChange |= hideRttCallScreenFragment(transaction);
|
|
didChange |= hideSpeakEasyFragment(transaction);
|
|
didChange |= hideAnswerScreenFragment(transaction);
|
|
} else if (shouldShowRttUi.shouldShow) {
|
|
didChange = hideInCallScreenFragment(transaction);
|
|
didChange |= hideVideoCallScreenFragment(transaction);
|
|
didChange |= hideAnswerScreenFragment(transaction);
|
|
didChange |= hideSpeakEasyFragment(transaction);
|
|
didChange |= showRttCallScreenFragment(transaction, shouldShowRttUi.call);
|
|
} else if (shouldShowSpeakEasyUi.shouldShow) {
|
|
didChange = hideInCallScreenFragment(transaction);
|
|
didChange |= hideVideoCallScreenFragment(transaction);
|
|
didChange |= hideAnswerScreenFragment(transaction);
|
|
didChange |= hideRttCallScreenFragment(transaction);
|
|
didChange |= showSpeakEasyFragment(transaction, shouldShowSpeakEasyUi.call);
|
|
} else {
|
|
didChange = showInCallScreenFragment(transaction);
|
|
didChange |= hideVideoCallScreenFragment(transaction);
|
|
didChange |= hideRttCallScreenFragment(transaction);
|
|
didChange |= hideSpeakEasyFragment(transaction);
|
|
didChange |= hideAnswerScreenFragment(transaction);
|
|
}
|
|
|
|
if (didChange) {
|
|
Trace.beginSection("InCallActivity.commitTransaction");
|
|
transaction.commitNow();
|
|
Trace.endSection();
|
|
Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
|
|
}
|
|
isInShowMainInCallFragment = false;
|
|
Trace.endSection();
|
|
}
|
|
|
|
private boolean showSpeakEasyFragment(FragmentTransaction transaction, DialerCall call) {
|
|
|
|
if (didShowSpeakEasyScreen) {
|
|
if (lastShownSpeakEasyScreenUniqueCallid.equals(call.getUniqueCallId())) {
|
|
LogUtil.i("InCallActivity.showSpeakEasyFragment", "found existing fragment");
|
|
return false;
|
|
}
|
|
hideSpeakEasyFragment(transaction);
|
|
LogUtil.i("InCallActivity.showSpeakEasyFragment", "hid existing fragment");
|
|
}
|
|
|
|
Optional<Fragment> speakEasyFragment = speakEasyCallManager.getSpeakEasyFragment(call);
|
|
if (speakEasyFragment.isPresent()) {
|
|
transaction.add(R.id.main, speakEasyFragment.get(), Tags.SPEAK_EASY_SCREEN);
|
|
didShowSpeakEasyScreen = true;
|
|
lastShownSpeakEasyScreenUniqueCallid = call.getUniqueCallId();
|
|
LogUtil.i(
|
|
"InCallActivity.showSpeakEasyFragment",
|
|
"set fragment for call %s",
|
|
lastShownSpeakEasyScreenUniqueCallid);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
private Fragment getSpeakEasyScreen() {
|
|
return getSupportFragmentManager().findFragmentByTag(Tags.SPEAK_EASY_SCREEN);
|
|
}
|
|
|
|
private boolean hideSpeakEasyFragment(FragmentTransaction transaction) {
|
|
if (!didShowSpeakEasyScreen) {
|
|
return false;
|
|
}
|
|
|
|
Fragment speakEasyFragment = getSpeakEasyScreen();
|
|
|
|
if (speakEasyFragment != null) {
|
|
transaction.remove(speakEasyFragment);
|
|
didShowSpeakEasyScreen = false;
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@VisibleForTesting
|
|
public void setSpeakEasyCallManager(SpeakEasyCallManager speakEasyCallManager) {
|
|
this.speakEasyCallManager = speakEasyCallManager;
|
|
}
|
|
|
|
@Nullable
|
|
public SpeakEasyCallManager getSpeakEasyCallManager() {
|
|
if (this.speakEasyCallManager == null) {
|
|
this.speakEasyCallManager = InCallPresenter.getInstance().getSpeakEasyCallManager();
|
|
}
|
|
return speakEasyCallManager;
|
|
}
|
|
|
|
private ShouldShowUiResult getShouldShowSpeakEasyUi() {
|
|
SpeakEasyCallManager speakEasyCallManager = getSpeakEasyCallManager();
|
|
|
|
if (speakEasyCallManager == null) {
|
|
return new ShouldShowUiResult(false, null);
|
|
}
|
|
|
|
DialerCall call =
|
|
CallList.getInstance().getIncomingCall() != null
|
|
? CallList.getInstance().getIncomingCall()
|
|
: CallList.getInstance().getActiveCall();
|
|
|
|
if (call == null) {
|
|
// This is a special case where the first call is not automatically resumed
|
|
// after the second active call is remotely disconnected.
|
|
DialerCall backgroundCall = CallList.getInstance().getBackgroundCall();
|
|
if (backgroundCall != null && backgroundCall.isSpeakEasyCall()) {
|
|
LogUtil.i("InCallActivity.getShouldShowSpeakEasyUi", "taking call off hold");
|
|
|
|
backgroundCall.unhold();
|
|
return new ShouldShowUiResult(true, backgroundCall);
|
|
}
|
|
|
|
return new ShouldShowUiResult(false, call);
|
|
}
|
|
|
|
if (!call.isSpeakEasyCall() || !call.isSpeakEasyEligible()) {
|
|
return new ShouldShowUiResult(false, call);
|
|
}
|
|
|
|
Optional<Fragment> speakEasyFragment = speakEasyCallManager.getSpeakEasyFragment(call);
|
|
|
|
if (!speakEasyFragment.isPresent()) {
|
|
return new ShouldShowUiResult(false, call);
|
|
}
|
|
return new ShouldShowUiResult(true, call);
|
|
}
|
|
|
|
private ShouldShowUiResult getShouldShowAnswerUi() {
|
|
DialerCall call = CallList.getInstance().getIncomingCall();
|
|
if (call != null && !call.isSpeakEasyCall()) {
|
|
LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found incoming call");
|
|
return new ShouldShowUiResult(true, call);
|
|
}
|
|
|
|
call = CallList.getInstance().getVideoUpgradeRequestCall();
|
|
if (call != null) {
|
|
LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found video upgrade request");
|
|
return new ShouldShowUiResult(true, call);
|
|
}
|
|
|
|
// Check if we're showing the answer screen and the call is disconnected. If this condition is
|
|
// true then we won't switch from the answer UI to the in call UI. This prevents flicker when
|
|
// the user rejects an incoming call.
|
|
call = CallList.getInstance().getFirstCall();
|
|
if (call == null) {
|
|
call = CallList.getInstance().getBackgroundCall();
|
|
}
|
|
if (didShowAnswerScreen && (call == null || call.getState() == DialerCallState.DISCONNECTED)) {
|
|
LogUtil.i("InCallActivity.getShouldShowAnswerUi", "found disconnecting incoming call");
|
|
return new ShouldShowUiResult(true, call);
|
|
}
|
|
|
|
return new ShouldShowUiResult(false, null);
|
|
}
|
|
|
|
private static ShouldShowUiResult getShouldShowVideoUi() {
|
|
DialerCall call = CallList.getInstance().getFirstCall();
|
|
if (call == null) {
|
|
LogUtil.i("InCallActivity.getShouldShowVideoUi", "null call");
|
|
return new ShouldShowUiResult(false, null);
|
|
}
|
|
|
|
if (call.isVideoCall()) {
|
|
LogUtil.i("InCallActivity.getShouldShowVideoUi", "found video call");
|
|
return new ShouldShowUiResult(true, call);
|
|
}
|
|
|
|
if (call.hasSentVideoUpgradeRequest() || call.hasReceivedVideoUpgradeRequest()) {
|
|
LogUtil.i("InCallActivity.getShouldShowVideoUi", "upgrading to video");
|
|
return new ShouldShowUiResult(true, call);
|
|
}
|
|
|
|
return new ShouldShowUiResult(false, null);
|
|
}
|
|
|
|
private static ShouldShowUiResult getShouldShowRttUi() {
|
|
DialerCall call = CallList.getInstance().getFirstCall();
|
|
if (call == null) {
|
|
LogUtil.i("InCallActivity.getShouldShowRttUi", "null call");
|
|
return new ShouldShowUiResult(false, null);
|
|
}
|
|
|
|
if (call.isActiveRttCall()) {
|
|
LogUtil.i("InCallActivity.getShouldShowRttUi", "found rtt call");
|
|
return new ShouldShowUiResult(true, call);
|
|
}
|
|
|
|
if (call.hasSentRttUpgradeRequest()) {
|
|
LogUtil.i("InCallActivity.getShouldShowRttUi", "upgrading to rtt");
|
|
return new ShouldShowUiResult(true, call);
|
|
}
|
|
|
|
return new ShouldShowUiResult(false, null);
|
|
}
|
|
|
|
private boolean showAnswerScreenFragment(FragmentTransaction transaction, DialerCall call) {
|
|
// When rejecting a call the active call can become null in which case we should continue
|
|
// showing the answer screen.
|
|
if (didShowAnswerScreen && call == null) {
|
|
return false;
|
|
}
|
|
|
|
Assert.checkArgument(call != null, "didShowAnswerScreen was false but call was still null");
|
|
|
|
boolean isVideoUpgradeRequest = call.hasReceivedVideoUpgradeRequest();
|
|
|
|
// Check if we're already showing an answer screen for this call.
|
|
if (didShowAnswerScreen) {
|
|
AnswerScreen answerScreen = getAnswerScreen();
|
|
if (answerScreen.getCallId().equals(call.getId())
|
|
&& answerScreen.isVideoCall() == call.isVideoCall()
|
|
&& answerScreen.isVideoUpgradeRequest() == isVideoUpgradeRequest
|
|
&& !answerScreen.isActionTimeout()) {
|
|
LogUtil.d(
|
|
"InCallActivity.showAnswerScreenFragment",
|
|
"answer fragment exists for same call and has NOT been accepted/rejected/timed out");
|
|
return false;
|
|
}
|
|
if (answerScreen.isActionTimeout()) {
|
|
LogUtil.i(
|
|
"InCallActivity.showAnswerScreenFragment",
|
|
"answer fragment exists but has been accepted/rejected and timed out");
|
|
} else {
|
|
LogUtil.i(
|
|
"InCallActivity.showAnswerScreenFragment",
|
|
"answer fragment exists but arguments do not match");
|
|
}
|
|
hideAnswerScreenFragment(transaction);
|
|
}
|
|
|
|
// Show a new answer screen.
|
|
AnswerScreen answerScreen =
|
|
AnswerBindings.createAnswerScreen(
|
|
call.getId(),
|
|
call.isActiveRttCall(),
|
|
call.isVideoCall(),
|
|
isVideoUpgradeRequest,
|
|
call.getVideoTech().isSelfManagedCamera(),
|
|
shouldAllowAnswerAndRelease(call),
|
|
CallList.getInstance().getBackgroundCall() != null,
|
|
getSpeakEasyCallManager().isAvailable(getApplicationContext())
|
|
&& call.isSpeakEasyEligible());
|
|
transaction.add(R.id.main, answerScreen.getAnswerScreenFragment(), Tags.ANSWER_SCREEN);
|
|
|
|
Logger.get(this).logScreenView(ScreenEvent.Type.INCOMING_CALL, this);
|
|
didShowAnswerScreen = true;
|
|
return true;
|
|
}
|
|
|
|
private boolean shouldAllowAnswerAndRelease(DialerCall call) {
|
|
if (CallList.getInstance().getActiveCall() == null) {
|
|
LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "no active call");
|
|
return false;
|
|
}
|
|
if (getSystemService(TelephonyManager.class).getPhoneType()
|
|
== TelephonyManager.PHONE_TYPE_CDMA) {
|
|
LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "PHONE_TYPE_CDMA not supported");
|
|
return false;
|
|
}
|
|
if (call.isVideoCall() || call.hasReceivedVideoUpgradeRequest()) {
|
|
LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "video call");
|
|
return false;
|
|
}
|
|
if (!ConfigProviderComponent.get(this)
|
|
.getConfigProvider()
|
|
.getBoolean(ConfigNames.ANSWER_AND_RELEASE_ENABLED, true)) {
|
|
LogUtil.i("InCallActivity.shouldAllowAnswerAndRelease", "disabled by config");
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
private boolean hideAnswerScreenFragment(FragmentTransaction transaction) {
|
|
if (!didShowAnswerScreen) {
|
|
return false;
|
|
}
|
|
AnswerScreen answerScreen = getAnswerScreen();
|
|
if (answerScreen != null) {
|
|
transaction.remove(answerScreen.getAnswerScreenFragment());
|
|
}
|
|
|
|
didShowAnswerScreen = false;
|
|
return true;
|
|
}
|
|
|
|
private boolean showInCallScreenFragment(FragmentTransaction transaction) {
|
|
if (didShowInCallScreen) {
|
|
return false;
|
|
}
|
|
InCallScreen inCallScreen = InCallBindings.createInCallScreen();
|
|
transaction.add(R.id.main, inCallScreen.getInCallScreenFragment(), Tags.IN_CALL_SCREEN);
|
|
Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
|
|
didShowInCallScreen = true;
|
|
return true;
|
|
}
|
|
|
|
private boolean hideInCallScreenFragment(FragmentTransaction transaction) {
|
|
if (!didShowInCallScreen) {
|
|
return false;
|
|
}
|
|
InCallScreen inCallScreen = getInCallScreen();
|
|
if (inCallScreen != null) {
|
|
transaction.remove(inCallScreen.getInCallScreenFragment());
|
|
}
|
|
didShowInCallScreen = false;
|
|
return true;
|
|
}
|
|
|
|
private boolean showRttCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
|
|
if (didShowRttCallScreen) {
|
|
if (getRttCallScreen().getCallId().equals(call.getId())) {
|
|
return false;
|
|
}
|
|
LogUtil.i("InCallActivity.showRttCallScreenFragment", "RTT call id doesn't match");
|
|
hideRttCallScreenFragment(transaction);
|
|
}
|
|
RttCallScreen rttCallScreen = RttBindings.createRttCallScreen(call.getId());
|
|
transaction.add(R.id.main, rttCallScreen.getRttCallScreenFragment(), Tags.RTT_CALL_SCREEN);
|
|
Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
|
|
didShowRttCallScreen = true;
|
|
// In some cases such as VZW, RTT request will be automatically accepted by modem. So the dialog
|
|
// won't make any sense and should be dismissed if it's already switched to RTT.
|
|
if (rttRequestDialogFragment != null) {
|
|
LogUtil.i("InCallActivity.showRttCallScreenFragment", "dismiss RTT request dialog");
|
|
rttRequestDialogFragment.dismiss();
|
|
rttRequestDialogFragment = null;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
private boolean hideRttCallScreenFragment(FragmentTransaction transaction) {
|
|
if (!didShowRttCallScreen) {
|
|
return false;
|
|
}
|
|
RttCallScreen rttCallScreen = getRttCallScreen();
|
|
if (rttCallScreen != null) {
|
|
transaction.remove(rttCallScreen.getRttCallScreenFragment());
|
|
}
|
|
didShowRttCallScreen = false;
|
|
return true;
|
|
}
|
|
|
|
private boolean showVideoCallScreenFragment(FragmentTransaction transaction, DialerCall call) {
|
|
if (didShowVideoCallScreen) {
|
|
VideoCallScreen videoCallScreen = getVideoCallScreen();
|
|
if (videoCallScreen.getCallId().equals(call.getId())) {
|
|
return false;
|
|
}
|
|
LogUtil.i(
|
|
"InCallActivity.showVideoCallScreenFragment",
|
|
"video call fragment exists but arguments do not match");
|
|
hideVideoCallScreenFragment(transaction);
|
|
}
|
|
|
|
LogUtil.i("InCallActivity.showVideoCallScreenFragment", "call: %s", call);
|
|
|
|
VideoCallScreen videoCallScreen =
|
|
VideoBindings.createVideoCallScreen(
|
|
call.getId(), call.getVideoTech().shouldUseSurfaceView());
|
|
transaction.add(
|
|
R.id.main, videoCallScreen.getVideoCallScreenFragment(), Tags.VIDEO_CALL_SCREEN);
|
|
|
|
Logger.get(this).logScreenView(ScreenEvent.Type.INCALL, this);
|
|
didShowVideoCallScreen = true;
|
|
return true;
|
|
}
|
|
|
|
private boolean hideVideoCallScreenFragment(FragmentTransaction transaction) {
|
|
if (!didShowVideoCallScreen) {
|
|
return false;
|
|
}
|
|
VideoCallScreen videoCallScreen = getVideoCallScreen();
|
|
if (videoCallScreen != null) {
|
|
transaction.remove(videoCallScreen.getVideoCallScreenFragment());
|
|
}
|
|
didShowVideoCallScreen = false;
|
|
return true;
|
|
}
|
|
|
|
private AnswerScreen getAnswerScreen() {
|
|
return (AnswerScreen) getSupportFragmentManager().findFragmentByTag(Tags.ANSWER_SCREEN);
|
|
}
|
|
|
|
private InCallScreen getInCallScreen() {
|
|
return (InCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.IN_CALL_SCREEN);
|
|
}
|
|
|
|
private VideoCallScreen getVideoCallScreen() {
|
|
return (VideoCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.VIDEO_CALL_SCREEN);
|
|
}
|
|
|
|
private RttCallScreen getRttCallScreen() {
|
|
return (RttCallScreen) getSupportFragmentManager().findFragmentByTag(Tags.RTT_CALL_SCREEN);
|
|
}
|
|
|
|
private InCallScreen getInCallOrRttCallScreen() {
|
|
InCallScreen inCallScreen = null;
|
|
if (didShowInCallScreen) {
|
|
inCallScreen = getInCallScreen();
|
|
}
|
|
if (didShowRttCallScreen) {
|
|
inCallScreen = getRttCallScreen();
|
|
}
|
|
return inCallScreen;
|
|
}
|
|
|
|
@Override
|
|
public void onPseudoScreenStateChanged(boolean isOn) {
|
|
LogUtil.i("InCallActivity.onPseudoScreenStateChanged", "isOn: " + isOn);
|
|
pseudoBlackScreenOverlay.setVisibility(isOn ? View.GONE : View.VISIBLE);
|
|
}
|
|
|
|
/**
|
|
* For some touch related issue, turning off the screen can be faked by drawing a black view over
|
|
* the activity. All touch events started when the screen is "off" is rejected.
|
|
*
|
|
* @see PseudoScreenState
|
|
*/
|
|
@Override
|
|
public boolean dispatchTouchEvent(MotionEvent event) {
|
|
// Reject any gesture that started when the screen is in the fake off state.
|
|
if (touchDownWhenPseudoScreenOff) {
|
|
if (event.getAction() == MotionEvent.ACTION_UP) {
|
|
touchDownWhenPseudoScreenOff = false;
|
|
}
|
|
return true;
|
|
}
|
|
// Reject all touch event when the screen is in the fake off state.
|
|
if (!InCallPresenter.getInstance().getPseudoScreenState().isOn()) {
|
|
if (event.getAction() == MotionEvent.ACTION_DOWN) {
|
|
touchDownWhenPseudoScreenOff = true;
|
|
LogUtil.i("InCallActivity.dispatchTouchEvent", "touchDownWhenPseudoScreenOff");
|
|
}
|
|
return true;
|
|
}
|
|
return super.dispatchTouchEvent(event);
|
|
}
|
|
|
|
@Override
|
|
public RttCallScreenDelegate newRttCallScreenDelegate(RttCallScreen videoCallScreen) {
|
|
return new RttCallPresenter();
|
|
}
|
|
|
|
private static class ShouldShowUiResult {
|
|
public final boolean shouldShow;
|
|
public final DialerCall call;
|
|
|
|
ShouldShowUiResult(boolean shouldShow, DialerCall call) {
|
|
this.shouldShow = shouldShow;
|
|
this.call = call;
|
|
}
|
|
}
|
|
|
|
private static final class IntentExtraNames {
|
|
static final String FOR_FULL_SCREEN = "InCallActivity.for_full_screen_intent";
|
|
static final String NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
|
|
static final String SHOW_DIALPAD = "InCallActivity.show_dialpad";
|
|
}
|
|
|
|
private static final class KeysForSavedInstance {
|
|
static final String DIALPAD_TEXT = "InCallActivity.dialpad_text";
|
|
static final String DID_SHOW_ANSWER_SCREEN = "did_show_answer_screen";
|
|
static final String DID_SHOW_IN_CALL_SCREEN = "did_show_in_call_screen";
|
|
static final String DID_SHOW_VIDEO_CALL_SCREEN = "did_show_video_call_screen";
|
|
static final String DID_SHOW_RTT_CALL_SCREEN = "did_show_rtt_call_screen";
|
|
static final String DID_SHOW_SPEAK_EASY_SCREEN = "did_show_speak_easy_screen";
|
|
}
|
|
|
|
/** Request codes for pending intents. */
|
|
public static final class PendingIntentRequestCodes {
|
|
static final int NON_FULL_SCREEN = 0;
|
|
static final int FULL_SCREEN = 1;
|
|
static final int BUBBLE = 2;
|
|
}
|
|
|
|
private static final class Tags {
|
|
static final String ANSWER_SCREEN = "tag_answer_screen";
|
|
static final String DIALPAD_FRAGMENT = "tag_dialpad_fragment";
|
|
static final String IN_CALL_SCREEN = "tag_in_call_screen";
|
|
static final String INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi";
|
|
static final String SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment";
|
|
static final String VIDEO_CALL_SCREEN = "tag_video_call_screen";
|
|
static final String RTT_CALL_SCREEN = "tag_rtt_call_screen";
|
|
static final String POST_CHAR_DIALOG_FRAGMENT = "tag_post_char_dialog_fragment";
|
|
static final String SPEAK_EASY_SCREEN = "tag_speak_easy_screen";
|
|
static final String RTT_REQUEST_DIALOG = "tag_rtt_request_dialog";
|
|
}
|
|
|
|
private static final class ConfigNames {
|
|
static final String ANSWER_AND_RELEASE_ENABLED = "answer_and_release_enabled";
|
|
}
|
|
|
|
private static final class SelectPhoneAccountListener
|
|
extends SelectPhoneAccountDialogFragment.SelectPhoneAccountListener {
|
|
private static final String TAG = SelectPhoneAccountListener.class.getCanonicalName();
|
|
|
|
private final Context appContext;
|
|
|
|
SelectPhoneAccountListener(Context appContext) {
|
|
this.appContext = appContext;
|
|
}
|
|
|
|
@Override
|
|
public void onPhoneAccountSelected(
|
|
PhoneAccountHandle selectedAccountHandle, boolean setDefault, String callId) {
|
|
DialerCall call = CallList.getInstance().getCallById(callId);
|
|
LogUtil.i(TAG, "Phone account select with call:\n%s", call);
|
|
|
|
if (call != null) {
|
|
call.phoneAccountSelected(selectedAccountHandle, false);
|
|
if (call.getPreferredAccountRecorder() != null) {
|
|
call.getPreferredAccountRecorder().record(appContext, selectedAccountHandle, setDefault);
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void onDialogDismissed(String callId) {
|
|
DialerCall call = CallList.getInstance().getCallById(callId);
|
|
LogUtil.i(TAG, "Disconnecting call:\n%s" + call);
|
|
|
|
if (call != null) {
|
|
call.disconnect();
|
|
}
|
|
}
|
|
}
|
|
}
|