Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 61e23164 authored by Kamaljeet Maini's avatar Kamaljeet Maini
Browse files

Add new options for call waiting response in InCallUI

When an incoming call is received while there is already an active call,
the user will be given four choices for the response:

1) Left button: hold the current call and answer incoming call
2) Bottom button: end the current call and answer the incoming call
3) Right button: decline the incoming call
4) Top button: Send a text message to the incoming caller if texting
   is enabled

The changes in this component include removing the call reponse popup
and handling the new hidden API.

Issue-Id: CYNGNOS-2749

Change-Id: I920b16842a609f3350bfd5344a4b367da4580349
(cherry picked from commit 1179ffdd)
parent c510dd26
Loading
Loading
Loading
Loading
+87 −106
Original line number Diff line number Diff line
@@ -48,7 +48,6 @@ import com.android.internal.telephony.PhoneConstants;
import com.android.internal.telephony.TelephonyProperties;
import com.android.internal.telephony.util.BlacklistUtils;
import com.android.internal.util.IndentingPrintWriter;
import com.android.server.telecom.ui.CallWaitingDialog;

import java.util.Collection;
import java.util.Collections;
@@ -903,6 +902,19 @@ public class CallsManager extends Call.ListenerBase implements VideoProviderProx
     * @param videoState The video state in which to answer the call.
     */
    void answerCall(final Call call, final int videoState) {
        answerCall(call, videoState, TelecomManager.CALL_WAITING_RESPONSE_NO_POPUP_END_CALL);
    }

    /**
     * Instructs Telecom to answer the specified call. Intended to be invoked by the in-call
     * app through {@link InCallAdapter} after Telecom notifies it of an incoming call followed by
     * the user opting to answer said call.
     *
     * @param call The call to answer.
     * @param videoState The video state in which to answer the call.
     * @param callWaitingResponseType Response type for call waiting.
     */
    void answerCall(final Call call, final int videoState, final int callWaitingResponseType) {
        if (!mCalls.contains(call)) {
            Log.i(this, "Request to answer a non-existent call %s", call);
        } else {
@@ -922,17 +934,17 @@ public class CallsManager extends Call.ListenerBase implements VideoProviderProx
                        activeCall.disconnect();
                    }
                } else {
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            CallWaitingListener listener = new CallWaitingListener(call,
                    switch (callWaitingResponseType) {
                        case TelecomManager.CALL_WAITING_RESPONSE_NO_POPUP_HOLD_CALL:
                            handleHoldCallAndAnswer(call,
                                    activeCall, videoState, CallsManager.this);
                            Dialog dialog =
                                    CallWaitingDialog.createCallWaitingDialog(mContext, call,
                                            listener, listener);
                            dialog.show();
                            break;
                        case TelecomManager.CALL_WAITING_RESPONSE_NO_POPUP_END_CALL:
                        default:
                            handleEndCallAndAnswer(call,
                                    activeCall, videoState, CallsManager.this);
                            break;
                    }
                    });
                    return;
                }
            }
@@ -2464,41 +2476,11 @@ public class CallsManager extends Call.ListenerBase implements VideoProviderProx
        return false;
    }

    private static class CallWaitingListener implements DialogInterface
            .OnClickListener {

        private final Call mNewCall;
        private final Call mActiveCall;
        private final CallsManager mLocalCallsManager;
        private final int mVideoState;

        /* package */ CallWaitingListener(Call newCall, Call activeCall, int videoState,
    private void handleHoldCallAndAnswer(Call newCall, Call activeCall, int videoState,
                                         CallsManager callsManager) {
            mNewCall = newCall;
            mActiveCall = activeCall;
            mLocalCallsManager = callsManager;
            mVideoState = videoState;
        }

        @Override
        public void onClick(DialogInterface dialog, int which) {
            switch (which) {
                case DialogInterface.BUTTON_POSITIVE:
                    // Hold call
                    handleHoldCallAndAnswer();
                    break;
                case DialogInterface.BUTTON_NEGATIVE:
                default:
                    // End call
                    handleEndCallAndAnswer();
                    break;
            }
        }

        private void handleHoldCallAndAnswer() {
        // We only want one held call, so if we have a held call already we need to
        // disconnect it
            Call heldCall = mLocalCallsManager.getHeldCall();
        Call heldCall = callsManager.getHeldCall();
        if (heldCall != null) {
            Log.v(this,
                    "Disconnecting held call %s before holding active call ", heldCall);
@@ -2507,58 +2489,57 @@ public class CallsManager extends Call.ListenerBase implements VideoProviderProx

        // TODO: This active call reference can be nullified and discarded from another thread,
        // Fix this by reworking the state machine surrounding calls within telecomm.
            if (mActiveCall != null) {
        if (activeCall != null) {
            Log.v(this, "Holding active/dialing call %s before answering incoming call %s.",
                        mLocalCallsManager.mForegroundCall, mNewCall);
                mActiveCall.hold();
                    callsManager.mForegroundCall, newCall);
            activeCall.hold();
        }
        // TODO: Wait until we get confirmation of
        // the active call being
        // on-hold before answering the new call.
        // TODO: Import logic from
        // CallManager.acceptCall()
            updateListeners(false);
        updateListeners(newCall, activeCall, videoState, callsManager, false);
    }

        private void handleEndCallAndAnswer() {
    private void handleEndCallAndAnswer(Call newCall, Call activeCall, int videoState,
                                        CallsManager callsManager) {
        // We don't want to hold, just disconnect

        // TODO: This active call reference can be nullified and discarded from another thread,
        // Fix this by reworking the state machine surrounding calls within telecomm.
            if (mActiveCall != null) {
        if (activeCall != null) {
            Log.v(this, "Holding active/dialing call %s for termination before answering incoming call %s.",
                        mLocalCallsManager.mForegroundCall, mNewCall);
                mActiveCall.hold();
                    callsManager.mForegroundCall, newCall);
            activeCall.hold();
        }
        // TODO: Wait until we get confirmation of
        // the active call being
        // on-hold before answering the new call.
        // TODO: Import logic from
        // CallManager.acceptCall()
            updateListeners(true);
        updateListeners(newCall, activeCall, videoState, callsManager, true);
    }

        private void updateListeners(boolean terminateActive) {
            for (CallsManagerListener listener : mLocalCallsManager.mListeners) {
                listener.onIncomingCallAnswered(mNewCall);
    private void updateListeners(Call newCall, Call activeCall, int videoState,
                                 CallsManager callsManager, boolean terminateActive) {
        for (CallsManagerListener listener : callsManager.mListeners) {
            listener.onIncomingCallAnswered(newCall);
        }
            mLocalCallsManager.updateLchStatus(mNewCall.getTargetPhoneAccount().getId());
        callsManager.updateLchStatus(newCall.getTargetPhoneAccount().getId());
        // We do not update the UI until we get
        // confirmation of
        // the answer() through
        // {@link #markCallAsActive}.
            mNewCall.answer(mVideoState);
            if (terminateActive && mActiveCall != null) {
        newCall.answer(videoState);
        if (terminateActive && activeCall != null) {
            Log.v(this, "Terminating active call %s after answering incoming call %s.",
                    mActiveCall, mNewCall);
                mActiveCall.disconnect();
                    activeCall, newCall);
            activeCall.disconnect();
        }

            if (mLocalCallsManager.isSpeakerphoneAutoEnabled(mVideoState)) {
                mNewCall.setStartWithSpeakerphoneOn(true);
            }
        if (callsManager.isSpeakerphoneAutoEnabled(videoState)) {
            newCall.setStartWithSpeakerphoneOn(true);
        }

    }

}
+21 −0
Original line number Diff line number Diff line
@@ -59,6 +59,27 @@ class InCallAdapter extends IInCallAdapter.Stub {
        }
    }

    @Override
    public void answerCallWithCallWaitingResponse(String callId, int videoState, int
            callWaitingResponseType) {
        long token = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                Log.d(this, "answerCall(%s,%d)", callId, videoState);
                if (mCallIdMapper.isValidCallId(callId)) {
                    Call call = mCallIdMapper.getCall(callId);
                    if (call != null) {
                        mCallsManager.answerCall(call, videoState, callWaitingResponseType);
                    } else {
                        Log.w(this, "answerCall, unknown call id: %s", callId);
                    }
                }
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    @Override
    public void rejectCall(String callId, boolean rejectWithMessage, String textMessage) {
        long token = Binder.clearCallingIdentity();
+0 −106
Original line number Diff line number Diff line
/*
 * Copyright (C) 2016 The CyanogenMod 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.server.telecom.ui;

import android.annotation.NonNull;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.text.TextUtils;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ListView;
import com.android.server.telecom.Call;
import com.android.server.telecom.R;

/**
 * <pre>
 *     This dialog shows up when you answer another call while currently in a call
 * </pre>
 *
 * @see {@link Dialog}
 */
public class CallWaitingDialog extends Dialog implements AdapterView.OnItemClickListener{

    // Members
    DialogInterface.OnClickListener mHoldListener;
    DialogInterface.OnClickListener mEndListener;

    // Views
    private ListView mButtonListView;
    private CallWaitingButtonAdapter mButtonAdapter;
    private Integer buttons[] = new Integer[] {
            CallWaitingDialogButton.BUTTON_HOLD,
            CallWaitingDialogButton.BUTTON_END
    };

    private CallWaitingDialog(@NonNull Context context,
            DialogInterface.OnClickListener holdListener,
            DialogInterface.OnClickListener endListener) {
        super(context, android.R.style.Theme_Material_Light_Dialog);
        setContentView(R.layout.call_waiting_dialog);
        mButtonListView = (ListView) findViewById(R.id.lv_buttons);
        mButtonAdapter = new CallWaitingButtonAdapter(context,
                R.layout.call_waiting_dialog_button, buttons);
        mButtonListView.setAdapter(mButtonAdapter);
        mHoldListener = holdListener;
        mEndListener = endListener;
        mButtonListView.setOnItemClickListener(this);
    }

    /**
     * Create a new call waiting choice dialog.  This should be the entrypoint
     *
     * @param context {@link Context}
     * @param call {@link Call}
     * @param holdListener {@link android.content.DialogInterface.OnClickListener}
     * @param endListener {@link android.content.DialogInterface.OnClickListener}
     * @return {@link Dialog}
     */
    public static Dialog createCallWaitingDialog(Context context, final Call call,
            DialogInterface.OnClickListener holdListener,
            DialogInterface.OnClickListener endListener) {
        CallWaitingDialog dialog = new CallWaitingDialog(context, holdListener, endListener);
        String template = context.getResources().getString(R.string.call_waiting_dialog_title);
        String name = (TextUtils.isEmpty(call.getName()) ? call.getNumber() : call.getName());
        String title = String.format(template, name);
        dialog.setTitle(title);
        dialog.setCancelable(false);
        dialog.getWindow()
                .setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
        return dialog;
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        switch (position) {
            case CallWaitingDialogButton.BUTTON_HOLD:
                if (mHoldListener != null) {
                    mHoldListener.onClick(this, DialogInterface.BUTTON_POSITIVE);
                }
                break;
            case CallWaitingDialogButton.BUTTON_END:
            default:
                if (mEndListener != null) {
                    mEndListener.onClick(this, DialogInterface.BUTTON_NEGATIVE);
                }
                break;
        }
        this.dismiss();
    }

}