Store a phone number with a custom label in call log and then call it with my app

from the CommonsWare Community archives

At September 6, 2018, 11:25am, Raptor asked:

I have an application which uses the Agora library to facilitate a videochat between two parties.

What I want to do is to store the number that calls in the phone’s call log, and then, when the user goes into the call log and presses on that number, that number to be passed into my app and used inside my app to call the respective user.

Here’s how I currently log the number that is calling the user:

@Override
        public void onInviteReceived(final String channelName, final String contactPhone, int uid, final String s2) { //call out other remote receiver
            Log.i(TAG, "onInviteReceived  channelName = " + channelName + " contactPhone = " + contactPhone);
            runOnUiThread(new Runnable() {
                @SuppressLint("MissingPermission")
                @Override
                public void run() {
                    Gson gson = new Gson();
                    CallExtra callExtra = gson.fromJson(s2, CallExtra.class);

                    ContentValues values = new ContentValues();
                    values.put(CallLog.Calls.CACHED_NUMBER_LABEL, "FairyApp");
                    values.put(CallLog.Calls.CACHED_NAME, "FairyApp");
                    values.put(CallLog.Calls.TYPE, CallLog.Calls.INCOMING_TYPE);
                    values.put(CallLog.Calls.DATE, System.currentTimeMillis());
                    values.put(CallLog.Calls.DURATION, 50);
                    values.put(CallLog.Calls.NUMBER, contactPhone);
                    
                    getContentResolver().insert(CallLog.Calls.CONTENT_URI, values);

As you can see, I’ve just hardcoded the information just to see if it works, and indeed it does - the number is being recorded into the call log of the phone (obviously, I took care of the permissions to do this already).

Then, I created a broadcast receiver that intercepts an intent of type android.intent.action.NEW_OUTGOING_CALL , in order to intercept the user calling that number and redirecting him or her to my app and then initiate the call from there.

Here’s how the Broadcast Receiver looks in the AndroidManifest:

<receiver android:name=".broadcastreceivers.OutgoingCallReceiver" >
        <intent-filter>
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        </intent-filter>
</receiver>

Here’s what the Receiver currently does (it’s hardcoded, for now):

public class OutgoingCallReceiver extends BroadcastReceiver {

@Override
public void onReceive(Context context, Intent intent) {
    String phoneNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
    if (phoneNumber.equals("4444")){
        context.startActivity(new Intent(context, HomeActivity.class));
    }
}

}

So, it basically intercepts the intent to call, gets the phone number, and if the phone number is “4444”, then it goes into my HomeActivity.

This works.

The problem that I’m having is trying to save a “label” for the stored number - I want to store an information that “this number is of type “MyApp”” and then, when the user presses to call that number from the phone’s call log, I want to use that information in my Broadcast Receiver and check “if this is type “MyApp” then open my HomeActivity, else just ignore this broadcast”.

So, my question is - how do I store a label into the call log, or some custom piece of information that I can then use in my Broadcast Receiver to identify calls that “belong” to my application?

Thank you.


At September 6, 2018, 12:15pm, mmurphy replied:

Off the cuff, you would store it in your own database, along with the phone number, then look it up from there. IOW, you would replace phoneNumber.equals("4444") with something that looks in your database (or perhaps an in-memory cache, if your process happens to have been around already).

You cannot add columns to the call log, and it is not safe for you to assume that you can just stuff your data in some existing column and it would work across 20,000+ device models and 2,000,000,000+ devices.

Moreover, since AFAIK all you have is a phone number, looking up your data in your own database and looking it up in the call log would be of roughly equal complexity (programming, runtime performance, etc.), so it is unclear what adding data to the call log would gain you, even if it were possible.


At September 6, 2018, 12:22pm, Raptor replied:

So it’s impossible to add some metadata to the stored “item” in the call log, like “this call is of type “MyApp””? I would have expected it to have such a thing (a custom type would’ve been nice, too, like “MyApp” instead of “Outgoing call”.

Well, OK, thanks for the suggestion.


At September 6, 2018, 12:38pm, mmurphy replied:

I am not aware of anything for that. It’s conceivable that there’s some undocumented hack that you could use, but then reliability across all those device models (and Android OS versions) becomes an issue.


At September 6, 2018, 12:38pm, Raptor replied:

Here’s another idea that we came up with:

How about I store the “number” with “MyApp” in front of it. For example, I used this:

values.put(CallLog.Calls.NUMBER, "Fairy" + contactPhone);

This resulted in “Fairy4444” being stored in my call log. When I actually called that number, it dialed “324794444”. So, I can choose some long “code” for a number used in my app, store the code + the real number, and then in my receiver I can check if the phone number starts with the code and then substring the real number out of it. Of course, I’ll have to choose some long code in order to avoid interferences with real numbers.


At September 6, 2018, 12:51pm, mmurphy replied:

How many of the 20,000+ Android device models were you planning on testing, to see whether the behavior on this one device holds up?

How many of the other apps that rely on accurate call log data were you planning on breaking?

Please store custom data in your own app.


At September 6, 2018, 12:56pm, Raptor replied:

Can’t I put some option in the call log to “call using MyApp”? WhatsApp has an option like that - when you select a phone number from your call log, there’s an icon there to use that number with WhatsApp. I’ve always wondered how they put that in there - do you know how that can be accomplished?


At September 6, 2018, 1:02pm, Raptor replied:

Oh, and there’s an additional problem:

Let’s say I do store the phone number in a database that belongs to my app, and then save the number in a phone log as right now. Then in that case, yes, I will be able to intercept the call intent and check if that number is in my database.

The problem is - in that situation, my app would always intercept the call to that number. Maybe the person just wants to do a usual phone call, and not get into my app. There’s no way to know the intent of the person.

So then, how would I differentiate between someone that wants to just do a phone call to that number and a person that wants to do a call in my app with that number?


At September 7, 2018, 11:14am, mmurphy replied:

My guess is that the app has an <activity> with an <intent-filter> for ACTION_DIAL (or perhaps ACTION_CALL). I have not researched this point, but I’d use this app or equivalents to examine the WhatsApp manifest and see what might be enabling the integration.

how would I differentiate between someone that wants to just do a phone call to that number and a person that wants to do a call in my app with that number?

Presumably they would start your app to use your app. I would not use NEW_OUTGOING_CALL as an integration point.


At September 7, 2018, 11:42am, Raptor replied:

I found this on SOF: https://stackoverflow.com/questions/48019509/how-to-provide-whatsapp-like-functionality-inside-contacts-apps but it looks very complicated, and I’m not sure if that’s going to solve my problem.

However, I’ve decided on a different approach: I will store my numbers in a local database and display a DialogFragment whenever a call will be initiated with a number that is in the app’s database, and ask the user if he or she wants to use the app or just make a regular phone call, with an option to remember the choice. For numbers that are not in the database, obviously this will not trigger.

And then I just redirect the user to my app if he or she presses “Yes”. Now the issue is that if you press “No”, the broadcast doesn’t propagate further to let the phone’s calling application to deal with it as usual.

Also, why wouldn’t you use NEW_OUTGOING_CALL as an integration point? What would you use instead? By the way, the broadcast receiver is declared in the manifest, not in an activity - the idea was to start the app whenever a phone number that has been used with the app is called (which, admittedly, has the potential to become annoying).


At September 7, 2018, 12:00pm, mmurphy replied:

What would you use instead?

I would figure out what <intent-filter> to use on <activity>, based on an examination of relevant app manifests.

the idea was to start the app whenever a phone number that has been used with the app is called (which, admittedly, has the potential to become annoying).

I’ll also add “creepy” to the list of issues. I do not want to be examining all calls just to optionally handle some calls. And if that means that I cannot be integrated into existing contacts or call log apps, so be it.


At September 7, 2018, 1:12pm, Raptor replied:

Why would you label that as “creepy”? Because it’s “verifying the numbers that you dial”? It doesn’t do anything with that information, it just compares it with the numbers in the app’s database. My issue is that it appears that once I intercept the intent to call and the user presses “No” when asked to choose if he wants to use the app for that number, the number doesn’t seem to propagate anymore to the default calling app of the phone.

There are also other problems, like the fact that, according to the docs:

Some apps (such as VoIP apps) may want to redirect the outgoing call to use their own service instead. Those apps should first prevent the call from being placed by setting resultData to null and then start their own app to make the call.

But how can I do that when I don’t know yet, in my Broadcast Receiver, if the user chooses to use my app or not? I don’t get the user choice until later, when I launch an activity with a DialogFragment that gets the user’s choice.

So, it’s really weird: you’d expect the broadcast to continue to the default phone call app but somehow it doesn’t, even though the docs say it should (unless you set the resultData to null - by the way, not sure how you do that).

EDIT: I tried it on my personal phone and the broadcast did propagate to the calling app.


At September 7, 2018, 1:29pm, mmurphy replied:

The user has no way of knowing this. A cursory security analysis would just show that you are monitoring the details of all outgoing calls in real time, in addition to after the fact (via the call log). It would take a more in-depth analysis to determine exactly what you are doing with the data.

Personally, I would not want my app to have access to any private information like this that is not absolutely necessary.


At September 7, 2018, 2:13pm, Raptor replied:

Yes, that’s perfectly legitimate. The alternative to all this would be to just have the history of the calls in the app and be done with it. I’m more interested in principle, like, “what can you do with X and Y”, simply because it’s interesting to know what is possible and what is not (regardless of how good an idea that actually is).

For example, I was wondering if I could intercept the call attempt, pass the number into the alert dialog, and if the user chooses “no” then I initiate the call myself, from the app (this only for numbers that have previously been used in the app). That way, it’s going to be “transparent” for the user.

The app is for a videochat between parents and kids, by the way, for spoken stories with AR effects and so on.