diff options
Diffstat (limited to 'java/com/android/incallui/InCallActivityCommon.java')
-rw-r--r-- | java/com/android/incallui/InCallActivityCommon.java | 820 |
1 files changed, 820 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..a2467dd72 --- /dev/null +++ b/java/com/android/incallui/InCallActivityCommon.java @@ -0,0 +1,820 @@ +/* + * 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.DialogFragment; +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.nano.ScreenEvent; +import com.android.dialer.util.ViewUtil; +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.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"; + + @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 InCallOrientationEventListener inCallOrientationEventListener; + private Animation dialpadSlideInAnimation; + private Animation dialpadSlideOutAnimation; + private boolean animateDialpadOnShow; + private String dtmfTextToPreopulate; + @DialpadRequestType private int showDialpadRequest = DIALPAD_REQUEST_NONE; + + private 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(); + } + } + }; + + 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); + } + } + + 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(); + } + + public void onNewIntent(Intent intent) { + 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. + 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; + } + + 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(); + } + } + } + + public void dismissPendingDialogs() { + if (dialog != null) { + dialog.dismiss(); + dialog = null; + } + } + + 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); + } + } + } + + 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(); + } + + boolean isNewOutgoingCall = false; + if (intent.getBooleanExtra(INTENT_EXTRA_NEW_OUTGOING_CALL, false)) { + isNewOutgoingCall = true; + 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(); + inCallActivity.onResolveIntent(outgoingCall, isNewOutgoingCall, didShowAccountSelectionDialog); + } + + private boolean maybeShowAccountSelectionDialog() { + DialerCall call = CallList.getInstance().getWaitingForAccountCall(); + if (call == null) { + return false; + } + + Bundle extras = call.getIntentExtras(); + List<PhoneAccountHandle> phoneAccountHandles; + if (extras != null) { + phoneAccountHandles = + extras.getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS); + } else { + phoneAccountHandles = new ArrayList<>(); + } + + DialogFragment dialogFragment = + SelectPhoneAccountDialogFragment.newInstance( + R.string.select_phone_account_for_calls, + true, + phoneAccountHandles, + selectAccountListener, + call.getId()); + dialogFragment.show(inCallActivity.getFragmentManager(), TAG_SELECT_ACCOUNT_FRAGMENT); + return true; + } +} |