/* * 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 pair = EnableWifiCallingPrompt.createDialog(inCallActivity, cause); showErrorDialog(pair.first, pair.second); } else if (shouldShowDisconnectErrorDialog(cause)) { Pair 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 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 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 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; } }