Making your own indication of an incoming call

  • Tutorial
After the last post about our Android application , some readers of the article had a question, how to show their own informational plate during a call? Well, today we will answer this question.

The general plan is quite simple:
  • we intercept the "incoming call" event using the intent filter ;
  • we draw our own window with the necessary information over the window of the telephone dialer.

Let’s go through each item in detail.

Intercept the call


In order to be able to intercept the “they are calling us” event, you need to add a request to read the status of the phone to the application manifest.

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

There, register a service to intercept the “call” event.

<receiverandroid:name=".CallReceiver"><intent-filter><actionandroid:name="android.intent.action.PHONE_STATE"/></intent-filter></receiver></application>

Finally, write some code to handle this event.

publicclassCallReceiverextendsBroadcastReceiver{
   privatestaticboolean incomingCall = false;
   @OverridepublicvoidonReceive(Context context, Intent intent){
       if (intent.getAction().equals("android.intent.action.PHONE_STATE")) {
           String phoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
           if (phoneState.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
               //Трубка не поднята, телефон звонит
               String phoneNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
               incomingCall = true;
               Log.debug("Show window: " + phoneNumber);
           } elseif (phoneState.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
               //Телефон находится в режиме звонка (набор номера при исходящем звонке / разговор)if (incomingCall) {
                   Log.debug("Close window.");
                   incomingCall = false;
               }
           } elseif (phoneState.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
               //Телефон находится в ждущем режиме - это событие наступает по окончанию разговора//или в ситуации "отказался поднимать трубку и сбросил звонок".if (incomingCall) {
                  Log.debug("Close window.");
                  incomingCall = false;
               }
           }
       }
   }
}

Please note that in this example we only catch the “incoming call” event, but the code shows how it can be redone if you need to track the outgoing one too. The variable with information about the call is static, because the BroadcastReceiver lives on the principle of "received the message - processed it - died", and the events of "picked up / ended the conversation" will receive a new instance of the object.

Call debugging


Of course, you can debug a call on a real phone, but it's easier and faster to still test on an emulator. A call from one native emulator to another is made using the standard dialer application, 4 digits act as a phone number - the port of this emulator.



An alternative way is to call from the Android Device Monitor utility or from the console using ADB . A noticeable minus of all these methods is that the emulator breaks the connection with the debugger for the duration of the call, but the ability to test the behavior of the window on different OS versions and different resolutions is worth it.

Show the die


Well, now the most interesting part is showing our die. To do this, firstly, we will need to add a request for rights to the manifest to create windows with the "system notification" flag.

<uses-permissionandroid:name="android.permission.SYSTEM_ALERT_WINDOW"/>

Secondly, we will edit the OnRecieve method and replace the simple log entry with the call or close of our window.

Log.debug("Show window: " + phoneNumber);
showWindow(context, phoneNumber);//добавили//[...]
Log.debug("Close window.");
closeWindow();//добавили

Well, the most interesting thing is the opening and closing of our window.

privatestatic WindowManager windowManager;
privatestatic ViewGroup windowLayout;
privatevoidshowWindow(Context context, String phone){
   windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
   LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
   WindowManager.LayoutParams params = new WindowManager.LayoutParams(
           WindowManager.LayoutParams.MATCH_PARENT,
           WindowManager.LayoutParams.WRAP_CONTENT,
           WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
           WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL,
           PixelFormat.TRANSLUCENT);
   params.gravity = Gravity.TOP;
   windowLayout = (ViewGroup) layoutInflater.inflate(R.layout.info, null);
   TextView textViewNumber=(TextView) windowLayout.findViewById(R.id.textViewNumber);
   Button buttonClose=(Button) windowLayout.findViewById(R.id.buttonClose);
   textViewNumber.setText(phone);
   buttonClose.setOnClickListener(new View.OnClickListener() {
       @OverridepublicvoidonClick(View v){
           closeWindow();
       }
   });
   windowManager.addView(windowLayout, params);
}
privatevoidcloseWindow(){
   if (windowLayout !=null){
       windowManager.removeView(windowLayout);
       windowLayout =null;
   }
}

Please note that to display the window we do not start a separate activity , but with our hands we display a new window through WindowManager . Why? The new activity falls into the general stack of screens, so if your application has at least one screen and it is running at the time of the call, the following will happen:

  1. the native telephone dialer is displayed on the screen
  2. the screen displays the active screen of your application
  3. your “window on top” of the dialer is displayed on the screen

As a result, the user will not be able to answer or reject the call without switching to the dialer on his own. In the case of manual window creation, point 2 is not executed and the user will see exactly what we wanted: a telephone dialer and our window on top of it.

Underwater rocks


Unfortunately, everything is not as rosy as it seems. As often happens in android, it is difficult to achieve 100% compatibility of a tricky feature.

First, you need to understand that users can have phones with different screen sizes, different resolutions and different versions of android, and you have to try pretty hard so that your window does not overlap the native controls on all possible configurations

Secondly, on the part of HTC phones with their own call program, the information block simply does not appear! It seems that their dialer application is also displayed with a system priority, so our plate appears to be “under their window”. It is unpleasant, but we have not yet found a solution to this problem. It is possible that the dialers of some other phones also conflict with this feature, but so far we have negative experience with only some models from HTC.

Demo project on GitHub.

Also popular now: