private void startUi(InCallState inCallState) { final Call incomingCall = mCallList.getIncomingCall(); final boolean isCallWaiting = (incomingCall != null && incomingCall.getState() == Call.State.CALL_WAITING); // If the screen is off, we need to make sure it gets turned on for incoming calls. // This normally works just fine thanks to FLAG_TURN_SCREEN_ON but that only works // when the activity is first created. Therefore, to ensure the screen is turned on // for the call waiting case, we finish() the current activity and start a new one. // There should be no jank from this since the screen is already off and will remain so // until our new activity is up. if (mProximitySensor.isScreenReallyOff() && isCallWaiting) { if (isActivityStarted()) { mInCallActivity.finish(); } mInCallActivity = null; } final PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); // If the screen is on, we'll prefer to not interrupt the user too much and slide in a card if (pm.isScreenOn()) { Intent intent = new Intent(mContext, InCallCardActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); mContext.startActivity(intent); } else { mStatusBarNotifier.updateNotificationAndLaunchIncomingCallUi(inCallState, mCallList); } }
/** * Called when there is a change to the call list. Sets the In-Call state for the entire in-call * app based on the information it gets from CallList. Dispatches the in-call state to all * listeners. Can trigger the creation or destruction of the UI based on the states that is * calculates. */ @Override public void onCallListChange(CallList callList) { if (callList == null) { return; } InCallState newState = getPotentialStateFromCallList(callList); newState = startOrFinishUi(newState); // Renable notification shade and soft navigation buttons, if we are no longer in the // incoming call screen if (!newState.isIncoming()) { if (mAccelerometerListener != null) { mAccelerometerListener.enableSensor(false); } CallCommandClient.getInstance().setSystemBarNavigationEnabled(true); } // Set the new state before announcing it to the world Log.i(this, "Phone switching state: " + mInCallState + " -> " + newState); mInCallState = newState; // notify listeners of new state for (InCallStateListener listener : mListeners) { Log.d(this, "Notify " + listener + " of state " + mInCallState.toString()); listener.onStateChange(mInCallState, callList); } if (isActivityStarted()) { final boolean hasCall = callList.getActiveOrBackgroundCall() != null || callList.getOutgoingCall() != null; mInCallActivity.dismissKeyguard(hasCall); } }
/** * Called when a call becomes disconnected. Called everytime an existing call changes from being * connected (incoming/outgoing/active) to disconnected. */ @Override public void onDisconnect(Call call) { hideDialpadForDisconnect(); maybeShowErrorDialogOnDisconnect(call); // We need to do the run the same code as onCallListChange. onCallListChange(CallList.getInstance()); if (isActivityStarted()) { mInCallActivity.dismissKeyguard(false); } }
private void attemptFinishActivity() { // Finish our presenter card in all cases, we won't need it anymore whatever might // happen. if (mInCallCardActivity != null) { mInCallCardActivity.finish(); } final boolean doFinish = (mInCallActivity != null && isActivityStarted()); Log.i(this, "Hide in call UI: " + doFinish); if (doFinish) { mInCallActivity.finish(); } }
/** * When the state of in-call changes, this is the first method to get called. It determines if the * UI needs to be started or finished depending on the new state and does it. */ private InCallState startOrFinishUi(InCallState newState) { Log.d(this, "startOrFinishUi: " + mInCallState + " -> " + newState); // TODO: Consider a proper state machine implementation // If the state isn't changing, we have already done any starting/stopping of // activities in a previous pass...so lets cut out early if (newState == mInCallState) { return newState; } // A new Incoming call means that the user needs to be notified of the the call (since // it wasn't them who initiated it). We do this through full screen notifications and // happens indirectly through {@link StatusBarListener}. // // The process for incoming calls is as follows: // // 1) CallList - Announces existence of new INCOMING call // 2) InCallPresenter - Gets announcement and calculates that the new InCallState // - should be set to INCOMING. // 3) InCallPresenter - This method is called to see if we need to start or finish // the app given the new state. // 4) StatusBarNotifier - Listens to InCallState changes. InCallPresenter calls // StatusBarNotifier explicitly to issue a FullScreen Notification // that will either start the InCallActivity or show the user a // top-level notification dialog if the user is in an immersive app. // That notification can also start the InCallActivity. // 5) InCallActivity - Main activity starts up and at the end of its onCreate will // call InCallPresenter::setActivity() to let the presenter // know that start-up is complete. // // [ AND NOW YOU'RE IN THE CALL. voila! ] // // Our app is started using a fullScreen notification. We need to do this whenever // we get an incoming call. final boolean startStartupSequence = (InCallState.INCOMING == newState); // A new outgoing call indicates that the user just now dialed a number and when that // happens we need to display the screen immediateley. // // This is different from the incoming call sequence because we do not need to shock the // user with a top-level notification. Just show the call UI normally. final boolean showCallUi = (InCallState.OUTGOING == newState); // TODO: Can we be suddenly in a call without it having been in the outgoing or incoming // state? I havent seen that but if it can happen, the code below should be enabled. // showCallUi |= (InCallState.INCALL && !isActivityStarted()); // The only time that we have an instance of mInCallActivity and it isn't started is // when it is being destroyed. In that case, lets avoid bringing up another instance of // the activity. When it is finally destroyed, we double check if we should bring it back // up so we aren't going to lose anything by avoiding a second startup here. boolean activityIsFinishing = mInCallActivity != null && !isActivityStarted(); if (activityIsFinishing) { Log.i(this, "Undo the state change: " + newState + " -> " + mInCallState); return mInCallState; } if (showCallUi) { Log.i(this, "Start in call UI"); showInCall(false); } else if (startStartupSequence) { Log.i(this, "Start Full Screen in call UI"); // We're about the bring up the in-call UI for an incoming call. If we still have // dialogs up, we need to clear them out before showing incoming screen. if (isActivityStarted()) { mInCallActivity.dismissPendingDialogs(); } startUi(newState); } else if (newState == InCallState.NO_CALLS) { // The new state is the no calls state. Tear everything down. attemptFinishActivity(); attemptCleanup(); } return newState; }
/** Hides the dialpad. Called when a call is disconnected (Requires hiding dialpad). */ private void hideDialpadForDisconnect() { if (isActivityStarted()) { mInCallActivity.hideDialpadForDisconnect(); } }
/** * For some disconnected causes, we show a dialog. This calls into the activity to show the dialog * if appropriate for the call. */ private void maybeShowErrorDialogOnDisconnect(Call call) { // For newly disconnected calls, we may want to show a dialog on specific error conditions if (isActivityStarted() && call.getState() == Call.State.DISCONNECTED) { mInCallActivity.maybeShowErrorDialogOnDisconnect(call.getDisconnectCause()); } }
public void onPostDialCharWait(int callId, String chars) { mInCallActivity.showPostCharWaitDialog(callId, chars); }
/** * Returns true of the activity has been created and is running. Returns true as long as activity * is not destroyed or finishing. This ensures that we return true even if the activity is paused * (not in foreground). */ public boolean isActivityStarted() { return (mInCallActivity != null && !mInCallActivity.isDestroyed() && !mInCallActivity.isFinishing()); }
/** Returns true if the incall app is the foreground application. */ public boolean isShowingInCallUi() { return (isActivityStarted() && mInCallActivity.isForegroundActivity()); }