1

I have successfully written the code to end calls programmatically. But before the call ends, I want to retrieve the caller's name to log it. How would I retrieve the caller's name? Note, the caller is presumed to be within the contact list.

I've already come across How to retrieve the contact name from incoming call number?, and I didn't find the answer satisfying. It had no context, and very little information.

Cœur
  • 37,241
  • 25
  • 195
  • 267
Slay
  • 221
  • 1
  • 5
  • 12

1 Answers1

0

You need the incoming/outgoing call numbers to retrieve the contact name from the device's contact list. Define the following permissions and receiver within the app's manifest:

<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />

<receiver android:name=".CallReceiver" >
            <intent-filter android:priority="999" >
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
        </receiver>

Then within the CallReciever class:

public class CallReceiver extends BroadcastReceiver {

    // The receiver will be recreated whenever android feels like it. We need a
    // static variable to remember data between instantiations
    static PhonecallStartEndDetector listener;
    string contactName;
    Context context;

    @Override
    public void onReceive(Context context, Intent intent) {
         this.context = context;
        if (listener == null) {
            listener = new PhonecallStartEndDetector();

        }

        // We listen to two intents. The new outgoing call only tells us of an
        // outgoing call. We use it to get the number.
        if (intent.getAction()
                .equals("android.intent.action.NEW_OUTGOING_CALL")) {
            listener.setOutgoingNumber(intent.getExtras().getString(
                    "android.intent.extra.PHONE_NUMBER"));
            return;
        }

        // The other intent tells us the phone state changed. Here we set a
        // listener to deal with it
        TelephonyManager telephony = (TelephonyManager) context
                .getSystemService(Context.TELEPHONY_SERVICE);
        telephony.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);

    }


    // Deals with actual events
    public class PhonecallStartEndDetector extends PhoneStateListener {
        int lastState = TelephonyManager.CALL_STATE_IDLE;
        boolean isIncoming;
        boolean isIncomingPicked;
        boolean isOutgoingStarted;
        String savedNumber; // because the passed incoming is only valid in ringing

        public PhonecallStartEndDetector() {

        }

        // The outgoing number is only sent via a separate intent, so we need to
        // store it out of band
        public void setOutgoingNumber(String number) {
            savedNumber = number;
        }

        // Incoming call- goes from IDLE to RINGING when it rings, to OFFHOOK
        // when it's answered, to IDLE when its hung up
        // Outgoing call- goes from IDLE to OFFHOOK when it dials out, to IDLE
        // when hung up
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);
                                                // state is changed
            if (lastState == state) {
                return;
            }

            switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
            //an incoming call has been started

                isIncoming = true;
                savedNumber = incomingNumber;

            break;
            case TelephonyManager.CALL_STATE_OFFHOOK:

                // Transition of ringing->offhook are pickups of incoming calls.
                // Nothing down on them
                if (lastState != TelephonyManager.CALL_STATE_RINGING) { 
                                                                        //if pickup but device didn't ring..it means was an outgoing call
                    isIncoming = false;
                    isIncomingPicked = false;
                    isOutgoingStarted = true;

                } else if (lastState == TelephonyManager.CALL_STATE_RINGING) { 
                                                                                // if pickup and device rang..it means an incoming call has been picked

                    isIncoming = true;
                    isIncomingPicked = true;
                    isOutgoingStarted = false;

                break;
            case TelephonyManager.CALL_STATE_IDLE:

                // Went to idle- this is the end of a call. What type depends on
                // previous state(s)
                if (lastState == TelephonyManager.CALL_STATE_RINGING) {
                    // Ring but no pickup- a miss


                }else if (isOutgoingStarted) {
               //outgoing call ended
              contactName = getContactName(savedNumber, context);

                } else if (isIncoming || isIncomingPicked) {
                    //incoming call ended
             contactName = getContactName(savedNumber, context);

            }
                break;
            }

        }

        //method to retrieve contact name 

private String getContactName(String number, Context context) {
        String contactNumber = "";

        // // define the columns I want the query to return
        String[] projection = new String[] {
                ContactsContract.PhoneLookup.DISPLAY_NAME,
                ContactsContract.PhoneLookup.NUMBER,
                ContactsContract.PhoneLookup.HAS_PHONE_NUMBER };

        // encode the phone number and build the filter URI
        Uri contactUri = Uri.withAppendedPath(
                ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
                Uri.encode(number));

        // query time
        Cursor cursor = context.getContentResolver().query(contactUri,
                projection, null, null, null);
        // querying all contacts = Cursor cursor =
        // context.getContentResolver().query(ContactsContract.Contacts.CONTENT_URI,
        // projection, null, null, null);

        if (cursor.moveToFirst()) {
            contactName = cursor.getString(cursor
                    .getColumnIndex(ContactsContract.PhoneLookup.DISPLAY_NAME));
        }
        cursor.close();
        return contactNumber.equals("") ? number : contactName;

    }

}

Since you're interested in retrieving the caller's name when before a call is ended, I retrieve the name when an incoming/outgoing call is ended. You can retrieve the caller's name when an incoming call is started or picked or when an outgoing call is started. I've added comments as a guide. Hope it helps :)

Nana Ghartey
  • 7,901
  • 1
  • 24
  • 26