summaryrefslogtreecommitdiff
path: root/java/com/android/incallui/InCallActivityCommon.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/incallui/InCallActivityCommon.java')
-rw-r--r--java/com/android/incallui/InCallActivityCommon.java890
1 files changed, 890 insertions, 0 deletions
diff --git a/java/com/android/incallui/InCallActivityCommon.java b/java/com/android/incallui/InCallActivityCommon.java
new file mode 100644
index 000000000..4b0a3cd05
--- /dev/null
+++ b/java/com/android/incallui/InCallActivityCommon.java
@@ -0,0 +1,890 @@
+/*
+ * 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.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnCancelListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.content.res.Configuration;
+import android.content.res.Resources;
+import android.os.Bundle;
+import android.support.annotation.IntDef;
+import android.support.annotation.NonNull;
+import android.support.annotation.Nullable;
+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.telecom.DisconnectCause;
+import android.telecom.PhoneAccountHandle;
+import android.text.TextUtils;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.Window;
+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.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener;
+import com.android.dialer.animation.AnimUtils;
+import com.android.dialer.animation.AnimationListenerAdapter;
+import com.android.dialer.common.LogUtil;
+import com.android.dialer.compat.CompatUtils;
+import com.android.dialer.logging.Logger;
+import com.android.dialer.logging.ScreenEvent;
+import com.android.dialer.util.ViewUtil;
+import com.android.incallui.audiomode.AudioModeProvider;
+import com.android.incallui.call.CallList;
+import com.android.incallui.call.DialerCall;
+import com.android.incallui.call.DialerCall.State;
+import com.android.incallui.call.TelecomAdapter;
+import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment;
+import com.android.incallui.telecomeventui.InternationalCallOnWifiDialogFragment.Callback;
+import com.android.incallui.wifi.EnableWifiCallingPrompt;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.util.ArrayList;
+import java.util.List;
+
+/** Shared functionality between the new and old in call activity. */
+public class InCallActivityCommon {
+
+ private static final String INTENT_EXTRA_SHOW_DIALPAD = "InCallActivity.show_dialpad";
+ private static final String INTENT_EXTRA_NEW_OUTGOING_CALL = "InCallActivity.new_outgoing_call";
+ private static final String INTENT_EXTRA_FOR_FULL_SCREEN =
+ "InCallActivity.for_full_screen_intent";
+
+ private static final String DIALPAD_TEXT_KEY = "InCallActivity.dialpad_text";
+
+ private static final String TAG_SELECT_ACCOUNT_FRAGMENT = "tag_select_account_fragment";
+ private static final String TAG_DIALPAD_FRAGMENT = "tag_dialpad_fragment";
+ private static final String TAG_INTERNATIONAL_CALL_ON_WIFI = "tag_international_call_on_wifi";
+
+ @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 final InCallActivity inCallActivity;
+ private boolean dismissKeyguard;
+ private boolean showPostCharWaitDialogOnResume;
+ private String showPostCharWaitDialogCallId;
+ private String showPostCharWaitDialogChars;
+ private Dialog dialog;
+ private SelectPhoneAccountDialogFragment selectPhoneAccountDialogFragment;
+ private InCallOrientationEventListener inCallOrientationEventListener;
+ private Animation dialpadSlideInAnimation;
+ private Animation dialpadSlideOutAnimation;
+ private boolean animateDialpadOnShow;
+ private String dtmfTextToPreopulate;
+ @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE;
+
+ private final SelectPhoneAccountListener selectAccountListener =
+ new SelectPhoneAccountListener() {
+ @Override
+ public void onPhoneAccountSelected(
+ PhoneAccountHandle selectedAccountHandle, boolean setDefault, String callId) {
+ DialerCall call = CallList.getInstance().getCallById(callId);
+ LogUtil.i(
+ "InCallActivityCommon.SelectPhoneAccountListener.onPhoneAccountSelected",
+ "call: " + call);
+ if (call != null) {
+ call.phoneAccountSelected(selectedAccountHandle, setDefault);
+ }
+ }
+
+ @Override
+ public void onDialogDismissed(String callId) {
+ DialerCall call = CallList.getInstance().getCallById(callId);
+ LogUtil.i(
+ "InCallActivityCommon.SelectPhoneAccountListener.onDialogDismissed",
+ "disconnecting call: " + call);
+ if (call != null) {
+ call.disconnect();
+ }
+ }
+ };
+
+ private InternationalCallOnWifiDialogFragment.Callback internationalCallOnWifiCallback =
+ new Callback() {
+ @Override
+ public void continueCall(@NonNull String callId) {
+ LogUtil.i("InCallActivityCommon.continueCall", "continuing call with id: %s", callId);
+ }
+
+ @Override
+ public void cancelCall(@NonNull String callId) {
+ DialerCall call = CallList.getInstance().getCallById(callId);
+ if (call == null) {
+ LogUtil.i("InCallActivityCommon.cancelCall", "call destroyed before dialog closed");
+ return;
+ }
+ LogUtil.i("InCallActivityCommon.cancelCall", "disconnecting international call on wifi");
+ call.disconnect();
+ }
+ };
+
+ public static void setIntentExtras(
+ Intent intent, boolean showDialpad, boolean newOutgoingCall, boolean isForFullScreen) {
+ if (showDialpad) {
+ intent.putExtra(INTENT_EXTRA_SHOW_DIALPAD, true);
+ }
+ intent.putExtra(INTENT_EXTRA_NEW_OUTGOING_CALL, newOutgoingCall);
+ intent.putExtra(INTENT_EXTRA_FOR_FULL_SCREEN, isForFullScreen);
+ }
+
+ public InCallActivityCommon(InCallActivity inCallActivity) {
+ this.inCallActivity = inCallActivity;
+ }
+
+ public void onCreate(Bundle icicle) {
+ // set this flag so this activity will stay in front of the keyguard
+ // Have the WindowManager filter out touch events that are "too fat".
+ int flags =
+ WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED
+ | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+ | WindowManager.LayoutParams.FLAG_IGNORE_CHEEK_PRESSES;
+
+ inCallActivity.getWindow().addFlags(flags);
+
+ inCallActivity.setContentView(R.layout.incall_screen);
+
+ internalResolveIntent(inCallActivity.getIntent());
+
+ boolean isLandscape =
+ inCallActivity.getResources().getConfiguration().orientation
+ == Configuration.ORIENTATION_LANDSCAPE;
+ boolean isRtl = ViewUtil.isRtl();
+
+ if (isLandscape) {
+ dialpadSlideInAnimation =
+ AnimationUtils.loadAnimation(
+ inCallActivity, isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right);
+ dialpadSlideOutAnimation =
+ AnimationUtils.loadAnimation(
+ inCallActivity,
+ isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right);
+ } else {
+ dialpadSlideInAnimation =
+ AnimationUtils.loadAnimation(inCallActivity, R.anim.dialpad_slide_in_bottom);
+ dialpadSlideOutAnimation =
+ AnimationUtils.loadAnimation(inCallActivity, 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) {
+ performHideDialpadFragment();
+ }
+ });
+
+ if (icicle != null) {
+ // If the dialpad was shown before, set variables indicating it should be shown and
+ // populated with the previous DTMF text. The dialpad is actually shown and populated
+ // in onResume() to ensure the hosting fragment has been inflated and is ready to receive it.
+ if (icicle.containsKey(INTENT_EXTRA_SHOW_DIALPAD)) {
+ boolean showDialpad = icicle.getBoolean(INTENT_EXTRA_SHOW_DIALPAD);
+ showDialpadRequest = showDialpad ? DIALPAD_REQUEST_SHOW : DIALPAD_REQUEST_HIDE;
+ animateDialpadOnShow = false;
+ }
+ dtmfTextToPreopulate = icicle.getString(DIALPAD_TEXT_KEY);
+
+ SelectPhoneAccountDialogFragment dialogFragment =
+ (SelectPhoneAccountDialogFragment)
+ inCallActivity.getFragmentManager().findFragmentByTag(TAG_SELECT_ACCOUNT_FRAGMENT);
+ if (dialogFragment != null) {
+ dialogFragment.setListener(selectAccountListener);
+ }
+ }
+
+ InternationalCallOnWifiDialogFragment existingInternationalFragment =
+ (InternationalCallOnWifiDialogFragment)
+ inCallActivity
+ .getSupportFragmentManager()
+ .findFragmentByTag(TAG_INTERNATIONAL_CALL_ON_WIFI);
+ if (existingInternationalFragment != null) {
+ LogUtil.i(
+ "InCallActivityCommon.onCreate", "international fragment exists attaching callback");
+ existingInternationalFragment.setCallback(internationalCallOnWifiCallback);
+ }
+
+ inCallOrientationEventListener = new InCallOrientationEventListener(inCallActivity);
+ }
+
+ public void onSaveInstanceState(Bundle out) {
+ // TODO: The dialpad fragment should handle this as part of its own state
+ out.putBoolean(INTENT_EXTRA_SHOW_DIALPAD, isDialpadVisible());
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null) {
+ out.putString(DIALPAD_TEXT_KEY, dialpadFragment.getDtmfText());
+ }
+ }
+
+ public void onStart() {
+ // setting activity should be last thing in setup process
+ InCallPresenter.getInstance().setActivity(inCallActivity);
+ enableInCallOrientationEventListener(
+ inCallActivity.getRequestedOrientation()
+ == InCallOrientationEventListener.ACTIVITY_PREFERENCE_ALLOW_ROTATION);
+
+ InCallPresenter.getInstance().onActivityStarted();
+ }
+
+ public void onResume() {
+ if (InCallPresenter.getInstance().isReadyForTearDown()) {
+ LogUtil.i(
+ "InCallActivityCommon.onResume",
+ "InCallPresenter is ready for tear down, not sending updates");
+ } else {
+ updateTaskDescription();
+ InCallPresenter.getInstance().onUiShowing(true);
+ }
+
+ // 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 and
+ // can hide the dialpad. Important when showing the dialpad from within dialer.
+ InCallPresenter.getInstance().setFullScreen(false, true /* force */);
+
+ inCallActivity.showDialpadFragment(true /* show */, animateDialpadOnShow /* animate */);
+ animateDialpadOnShow = false;
+
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null) {
+ dialpadFragment.setDtmfText(dtmfTextToPreopulate);
+ dtmfTextToPreopulate = null;
+ }
+ } else {
+ LogUtil.i("InCallActivityCommon.onResume", "force hide dialpad");
+ if (getDialpadFragment() != null) {
+ inCallActivity.showDialpadFragment(false /* show */, false /* animate */);
+ }
+ }
+ showDialpadRequest = DIALPAD_REQUEST_NONE;
+ }
+
+ if (showPostCharWaitDialogOnResume) {
+ showPostCharWaitDialog(showPostCharWaitDialogCallId, showPostCharWaitDialogChars);
+ }
+
+ CallList.getInstance()
+ .onInCallUiShown(
+ inCallActivity.getIntent().getBooleanExtra(INTENT_EXTRA_FOR_FULL_SCREEN, false));
+ }
+
+ // onPause is guaranteed to be called when the InCallActivity goes
+ // in the background.
+ public void onPause() {
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null) {
+ dialpadFragment.onDialerKeyUp(null);
+ }
+
+ InCallPresenter.getInstance().onUiShowing(false);
+ if (inCallActivity.isFinishing()) {
+ InCallPresenter.getInstance().unsetActivity(inCallActivity);
+ }
+ }
+
+ public void onStop() {
+ enableInCallOrientationEventListener(false);
+ InCallPresenter.getInstance().updateIsChangingConfigurations();
+ InCallPresenter.getInstance().onActivityStopped();
+ }
+
+ public void onDestroy() {
+ InCallPresenter.getInstance().unsetActivity(inCallActivity);
+ InCallPresenter.getInstance().updateIsChangingConfigurations();
+ }
+
+ void onNewIntent(Intent intent, boolean isRecreating) {
+ LogUtil.i("InCallActivityCommon.onNewIntent", "");
+
+ // 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 potentially 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!)
+ inCallActivity.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 recreate since this will be called in onCreate().
+ if (!isRecreating) {
+ internalResolveIntent(intent);
+ }
+ }
+
+ public boolean onBackPressed(boolean isInCallScreenVisible) {
+ LogUtil.i("InCallActivityCommon.onBackPressed", "");
+
+ // BACK is also used to exit out of any "special modes" of the
+ // in-call UI:
+ if (!inCallActivity.isVisible()) {
+ return true;
+ }
+
+ if (!isInCallScreenVisible) {
+ return true;
+ }
+
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null && dialpadFragment.isVisible()) {
+ inCallActivity.showDialpadFragment(false /* show */, true /* animate */);
+ return true;
+ }
+
+ // Always disable the Back key while an incoming call is ringing
+ DialerCall call = CallList.getInstance().getIncomingCall();
+ if (call != null) {
+ LogUtil.i("InCallActivityCommon.onBackPressed", "consume Back press for an incoming call");
+ return true;
+ }
+
+ // Nothing special to do. Fall back to the default behavior.
+ return false;
+ }
+
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ // push input to the dialer.
+ if (dialpadFragment != null
+ && (dialpadFragment.isVisible())
+ && (dialpadFragment.onDialerKeyUp(event))) {
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_CALL) {
+ // Always consume CALL to be sure the PhoneWindow won't do anything with it
+ return true;
+ }
+ return false;
+ }
+
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_CALL:
+ boolean handled = InCallPresenter.getInstance().handleCallKey();
+ if (!handled) {
+ LogUtil.e(
+ "InCallActivityCommon.onKeyDown",
+ "InCallPresenter should always handle KEYCODE_CALL in onKeyDown");
+ }
+ // Always consume CALL to be sure the PhoneWindow won't do anything with it
+ return true;
+
+ // Note there's no KeyEvent.KEYCODE_ENDCALL case here.
+ // The standard system-wide handling of the ENDCALL key
+ // (see PhoneWindowManager's handling of KEYCODE_ENDCALL)
+ // already implements exactly what the UI spec wants,
+ // namely (1) "hang up" if there's a current active call,
+ // or (2) "don't answer" if there's a current ringing call.
+
+ case KeyEvent.KEYCODE_CAMERA:
+ // Disable the CAMERA button while in-call since it's too
+ // easy to press accidentally.
+ 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;
+
+ // Various testing/debugging features, enabled ONLY when VERBOSE == true.
+ case KeyEvent.KEYCODE_SLASH:
+ if (LogUtil.isVerboseEnabled()) {
+ LogUtil.v(
+ "InCallActivityCommon.onKeyDown",
+ "----------- InCallActivity View dump --------------");
+ // Dump starting from the top-level view of the entire activity:
+ Window w = inCallActivity.getWindow();
+ View decorView = w.getDecorView();
+ LogUtil.v("InCallActivityCommon.onKeyDown", "View dump:" + decorView);
+ return true;
+ }
+ break;
+ case KeyEvent.KEYCODE_EQUALS:
+ break;
+ default: // fall out
+ }
+
+ return event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event);
+ }
+
+ private boolean handleDialerKeyDown(int keyCode, KeyEvent event) {
+ LogUtil.v("InCallActivityCommon.handleDialerKeyDown", "keyCode %d, event: %s", keyCode, event);
+
+ // As soon as the user starts typing valid dialable keys on the
+ // keyboard (presumably to type DTMF tones) we start passing the
+ // key events to the DTMFDialer's onDialerKeyDown.
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment != null && dialpadFragment.isVisible()) {
+ return dialpadFragment.onDialerKeyDown(event);
+ }
+
+ return false;
+ }
+
+ public void dismissKeyguard(boolean dismiss) {
+ if (dismissKeyguard == dismiss) {
+ return;
+ }
+ dismissKeyguard = dismiss;
+ if (dismiss) {
+ inCallActivity.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ } else {
+ inCallActivity.getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+ }
+ }
+
+ public void showPostCharWaitDialog(String callId, String chars) {
+ if (inCallActivity.isVisible()) {
+ PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars);
+ fragment.show(inCallActivity.getSupportFragmentManager(), "postCharWait");
+
+ showPostCharWaitDialogOnResume = false;
+ showPostCharWaitDialogCallId = null;
+ showPostCharWaitDialogChars = null;
+ } else {
+ showPostCharWaitDialogOnResume = true;
+ showPostCharWaitDialogCallId = callId;
+ showPostCharWaitDialogChars = chars;
+ }
+ }
+
+ public void maybeShowErrorDialogOnDisconnect(DisconnectCause cause) {
+ LogUtil.i(
+ "InCallActivityCommon.maybeShowErrorDialogOnDisconnect", "disconnect cause: %s", cause);
+
+ if (!inCallActivity.isFinishing()) {
+ if (EnableWifiCallingPrompt.shouldShowPrompt(cause)) {
+ Pair<Dialog, CharSequence> pair =
+ EnableWifiCallingPrompt.createDialog(inCallActivity, cause);
+ showErrorDialog(pair.first, pair.second);
+ } else if (shouldShowDisconnectErrorDialog(cause)) {
+ Pair<Dialog, CharSequence> pair = getDisconnectErrorDialog(inCallActivity, cause);
+ showErrorDialog(pair.first, pair.second);
+ }
+ }
+ }
+
+ /**
+ * 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() == State.ONHOLD) {
+ call.unhold();
+ }
+ }
+ }
+
+ void dismissPendingDialogs() {
+ if (dialog != null) {
+ dialog.dismiss();
+ dialog = null;
+ }
+ if (selectPhoneAccountDialogFragment != null) {
+ selectPhoneAccountDialogFragment.dismiss();
+ selectPhoneAccountDialogFragment = null;
+ }
+
+ InternationalCallOnWifiDialogFragment internationalCallOnWifiFragment =
+ (InternationalCallOnWifiDialogFragment)
+ inCallActivity
+ .getSupportFragmentManager()
+ .findFragmentByTag(TAG_INTERNATIONAL_CALL_ON_WIFI);
+ if (internationalCallOnWifiFragment != null) {
+ LogUtil.i(
+ "InCallActivityCommon.dismissPendingDialogs",
+ "dismissing InternationalCallOnWifiDialogFragment");
+ internationalCallOnWifiFragment.dismiss();
+ }
+ }
+
+ private static boolean shouldShowDisconnectErrorDialog(@NonNull DisconnectCause cause) {
+ return !TextUtils.isEmpty(cause.getDescription())
+ && (cause.getCode() == DisconnectCause.ERROR
+ || cause.getCode() == DisconnectCause.RESTRICTED);
+ }
+
+ private static Pair<Dialog, CharSequence> getDisconnectErrorDialog(
+ @NonNull Context context, @NonNull DisconnectCause cause) {
+ CharSequence message = cause.getDescription();
+ Dialog dialog =
+ new AlertDialog.Builder(context)
+ .setMessage(message)
+ .setPositiveButton(android.R.string.ok, null)
+ .create();
+ return new Pair<>(dialog, message);
+ }
+
+ private void showErrorDialog(Dialog dialog, CharSequence message) {
+ LogUtil.i("InCallActivityCommon.showErrorDialog", "message: %s", message);
+ inCallActivity.dismissPendingDialogs();
+
+ // Show toast if apps is in background when dialog won't be visible.
+ if (!inCallActivity.isVisible()) {
+ Toast.makeText(inCallActivity.getApplicationContext(), message, Toast.LENGTH_LONG).show();
+ return;
+ }
+
+ this.dialog = dialog;
+ dialog.setOnDismissListener(
+ new OnDismissListener() {
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ LogUtil.i("InCallActivityCommon.showErrorDialog", "dialog dismissed");
+ onDialogDismissed();
+ }
+ });
+ dialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
+ dialog.show();
+ }
+
+ private void onDialogDismissed() {
+ dialog = null;
+ CallList.getInstance().onErrorDialogDismissed();
+ InCallPresenter.getInstance().onDismissDialog();
+ }
+
+ public void enableInCallOrientationEventListener(boolean enable) {
+ if (enable) {
+ inCallOrientationEventListener.enable(true);
+ } else {
+ inCallOrientationEventListener.disable();
+ }
+ }
+
+ public void setExcludeFromRecents(boolean exclude) {
+ List<AppTask> tasks = inCallActivity.getSystemService(ActivityManager.class).getAppTasks();
+ int taskId = inCallActivity.getTaskId();
+ for (int i = 0; i < tasks.size(); i++) {
+ ActivityManager.AppTask task = tasks.get(i);
+ try {
+ if (task.getTaskInfo().id == taskId) {
+ task.setExcludeFromRecents(exclude);
+ }
+ } catch (RuntimeException e) {
+ LogUtil.e(
+ "InCallActivityCommon.setExcludeFromRecents",
+ "RuntimeException when excluding task from recents.",
+ e);
+ }
+ }
+ }
+
+ void showInternationalCallOnWifiDialog(@NonNull DialerCall call) {
+ LogUtil.enterBlock("InCallActivityCommon.showInternationalCallOnWifiDialog");
+ if (!InternationalCallOnWifiDialogFragment.shouldShow(inCallActivity)) {
+ LogUtil.i(
+ "InCallActivityCommon.showInternationalCallOnWifiDialog",
+ "InternationalCallOnWifiDialogFragment.shouldShow returned false");
+ return;
+ }
+
+ InternationalCallOnWifiDialogFragment fragment =
+ InternationalCallOnWifiDialogFragment.newInstance(
+ call.getId(), internationalCallOnWifiCallback);
+ fragment.show(inCallActivity.getSupportFragmentManager(), TAG_INTERNATIONAL_CALL_ON_WIFI);
+ }
+
+ public void showWifiToLteHandoverToast(DialerCall call) {
+ if (call.hasShownWiFiToLteHandoverToast()) {
+ return;
+ }
+ Toast.makeText(
+ inCallActivity, R.string.video_call_wifi_to_lte_handover_toast, Toast.LENGTH_LONG)
+ .show();
+ call.setHasShownWiFiToLteHandoverToast();
+ }
+
+ public void showWifiFailedDialog(final DialerCall call) {
+ if (call.showWifiHandoverAlertAsToast()) {
+ LogUtil.i("InCallActivityCommon.showWifiFailedDialog", "as toast");
+ Toast.makeText(
+ inCallActivity, R.string.video_call_lte_to_wifi_failed_message, Toast.LENGTH_SHORT)
+ .show();
+ return;
+ }
+
+ dismissPendingDialogs();
+
+ AlertDialog.Builder builder =
+ new AlertDialog.Builder(inCallActivity)
+ .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);
+ final CheckBox wifiHandoverFailureCheckbox =
+ (CheckBox) dialogCheckBoxView.findViewById(R.id.video_call_lte_to_wifi_failed_checkbox);
+ wifiHandoverFailureCheckbox.setChecked(false);
+
+ dialog =
+ builder
+ .setView(dialogCheckBoxView)
+ .setMessage(R.string.video_call_lte_to_wifi_failed_message)
+ .setOnCancelListener(
+ new OnCancelListener() {
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ onDialogDismissed();
+ }
+ })
+ .setPositiveButton(
+ android.R.string.ok,
+ new DialogInterface.OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int id) {
+ call.setDoNotShowDialogForHandoffToWifiFailure(
+ wifiHandoverFailureCheckbox.isChecked());
+ dialog.cancel();
+ onDialogDismissed();
+ }
+ })
+ .create();
+
+ LogUtil.i("InCallActivityCommon.showWifiFailedDialog", "as dialog");
+ dialog.show();
+ }
+
+ public boolean showDialpadFragment(boolean show, boolean animate) {
+ // If the dialpad is already visible, don't animate in. If it's gone, don't animate out.
+ boolean isDialpadVisible = isDialpadVisible();
+ LogUtil.i(
+ "InCallActivityCommon.showDialpadFragment",
+ "show: %b, animate: %b, " + "isDialpadVisible: %b",
+ show,
+ animate,
+ isDialpadVisible);
+ if (show == isDialpadVisible) {
+ return false;
+ }
+
+ FragmentManager dialpadFragmentManager = inCallActivity.getDialpadFragmentManager();
+ if (dialpadFragmentManager == null) {
+ LogUtil.i(
+ "InCallActivityCommon.showDialpadFragment", "unable to show or hide dialpad fragment");
+ return false;
+ }
+
+ // We don't do a FragmentTransaction on the hide case because it will be dealt with when
+ // the listener is fired after an animation finishes.
+ if (!animate) {
+ if (show) {
+ performShowDialpadFragment(dialpadFragmentManager);
+ } else {
+ performHideDialpadFragment();
+ }
+ } else {
+ if (show) {
+ performShowDialpadFragment(dialpadFragmentManager);
+ getDialpadFragment().animateShowDialpad();
+ }
+ getDialpadFragment()
+ .getView()
+ .startAnimation(show ? dialpadSlideInAnimation : dialpadSlideOutAnimation);
+ }
+
+ ProximitySensor sensor = InCallPresenter.getInstance().getProximitySensor();
+ if (sensor != null) {
+ sensor.onDialpadVisible(show);
+ }
+ showDialpadRequest = DIALPAD_REQUEST_NONE;
+ return true;
+ }
+
+ private void performShowDialpadFragment(@NonNull FragmentManager dialpadFragmentManager) {
+ FragmentTransaction transaction = dialpadFragmentManager.beginTransaction();
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ if (dialpadFragment == null) {
+ transaction.add(
+ inCallActivity.getDialpadContainerId(), new DialpadFragment(), TAG_DIALPAD_FRAGMENT);
+ } else {
+ transaction.show(dialpadFragment);
+ }
+
+ transaction.commitAllowingStateLoss();
+ dialpadFragmentManager.executePendingTransactions();
+
+ Logger.get(inCallActivity).logScreenView(ScreenEvent.Type.INCALL_DIALPAD, inCallActivity);
+ }
+
+ private void performHideDialpadFragment() {
+ FragmentManager fragmentManager = inCallActivity.getDialpadFragmentManager();
+ if (fragmentManager == null) {
+ LogUtil.e(
+ "InCallActivityCommon.performHideDialpadFragment", "child fragment manager is null");
+ return;
+ }
+
+ Fragment fragment = fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
+ if (fragment != null) {
+ FragmentTransaction transaction = fragmentManager.beginTransaction();
+ transaction.hide(fragment);
+ transaction.commitAllowingStateLoss();
+ fragmentManager.executePendingTransactions();
+ }
+ }
+
+ public boolean isDialpadVisible() {
+ DialpadFragment dialpadFragment = getDialpadFragment();
+ return dialpadFragment != null && dialpadFragment.isVisible();
+ }
+
+ /** Returns the {@link DialpadFragment} that's shown by this activity, or {@code null} */
+ @Nullable
+ private DialpadFragment getDialpadFragment() {
+ FragmentManager fragmentManager = inCallActivity.getDialpadFragmentManager();
+ if (fragmentManager == null) {
+ return null;
+ }
+ return (DialpadFragment) fragmentManager.findFragmentByTag(TAG_DIALPAD_FRAGMENT);
+ }
+
+ public void updateTaskDescription() {
+ Resources resources = inCallActivity.getResources();
+ int color;
+ if (resources.getBoolean(R.bool.is_layout_landscape)) {
+ color =
+ ResourcesCompat.getColor(
+ resources, R.color.statusbar_background_color, inCallActivity.getTheme());
+ } else {
+ color = InCallPresenter.getInstance().getThemeColorManager().getSecondaryColor();
+ }
+
+ TaskDescription td =
+ new TaskDescription(resources.getString(R.string.notification_ongoing_call), null, color);
+ inCallActivity.setTaskDescription(td);
+ }
+
+ public boolean hasPendingDialogs() {
+ return dialog != null;
+ }
+
+ private void internalResolveIntent(Intent intent) {
+ if (!intent.getAction().equals(Intent.ACTION_MAIN)) {
+ return;
+ }
+
+ if (intent.hasExtra(INTENT_EXTRA_SHOW_DIALPAD)) {
+ // SHOW_DIALPAD_EXTRA can be used here to specify whether the DTMF
+ // dialpad should be initially visible. If the extra isn't
+ // present at all, we just leave the dialpad in its previous state.
+ boolean showDialpad = intent.getBooleanExtra(INTENT_EXTRA_SHOW_DIALPAD, false);
+ LogUtil.i("InCallActivityCommon.internalResolveIntent", "SHOW_DIALPAD_EXTRA: " + showDialpad);
+
+ relaunchedFromDialer(showDialpad);
+ }
+
+ DialerCall outgoingCall = CallList.getInstance().getOutgoingCall();
+ if (outgoingCall == null) {
+ outgoingCall = CallList.getInstance().getPendingOutgoingCall();
+ }
+
+ if (intent.getBooleanExtra(INTENT_EXTRA_NEW_OUTGOING_CALL, false)) {
+ intent.removeExtra(INTENT_EXTRA_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 the version is not MSIM compatible, then ignore this code.
+ if (CompatUtils.isMSIMCompatible()
+ && InCallPresenter.isCallWithNoValidAccounts(outgoingCall)) {
+ LogUtil.i(
+ "InCallActivityCommon.internalResolveIntent",
+ "call with no valid accounts, disconnecting");
+ outgoingCall.disconnect();
+ }
+
+ dismissKeyguard(true);
+ }
+
+ boolean didShowAccountSelectionDialog = maybeShowAccountSelectionDialog();
+ if (didShowAccountSelectionDialog) {
+ inCallActivity.hideMainInCallFragment();
+ }
+ }
+
+ private boolean maybeShowAccountSelectionDialog() {
+ DialerCall waitingForAccountCall = CallList.getInstance().getWaitingForAccountCall();
+ if (waitingForAccountCall == null) {
+ return false;
+ }
+
+ Bundle extras = waitingForAccountCall.getIntentExtras();
+ List<PhoneAccountHandle> phoneAccountHandles;
+ if (extras != null) {
+ phoneAccountHandles =
+ extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS);
+ } else {
+ phoneAccountHandles = new ArrayList<>();
+ }
+
+ selectPhoneAccountDialogFragment =
+ SelectPhoneAccountDialogFragment.newInstance(
+ R.string.select_phone_account_for_calls,
+ true,
+ phoneAccountHandles,
+ selectAccountListener,
+ waitingForAccountCall.getId());
+ selectPhoneAccountDialogFragment.show(
+ inCallActivity.getFragmentManager(), TAG_SELECT_ACCOUNT_FRAGMENT);
+ return true;
+ }
+}