| /* |
| * Copyright (C) 2006 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.ActionBar; |
| import android.app.Activity; |
| import android.app.AlertDialog; |
| import android.app.FragmentManager; |
| import android.app.FragmentTransaction; |
| import android.content.Context; |
| import android.content.DialogInterface; |
| import android.content.DialogInterface.OnClickListener; |
| import android.content.DialogInterface.OnCancelListener; |
| import android.content.Intent; |
| import android.content.res.Configuration; |
| import android.content.res.Resources; |
| import android.graphics.Point; |
| import android.net.Uri; |
| import android.os.Bundle; |
| import android.telecom.DisconnectCause; |
| import android.telecom.PhoneAccountHandle; |
| import android.telecom.TelecomManager; |
| import android.telephony.PhoneNumberUtils; |
| import android.text.TextUtils; |
| import android.view.MenuItem; |
| import android.view.animation.Animation; |
| import android.view.animation.AnimationUtils; |
| import android.view.KeyEvent; |
| import android.view.View; |
| import android.view.Window; |
| import android.view.WindowManager; |
| import android.view.accessibility.AccessibilityEvent; |
| |
| import com.android.phone.common.animation.AnimUtils; |
| import com.android.phone.common.animation.AnimationListenerAdapter; |
| import com.android.contacts.common.interactions.TouchPointManager; |
| import com.android.contacts.common.util.MaterialColorMapUtils; |
| import com.android.contacts.common.util.MaterialColorMapUtils.MaterialPalette; |
| import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment; |
| import com.android.contacts.common.widget.SelectPhoneAccountDialogFragment.SelectPhoneAccountListener; |
| import com.android.incallui.Call.State; |
| |
| import java.util.ArrayList; |
| import java.util.List; |
| import java.util.Locale; |
| |
| /** |
| * Phone app "in call" screen. |
| */ |
| public class InCallActivity extends Activity { |
| |
| public static final String SHOW_DIALPAD_EXTRA = "InCallActivity.show_dialpad"; |
| public static final String DIALPAD_TEXT_EXTRA = "InCallActivity.dialpad_text"; |
| public static final String NEW_OUTGOING_CALL_EXTRA = "InCallActivity.new_outgoing_call"; |
| public static final String SHOW_CIRCULAR_REVEAL_EXTRA = "InCallActivity.show_circular_reveal"; |
| |
| private CallButtonFragment mCallButtonFragment; |
| private CallCardFragment mCallCardFragment; |
| private AnswerFragment mAnswerFragment; |
| private DialpadFragment mDialpadFragment; |
| private ConferenceManagerFragment mConferenceManagerFragment; |
| private FragmentManager mChildFragmentManager; |
| |
| private boolean mIsForegroundActivity; |
| private AlertDialog mDialog; |
| |
| /** Use to pass 'showDialpad' from {@link #onNewIntent} to {@link #onResume} */ |
| private boolean mShowDialpadRequested; |
| |
| /** Use to determine if the dialpad should be animated on show. */ |
| private boolean mAnimateDialpadOnShow; |
| |
| /** Use to determine the DTMF Text which should be pre-populated in the dialpad. */ |
| private String mDtmfText; |
| |
| /** Use to pass parameters for showing the PostCharDialog to {@link #onResume} */ |
| private boolean mShowPostCharWaitDialogOnResume; |
| private String mShowPostCharWaitDialogCallId; |
| private String mShowPostCharWaitDialogChars; |
| |
| private boolean mIsLandscape; |
| private Animation mSlideIn; |
| private Animation mSlideOut; |
| private boolean mDismissKeyguard = false; |
| |
| AnimationListenerAdapter mSlideOutListener = new AnimationListenerAdapter() { |
| @Override |
| public void onAnimationEnd(Animation animation) { |
| showDialpad(false); |
| } |
| }; |
| |
| /** |
| * Stores the current orientation of the activity. Used to determine if a change in orientation |
| * has occurred. |
| */ |
| private int mCurrentOrientation; |
| |
| @Override |
| protected void onCreate(Bundle icicle) { |
| Log.d(this, "onCreate()... this = " + this); |
| |
| super.onCreate(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; |
| |
| getWindow().addFlags(flags); |
| |
| // Setup action bar for the conference call manager. |
| requestWindowFeature(Window.FEATURE_ACTION_BAR_OVERLAY); |
| ActionBar actionBar = getActionBar(); |
| if (actionBar != null) { |
| actionBar.setDisplayHomeAsUpEnabled(true); |
| actionBar.setDisplayShowTitleEnabled(true); |
| actionBar.hide(); |
| } |
| |
| // TODO(klp): Do we need to add this back when prox sensor is not available? |
| // lp.inputFeatures |= WindowManager.LayoutParams.INPUT_FEATURE_DISABLE_USER_ACTIVITY; |
| |
| // Inflate everything in incall_screen.xml and add it to the screen. |
| setContentView(R.layout.incall_screen); |
| |
| initializeInCall(); |
| |
| internalResolveIntent(getIntent()); |
| |
| mCurrentOrientation = getResources().getConfiguration().orientation; |
| mIsLandscape = getResources().getConfiguration().orientation |
| == Configuration.ORIENTATION_LANDSCAPE; |
| |
| final boolean isRtl = TextUtils.getLayoutDirectionFromLocale(Locale.getDefault()) == |
| View.LAYOUT_DIRECTION_RTL; |
| |
| if (mIsLandscape) { |
| mSlideIn = AnimationUtils.loadAnimation(this, |
| isRtl ? R.anim.dialpad_slide_in_left : R.anim.dialpad_slide_in_right); |
| mSlideOut = AnimationUtils.loadAnimation(this, |
| isRtl ? R.anim.dialpad_slide_out_left : R.anim.dialpad_slide_out_right); |
| } else { |
| mSlideIn = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_in_bottom); |
| mSlideOut = AnimationUtils.loadAnimation(this, R.anim.dialpad_slide_out_bottom); |
| } |
| |
| mSlideIn.setInterpolator(AnimUtils.EASE_IN); |
| mSlideOut.setInterpolator(AnimUtils.EASE_OUT); |
| |
| mSlideOut.setAnimationListener(mSlideOutListener); |
| |
| 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 CallCardFragment has been inflated and is ready |
| // to receive it. |
| mShowDialpadRequested = icicle.getBoolean(SHOW_DIALPAD_EXTRA); |
| mAnimateDialpadOnShow = false; |
| mDtmfText = icicle.getString(DIALPAD_TEXT_EXTRA); |
| } |
| Log.d(this, "onCreate(): exit"); |
| } |
| |
| @Override |
| protected void onSaveInstanceState(Bundle out) { |
| out.putBoolean(SHOW_DIALPAD_EXTRA, mCallButtonFragment.isDialpadVisible()); |
| if (mDialpadFragment != null) { |
| out.putString(DIALPAD_TEXT_EXTRA, mDialpadFragment.getDtmfText()); |
| } |
| super.onSaveInstanceState(out); |
| } |
| |
| @Override |
| protected void onStart() { |
| Log.d(this, "onStart()..."); |
| super.onStart(); |
| |
| // setting activity should be last thing in setup process |
| InCallPresenter.getInstance().setActivity(this); |
| } |
| |
| @Override |
| protected void onResume() { |
| Log.i(this, "onResume()..."); |
| super.onResume(); |
| |
| mIsForegroundActivity = true; |
| |
| InCallPresenter.getInstance().setThemeColors(); |
| InCallPresenter.getInstance().onUiShowing(true); |
| |
| if (mShowDialpadRequested) { |
| mCallButtonFragment.displayDialpad(true /* show */, |
| mAnimateDialpadOnShow /* animate */); |
| mShowDialpadRequested = false; |
| mAnimateDialpadOnShow = false; |
| |
| if (mDialpadFragment != null) { |
| mDialpadFragment.setDtmfText(mDtmfText); |
| mDtmfText = null; |
| } |
| } |
| |
| if (mShowPostCharWaitDialogOnResume) { |
| showPostCharWaitDialog(mShowPostCharWaitDialogCallId, mShowPostCharWaitDialogChars); |
| } |
| } |
| |
| // onPause is guaranteed to be called when the InCallActivity goes |
| // in the background. |
| @Override |
| protected void onPause() { |
| Log.d(this, "onPause()..."); |
| super.onPause(); |
| |
| mIsForegroundActivity = false; |
| |
| if (mDialpadFragment != null ) { |
| mDialpadFragment.onDialerKeyUp(null); |
| } |
| |
| InCallPresenter.getInstance().onUiShowing(false); |
| if (isFinishing()) { |
| InCallPresenter.getInstance().unsetActivity(this); |
| } |
| } |
| |
| @Override |
| protected void onStop() { |
| Log.d(this, "onStop()..."); |
| super.onStop(); |
| } |
| |
| @Override |
| protected void onDestroy() { |
| Log.d(this, "onDestroy()... this = " + this); |
| InCallPresenter.getInstance().unsetActivity(this); |
| super.onDestroy(); |
| } |
| |
| /** |
| * Returns true when theActivity is in foreground (between onResume and onPause). |
| */ |
| /* package */ boolean isForegroundActivity() { |
| return mIsForegroundActivity; |
| } |
| |
| private boolean hasPendingErrorDialog() { |
| return mDialog != null; |
| } |
| |
| /** |
| * Dismisses the in-call screen. |
| * |
| * We never *really* finish() the InCallActivity, since we don't want to get destroyed and then |
| * have to be re-created from scratch for the next call. Instead, we just move ourselves to the |
| * back of the activity stack. |
| * |
| * This also means that we'll no longer be reachable via the BACK button (since moveTaskToBack() |
| * puts us behind the Home app, but the home app doesn't allow the BACK key to move you any |
| * farther down in the history stack.) |
| * |
| * (Since the Phone app itself is never killed, this basically means that we'll keep a single |
| * InCallActivity instance around for the entire uptime of the device. This noticeably improves |
| * the UI responsiveness for incoming calls.) |
| */ |
| @Override |
| public void finish() { |
| Log.i(this, "finish(). Dialog showing: " + (mDialog != null)); |
| |
| // skip finish if we are still showing a dialog. |
| if (!hasPendingErrorDialog() && !mAnswerFragment.hasPendingDialogs()) { |
| super.finish(); |
| } |
| } |
| |
| @Override |
| protected void onNewIntent(Intent intent) { |
| Log.d(this, "onNewIntent: intent = " + intent); |
| |
| // 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!) |
| 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); |
| } |
| |
| @Override |
| public void onBackPressed() { |
| Log.i(this, "onBackPressed"); |
| |
| // BACK is also used to exit out of any "special modes" of the |
| // in-call UI: |
| |
| if (!mConferenceManagerFragment.isVisible() && !mCallCardFragment.isVisible()) { |
| return; |
| } |
| |
| if (mDialpadFragment != null && mDialpadFragment.isVisible()) { |
| mCallButtonFragment.displayDialpad(false /* show */, true /* animate */); |
| return; |
| } else if (mConferenceManagerFragment.isVisible()) { |
| showConferenceCallManager(false); |
| return; |
| } |
| |
| // Always disable the Back key while an incoming call is ringing |
| final Call call = CallList.getInstance().getIncomingCall(); |
| if (call != null) { |
| Log.d(this, "Consume Back press for an incoming call"); |
| return; |
| } |
| |
| // Nothing special to do. Fall back to the default behavior. |
| super.onBackPressed(); |
| } |
| |
| @Override |
| public boolean onOptionsItemSelected(MenuItem item) { |
| final int itemId = item.getItemId(); |
| if (itemId == android.R.id.home) { |
| onBackPressed(); |
| return true; |
| } |
| return super.onOptionsItemSelected(item); |
| } |
| |
| @Override |
| public boolean onKeyUp(int keyCode, KeyEvent event) { |
| // push input to the dialer. |
| if (mDialpadFragment != null && (mDialpadFragment.isVisible()) && |
| (mDialpadFragment.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 super.onKeyUp(keyCode, event); |
| } |
| |
| @Override |
| public boolean onKeyDown(int keyCode, KeyEvent event) { |
| switch (keyCode) { |
| case KeyEvent.KEYCODE_CALL: |
| boolean handled = InCallPresenter.getInstance().handleCallKey(); |
| if (!handled) { |
| Log.w(this, "InCallActivity 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: |
| // toggle mute |
| TelecomAdapter.getInstance().mute(!AudioModeProvider.getInstance().getMute()); |
| return true; |
| |
| // Various testing/debugging features, enabled ONLY when VERBOSE == true. |
| case KeyEvent.KEYCODE_SLASH: |
| if (Log.VERBOSE) { |
| Log.v(this, "----------- InCallActivity View dump --------------"); |
| // Dump starting from the top-level view of the entire activity: |
| Window w = this.getWindow(); |
| View decorView = w.getDecorView(); |
| Log.d(this, "View dump:" + decorView); |
| return true; |
| } |
| break; |
| case KeyEvent.KEYCODE_EQUALS: |
| // TODO: Dump phone state? |
| break; |
| } |
| |
| if (event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event)) { |
| return true; |
| } |
| |
| return super.onKeyDown(keyCode, event); |
| } |
| |
| private boolean handleDialerKeyDown(int keyCode, KeyEvent event) { |
| Log.v(this, "handleDialerKeyDown: keyCode " + keyCode + ", event " + 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. |
| if (mDialpadFragment != null && mDialpadFragment.isVisible()) { |
| return mDialpadFragment.onDialerKeyDown(event); |
| |
| // TODO: If the dialpad isn't currently visible, maybe |
| // consider automatically bringing it up right now? |
| // (Just to make sure the user sees the digits widget...) |
| // But this probably isn't too critical since it's awkward to |
| // use the hard keyboard while in-call in the first place, |
| // especially now that the in-call UI is portrait-only... |
| } |
| |
| return false; |
| } |
| |
| @Override |
| public void onConfigurationChanged(Configuration config) { |
| InCallPresenter.getInstance().getProximitySensor().onConfigurationChanged(config); |
| Log.d(this, "onConfigurationChanged "+config.orientation); |
| |
| // Check to see if the orientation changed to prevent triggering orientation change events |
| // for other configuration changes. |
| if (config.orientation != mCurrentOrientation) { |
| mCurrentOrientation = config.orientation; |
| InCallPresenter.getInstance().onDeviceRotationChange( |
| getWindowManager().getDefaultDisplay().getRotation()); |
| InCallPresenter.getInstance().onDeviceOrientationChange(mCurrentOrientation); |
| } |
| super.onConfigurationChanged(config); |
| } |
| |
| public CallButtonFragment getCallButtonFragment() { |
| return mCallButtonFragment; |
| } |
| |
| public CallCardFragment getCallCardFragment() { |
| return mCallCardFragment; |
| } |
| |
| private void internalResolveIntent(Intent intent) { |
| final String action = intent.getAction(); |
| |
| if (action.equals(intent.ACTION_MAIN)) { |
| // This action is the normal way to bring up the in-call UI. |
| // |
| // But we do check here for one extra that can come along with the |
| // ACTION_MAIN intent: |
| |
| if (intent.hasExtra(SHOW_DIALPAD_EXTRA)) { |
| // 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. |
| |
| final boolean showDialpad = intent.getBooleanExtra(SHOW_DIALPAD_EXTRA, false); |
| Log.d(this, "- internalResolveIntent: SHOW_DIALPAD_EXTRA: " + showDialpad); |
| |
| relaunchedFromDialer(showDialpad); |
| } |
| |
| if (intent.getBooleanExtra(NEW_OUTGOING_CALL_EXTRA, false)) { |
| intent.removeExtra(NEW_OUTGOING_CALL_EXTRA); |
| Call call = CallList.getInstance().getOutgoingCall(); |
| if (call == null) { |
| call = CallList.getInstance().getPendingOutgoingCall(); |
| } |
| |
| Bundle extras = null; |
| if (call != null) { |
| extras = call.getTelecommCall().getDetails().getExtras(); |
| } |
| if (extras == null) { |
| // Initialize the extras bundle to avoid NPE |
| extras = new Bundle(); |
| } |
| |
| Point touchPoint = null; |
| if (TouchPointManager.getInstance().hasValidPoint()) { |
| // Use the most immediate touch point in the InCallUi if available |
| touchPoint = TouchPointManager.getInstance().getPoint(); |
| } else { |
| // Otherwise retrieve the touch point from the call intent |
| if (call != null) { |
| touchPoint = (Point) extras.getParcelable(TouchPointManager.TOUCH_POINT); |
| } |
| } |
| |
| // This is only true in the case where an outgoing call is initiated by tapping |
| // on the "Select account dialog", in which case we skip the initial animation. In |
| // most other cases the circular reveal is done by OutgoingCallAnimationActivity. |
| final boolean showCircularReveal = |
| intent.getBooleanExtra(SHOW_CIRCULAR_REVEAL_EXTRA, false); |
| mCallCardFragment.animateForNewOutgoingCall(touchPoint, showCircularReveal); |
| |
| /* |
| * If both a phone account handle and a list of phone accounts to choose from are |
| * missing, then disconnect the call because there is no way to place an outgoing |
| * call. |
| * The exception is emergency calls, which may be waiting for the ConnectionService |
| * to set the PhoneAccount during the PENDING_OUTGOING state. |
| */ |
| if (call != null && !isEmergencyCall(call)) { |
| final List<PhoneAccountHandle> phoneAccountHandles = extras |
| .getParcelableArrayList(android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS); |
| if (call.getAccountHandle() == null && |
| (phoneAccountHandles == null || phoneAccountHandles.isEmpty())) { |
| TelecomAdapter.getInstance().disconnectCall(call.getId()); |
| } |
| } |
| |
| dismissKeyguard(true); |
| } |
| |
| Call pendingAccountSelectionCall = CallList.getInstance().getWaitingForAccountCall(); |
| if (pendingAccountSelectionCall != null) { |
| mCallCardFragment.setVisible(false); |
| Bundle extras = pendingAccountSelectionCall |
| .getTelecommCall().getDetails().getExtras(); |
| |
| final List<PhoneAccountHandle> phoneAccountHandles; |
| if (extras != null) { |
| phoneAccountHandles = extras.getParcelableArrayList( |
| android.telecom.Call.AVAILABLE_PHONE_ACCOUNTS); |
| } else { |
| phoneAccountHandles = new ArrayList<>(); |
| } |
| |
| SelectPhoneAccountListener listener = new SelectPhoneAccountListener() { |
| @Override |
| public void onPhoneAccountSelected(PhoneAccountHandle selectedAccountHandle, |
| boolean setDefault) { |
| InCallPresenter.getInstance().handleAccountSelection(selectedAccountHandle, |
| setDefault); |
| } |
| @Override |
| public void onDialogDismissed() { |
| InCallPresenter.getInstance().cancelAccountSelection(); |
| } |
| }; |
| |
| SelectPhoneAccountDialogFragment.showAccountDialog(getFragmentManager(), |
| R.string.select_phone_account_for_calls, true, phoneAccountHandles, |
| listener); |
| } else { |
| mCallCardFragment.setVisible(true); |
| } |
| |
| return; |
| } |
| } |
| |
| private boolean isEmergencyCall(Call call) { |
| final Uri handle = call.getHandle(); |
| if (handle == null) { |
| return false; |
| } |
| return PhoneNumberUtils.isEmergencyNumber(handle.getSchemeSpecificPart()); |
| } |
| |
| private void relaunchedFromDialer(boolean showDialpad) { |
| mShowDialpadRequested = showDialpad; |
| mAnimateDialpadOnShow = true; |
| |
| if (mShowDialpadRequested) { |
| // 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. |
| final Call call = CallList.getInstance().getActiveOrBackgroundCall(); |
| if (call != null && call.getState() == State.ONHOLD) { |
| TelecomAdapter.getInstance().unholdCall(call.getId()); |
| } |
| } |
| } |
| |
| private void initializeInCall() { |
| if (mCallCardFragment == null) { |
| mCallCardFragment = (CallCardFragment) getFragmentManager() |
| .findFragmentById(R.id.callCardFragment); |
| } |
| |
| mChildFragmentManager = mCallCardFragment.getChildFragmentManager(); |
| |
| if (mCallButtonFragment == null) { |
| mCallButtonFragment = (CallButtonFragment) mChildFragmentManager |
| .findFragmentById(R.id.callButtonFragment); |
| mCallButtonFragment.getView().setVisibility(View.INVISIBLE); |
| } |
| |
| if (mAnswerFragment == null) { |
| mAnswerFragment = (AnswerFragment) mChildFragmentManager |
| .findFragmentById(R.id.answerFragment); |
| } |
| |
| if (mConferenceManagerFragment == null) { |
| mConferenceManagerFragment = (ConferenceManagerFragment) getFragmentManager() |
| .findFragmentById(R.id.conferenceManagerFragment); |
| mConferenceManagerFragment.getView().setVisibility(View.INVISIBLE); |
| } |
| } |
| |
| /** |
| * Simulates a user click to hide the dialpad. This will update the UI to show the call card, |
| * update the checked state of the dialpad button, and update the proximity sensor state. |
| */ |
| public void hideDialpadForDisconnect() { |
| mCallButtonFragment.displayDialpad(false /* show */, true /* animate */); |
| } |
| |
| public void dismissKeyguard(boolean dismiss) { |
| if (mDismissKeyguard == dismiss) { |
| return; |
| } |
| mDismissKeyguard = dismiss; |
| if (dismiss) { |
| getWindow().addFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); |
| } else { |
| getWindow().clearFlags(WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD); |
| } |
| } |
| |
| private void showDialpad(boolean showDialpad) { |
| // If the dialpad is being shown and it has not already been loaded, replace the dialpad |
| // placeholder with the actual fragment before continuing. |
| if (mDialpadFragment == null && showDialpad) { |
| final FragmentTransaction loadTransaction = mChildFragmentManager.beginTransaction(); |
| View fragmentContainer = findViewById(R.id.dialpadFragmentContainer); |
| mDialpadFragment = new DialpadFragment(); |
| loadTransaction.replace(fragmentContainer.getId(), mDialpadFragment, |
| DialpadFragment.class.getName()); |
| loadTransaction.commitAllowingStateLoss(); |
| mChildFragmentManager.executePendingTransactions(); |
| } |
| |
| final FragmentTransaction ft = mChildFragmentManager.beginTransaction(); |
| if (showDialpad) { |
| ft.show(mDialpadFragment); |
| } else { |
| ft.hide(mDialpadFragment); |
| } |
| ft.commitAllowingStateLoss(); |
| } |
| |
| public void displayDialpad(boolean showDialpad, boolean animate) { |
| // If the dialpad is already visible, don't animate in. If it's gone, don't animate out. |
| if ((showDialpad && isDialpadVisible()) || (!showDialpad && !isDialpadVisible())) { |
| return; |
| } |
| // 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) { |
| showDialpad(showDialpad); |
| } else { |
| if (showDialpad) { |
| showDialpad(true); |
| mDialpadFragment.animateShowDialpad(); |
| } |
| mCallCardFragment.onDialpadVisiblityChange(showDialpad); |
| mDialpadFragment.getView().startAnimation(showDialpad ? mSlideIn : mSlideOut); |
| } |
| |
| InCallPresenter.getInstance().getProximitySensor().onDialpadVisible(showDialpad); |
| } |
| |
| public boolean isDialpadVisible() { |
| return mDialpadFragment != null && mDialpadFragment.isVisible(); |
| } |
| |
| /** |
| * Hides or shows the conference manager fragment. |
| * |
| * @param show {@code true} if the conference manager should be shown, {@code false} if it |
| * should be hidden. |
| */ |
| public void showConferenceCallManager(boolean show) { |
| mConferenceManagerFragment.setVisible(show); |
| |
| // Need to hide the call card fragment to ensure that accessibility service does not try to |
| // give focus to the call card when the conference manager is visible. |
| mCallCardFragment.getView().setVisibility(show ? View.GONE : View.VISIBLE); |
| } |
| |
| public void showPostCharWaitDialog(String callId, String chars) { |
| if (isForegroundActivity()) { |
| final PostCharDialogFragment fragment = new PostCharDialogFragment(callId, chars); |
| fragment.show(getFragmentManager(), "postCharWait"); |
| |
| mShowPostCharWaitDialogOnResume = false; |
| mShowPostCharWaitDialogCallId = null; |
| mShowPostCharWaitDialogChars = null; |
| } else { |
| mShowPostCharWaitDialogOnResume = true; |
| mShowPostCharWaitDialogCallId = callId; |
| mShowPostCharWaitDialogChars = chars; |
| } |
| } |
| |
| @Override |
| public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) { |
| if (mCallCardFragment != null) { |
| mCallCardFragment.dispatchPopulateAccessibilityEvent(event); |
| } |
| return super.dispatchPopulateAccessibilityEvent(event); |
| } |
| |
| public void maybeShowErrorDialogOnDisconnect(DisconnectCause disconnectCause) { |
| Log.d(this, "maybeShowErrorDialogOnDisconnect"); |
| |
| if (!isFinishing() && !TextUtils.isEmpty(disconnectCause.getDescription()) |
| && (disconnectCause.getCode() == DisconnectCause.ERROR || |
| disconnectCause.getCode() == DisconnectCause.RESTRICTED)) { |
| showErrorDialog(disconnectCause.getDescription()); |
| } |
| } |
| |
| public void dismissPendingDialogs() { |
| if (mDialog != null) { |
| mDialog.dismiss(); |
| mDialog = null; |
| } |
| mAnswerFragment.dismissPendingDialogues(); |
| } |
| |
| /** |
| * Utility function to bring up a generic "error" dialog. |
| */ |
| private void showErrorDialog(CharSequence msg) { |
| Log.i(this, "Show Dialog: " + msg); |
| |
| dismissPendingDialogs(); |
| |
| mDialog = new AlertDialog.Builder(this) |
| .setMessage(msg) |
| .setPositiveButton(android.R.string.ok, new OnClickListener() { |
| @Override |
| public void onClick(DialogInterface dialog, int which) { |
| onDialogDismissed(); |
| }}) |
| .setOnCancelListener(new OnCancelListener() { |
| @Override |
| public void onCancel(DialogInterface dialog) { |
| onDialogDismissed(); |
| }}) |
| .create(); |
| |
| mDialog.getWindow().addFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND); |
| mDialog.show(); |
| } |
| |
| private void onDialogDismissed() { |
| mDialog = null; |
| InCallPresenter.getInstance().onDismissDialog(); |
| } |
| } |