Pop up window over Android native incoming call screen like true caller Android app

80,416

Solution 1

I am not sure that your custom GUI will always be on top of the default one, because the system broadcast receiver and your receiver are both trying to display its GUI on top of the screen. We are not sure which one is called first, but one tricky work to make your GUI on top of the screen is when the phone is ringing call your activity after 1-2 second(s) used handler for that.

new Handler().postDelayed(new Runnable() {

     @Override
     public void run() {
         // TODO Auto-generated method stub
         Intent intent = new Intent(context, AcceptReject.class);
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         context.startActivity(intent);
     }
 }, 2000);

I hope it may help you.

Solution 2

I just tested on the Android 4.2 (Jelly Bean) emulator, and it works perfect by blocking the entire incoming call screen just like truecaller:

public void onReceive(Context context, Intent intent) {

    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
        LayoutParams.MATCH_PARENT,
        LayoutParams.MATCH_PARENT, WindowManager.LayoutParams.TYPE_SYSTEM_ALERT |
        WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL |
        WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSPARENT);

    params.height = LayoutParams.MATCH_PARENT;
    params.width = LayoutParams.MATCH_PARENT;
    params.format = PixelFormat.TRANSLUCENT;

    params.gravity = Gravity.TOP;

    LinearLayout ly = new LinearLayout(context);
    ly.setBackgroundColor(Color.RED);
    ly.setOrientation(LinearLayout.VERTICAL);

    wm.addView(ly, params);
}

In the manifest:

<receiver android:name=""  android:enabled="true" >
    <intent-filter android:priority="-1">
        <action android:name="android.intent.action.PHONE_STATE" />
    </intent-filter>
</receiver>

Solution 3

Try the code before the super.onCreate method. I think after calling the super the code is skipped. Sometime this type of tricks worked for me.

Solution 4

I am also working on it (I might be wrong to understand you here). What you want to achieve is to display that activity in Android 4.2 (Jelly Bean). I just placed a delay to display the activity. I have used PhoneStateListener in different class. I am able to display new activity on caller screen. Here is my full code:

Enter image description here

File MyBroadcastReceiver.java

public class MyBroadcastReceiver extends BroadcastReceiver {
    static CustomPhoneStateListener phoneStateListener;
    Context context;
    Intent intent;

    @Override
    public void onReceive(Context context, Intent intent) {
        this.context = context;
        this.intent = intent;
        // TODO Auto-generated method stub

            TelephonyManager telephonyManager = (TelephonyManager) context
                    .getSystemService(Context.TELEPHONY_SERVICE);           
            phoneStateListener = new CustomPhoneStateListener(context);
            telephonyManager.listen(phoneStateListener,
                    PhoneStateListener.LISTEN_CALL_STATE);
    }
}

File CustomPhoneStateListener.java

public class CustomPhoneStateListener extends PhoneStateListener {

    // private static final String TAG = "PhoneStateChanged";
    Context context; // Context to make Toast if required
    private AudioManager amanager;
    Intent i1;

    public CustomPhoneStateListener(Context context) {
        super();
        this.context = context;
        i1 = new Intent(context, YourActivity.class);       
        i1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        i1.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);

    }

    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);

        switch (state) {
        case TelephonyManager.CALL_STATE_IDLE:
            Toast.makeText(context, "Phone state Idle", Toast.LENGTH_LONG)
                    .show();

            break;
        case TelephonyManager.CALL_STATE_OFFHOOK:

            Toast.makeText(context, "Phone state Off hook", Toast.LENGTH_LONG)
                    .show();

            break;
        case TelephonyManager.CALL_STATE_RINGING:           
            try {
                Thread.sleep(3000);
                context.startActivity(i1);              
            } catch (Exception e) {
                e.getLocalizedMessage();
            }

        default:
            break;
        }
    }

and YourActivity will remain as you have created... Note: I am facing some problems also in this code they are here.

  1. When the call closed is clolse (missed call or rejected) the activity is not being closed.
  2. I am not able to click on Activity (I want to put one button there for my app)
  3. It works only the first time. When I make call a second time, my app stops (I think it is because Activity is not being closed when the call is dismissed)

(Help accepted for these problems. Thank you. Might help some one)

UPDATE

HERE IS LINK OF SMALL DEMO HOW TO ACHIEVE THIS.

  1. When the call closed is clolse (missed call or rejected) the activity is not being closed. - SOLVED
  2. I am not able to click on Activity (I want to put one button there for my app) - SOLVED
  3. It works only the first time. When I make call a second time, my app stops (I think it is because Activity is not being closed when the call is dismissed) - SOLVED

Solution 5

I think you shouldn't start activity to achieve the described result. You need a separate view having LayoutParams.TYPE_SYSTEM_OVERLAY set in its layout params.

You can position this view wherever you want on the screen, or just cover the whole screen.

Here are few lines of code:

 _av = new ActivatorView(this);
 _avLayoutParams = new WindowManager.LayoutParams(0, 0, 0, 0,
     WindowManager.LayoutParams.TYPE_SYSTEM_OVERLAY,
     WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,
     PixelFormat.OPAQUE);
 _avLayoutParams.screenBrightness = _fScreenBrightness = 20f;

 WindowManager wm = (WindowManager) getSystemService(WINDOW_SERVICE);
 wm.addView(_av, _avLayoutParams);

https://bitbucket.org/gyrussolutions/yaab/src/f01cc8aff690cae1b1107287cb17835b8a3c1643/src/biz/gyrus/yaab/LightMonitorService.java?at=default#cl-338 - the full source code, consider it a sample.

Share:
80,416
Nikhil Agrawal
Author by

Nikhil Agrawal

Experienced and Passionate Java developer with a wide range of skill set and wide technology background in both core and advanced java world. I have experience of working with agile methodologies with hands on experience of tools like JIRA and confluence. I have had training and am following secure software development methods. I have been using Secure SDLC in my projects for quite a while. I have handled the requirements from initiation to deployment phase, like requirement gathering and analysis, transforming business requirement into technical requirement, preparing technical designs, developing and testing the module with the help of a development team and deployment. I like to explore and implement new technologies and have implemented new technologies in projects before. I like to read technical news, articles and blogs to keep myself updated. Technology Stack : • Java • Spring Framework • SpringBoot • Microservices • Hibernate • REST Web Services • Javascript, jQuery, HTML • Backbone.js, Marionette.js • Git • JIRA • Salesforce • Jenkins

Updated on March 29, 2020

Comments

  • Nikhil Agrawal
    Nikhil Agrawal about 4 years

    I am developing a broadcast receiver for incoming calls in Android and on getting incoming calls I want to inflate a pop up over the native incoming call screen.

    I completed that code. But now the problem is that in the Android 4.1 (Jelly Bean) API level 17 when a phone rings, the PHONE_STATE is coming as OFF HOOK, and if I am calling an activity, it gets called, but the code under it doesn't get executed. I am listing the code:

    My broadcast receiver

    package com.example.popwindowonincomingcallscreen;
    
    import java.util.concurrent.Executors;
    import java.util.concurrent.ScheduledExecutorService;
    
    import android.content.BroadcastReceiver;
    import android.content.Context;
    import android.content.Intent;
    import android.telephony.TelephonyManager;
    import android.util.Log;
    
    public class IncomingBroadcastReceiver extends BroadcastReceiver {
    
        @Override
        public void onReceive(Context context, Intent intent) {
    
            Log.d("IncomingBroadcastReceiver: onReceive: ", "flag1");
    
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            Log.d("IncomingBroadcastReceiver: onReceive: ", state);
            if (state.equals(TelephonyManager.EXTRA_STATE_RINGING)
                    || state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
    
                Log.d("Ringing", "Phone is ringing");
    
                Intent i = new Intent(context, IncomingCallActivity.class);
                i.putExtras(intent);
                i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                i.addFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
                Wait.oneSec();
                context.startActivity(i);
            }
        }
    }
    

    An the activity which I am calling:

    import android.app.Activity;
    import android.os.Bundle;
    import android.telephony.TelephonyManager;
    import android.util.Log;
    import android.view.View.MeasureSpec;
    import android.view.Window;
    import android.view.WindowManager;
    import android.widget.TextView;
    
    public class IncomingCallActivity extends Activity {
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
    
            try {
                Log.d("IncomingCallActivity: onCreate: ", "flag2");
    
                */ After this line, the code is not executed in Android 4.1 (Jelly Bean) only/*
    
                // TODO Auto-generated method stub
                super.onCreate(savedInstanceState);
    
                getWindow().addFlags(WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE);
                getWindow().addFlags(
                        WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL);
    
                Log.d("IncomingCallActivity: onCreate: ", "flagy");
    
                setContentView(R.layout.main);
    
                Log.d("IncomingCallActivity: onCreate: ", "flagz");
    
                String number = getIntent().getStringExtra(
                        TelephonyManager.EXTRA_INCOMING_NUMBER);
                TextView text = (TextView) findViewById(R.id.text);
                text.setText("Incoming call from " + number);
            } 
            catch (Exception e) {
                Log.d("Exception", e.toString());
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
    

    After

    try {
        Log.d("IncomingCallActivity: onCreate: ", "flag2");
    }
    

    The code is not executing in Android 4.1 (Jelly Bean), but in other versions it is working.

    I have tried almost all ways I can do. This code is displaying an translucent activity over the native call screen, and it doesn't block background controls, like picking up the phone. But I want it like true caller. I have attached an snapshot on how the true caller is displaying a window on the incoming call screen.

    How can I achieve this functionality for an Android app?

    This is how a true caller works:

    Enter image description here

    My present output:

    Enter image description here

    Update 1

    After bounty also I am not getting the exact thing I am looking for, but I will get back to all; I am working upon it. Anyway, this code works for most Android phones. If anybody is going to use and catch the solution for it, please write here so that everybody can get the benefit.

    Update 2

    I tried to implement Toast in the broadcast receiver's onReceive method because toast is a native component of Android, but it is also not getting displayed in Android 4.1 (Jelly Bean).

    My idea was to implement Toast in the broadcast receiver's onReceive method and afterwards changing its design according to our needs and tuning its duration of display. But one more problem is that findViewById doesn't work in the broadcast receiver, so I think we have to make a LinearLayout programmatically for customizing the toast.