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

11

53

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.

Excellent answered 28/3, 2013 at 14:6 Comment(8)
It blocks background controls like picking/rejecting calls. I have done similar way.Nyeman
How you solve this ????Cockspur
Solutions from this thread worked, but overlying view is hiding call control buttons on some devices. Do someone has any solution for this???Sharpen
i using your code but reciver is not calling.. why is that soImpolicy
can you define manifest..i am doing sameIncondite
Did you get any solution to this ? If So, please share your code plsTunstall
did you get the solution?Balmy
Consider this other solution: Provide the caller id for incoming call from my own appSynchronism
H
32

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.

Hurwitz answered 20/4, 2013 at 5:32 Comment(3)
I have tried to call mu activity after 3 4 5 seconds but its nor working.Excellent
I am awarding this bounty to you because your thinking for this question is going in right direction. It needs some delays but this is not working for jelly bean Please help me.Excellent
I am trying to dismiss the popup window after call is disconnected, but unable to succeed.Nyeman
S
9

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.

Soninlaw answered 18/4, 2013 at 10:14 Comment(5)
No I haven't tried it before on create. I will be trying it. But whats the reason for writing it before super.oncreate().Excellent
I will be trying but the reason behind it . Why do you think It will work.Excellent
No reason.. I know it sounds crazy but worked for me before (but not this problem)Soninlaw
I will trying on Saturday because I am on leave rigth now.Excellent
all the best. i think another possibility you are calling the super method inside the try block right? what if the super is causing an exception and your block is catching it. Then the remaining code will not execute right? So try rearranging the order, just try calling the super.onCreate(savedInstanceState); before your try block and check does your Log is printing or not.Soninlaw
A
9

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>
Absinthe answered 18/5, 2013 at 6:38 Comment(11)
Can you put a screen shot because In my jelly bean its not working.Excellent
you can check out screenshots for Callurgency on google play. That's how i did it, and it works on jelly bean 4.2Absinthe
thanks @SamAdams this is helpful, how did you bind the urgency data to a phone call though?Mungovan
I have followed your code. could you tell me where and how I can remove this layout from windowManager?Birth
Consider adding permission "android.permission.SYSTEM_ALERT_WINDOW" in case you encountered "Unable to add window android.view.ViewRoot$W@6292365 — permission denied for this window type" errorErnie
Nice solution mate, just one question, how to remove this with call hang up event ?Cockspur
@Prince have you found solution for that problem ? thanksDrake
Nop. I didnt found any solution regrading same :( @ralphspoonCockspur
@Prince, sad, tried everything, when ever i tried to remove it it fires IllegalArgumentException, the view token is null, thats why it cant be removed. if you found solution please add. thanksDrake
for those who are still looking for an answer please look at my answer here : #8767239Drake
you are superb @Sam Adams, i was searching from 15 days now search end here. can u please tell me how to get layout.xml to show specific layout on code , waiting for your AnswersElflock
W
7

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
Waers answered 15/6, 2013 at 7:22 Comment(7)
Thanks..I am not able to get this working on latest android 4.4.X..Please helpScoggins
What is problem?? Dialog is not display or listener not working?.Waers
Hi Dharmik, I know that the thread is quite old. But would you like sharing the Code .. as i am able to make a toast over the calling screen but not a Text box like you .... Please share the Code. I am trying to make a PopUp screen over the Screen when a call is received or made. Same as that of TrueCaller Please HelpDeprave
@shivam.kotwalia I have added git hub link. Let me know if any issue,Waers
Hi Dharmik, Thanks :) I will definitely disturb you, if i get a problem.Deprave
Do you have ay reference for, how to do this in iOS?Dynasty
If the activity is on the top how the user will be able to access call accept and reject button?Balmy
T
6

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.

Threephase answered 20/4, 2013 at 7:1 Comment(2)
@Nikhil Agrawal This is the right direction. I have something similar working for versions 2.x to 4.2.x without fail. (See screen shots in my app 4W Job Manager). You need to be more specific in your comments: "Not working" does not encourage us to help any further.Dialectic
LayoutParams.TYPE_SYSTEM_OVERLAY doesn't take input events on window.Montiel
I
6

I'm trying something similar, adding an extra button to the incoming call screen.

The answer Sam Adams posted is working for me, although I'm calling the code from a PhoneStateListener. Apart from that, the only real difference to his code is I'm inflating a layout:

overlay = (RelativeLayout) inflater.inflate(R.layout.overlay, null);
wm.addView(overlay, params);

It is working on emulators as well as on a HTC One S (running Android 4.1.1).

Something you need to keep in mind is keeping a reference to the overlay view you are adding, and remove it again (call removeView() on windowmanager instance) when the phone goes back to idle (when the listener gets TelephonyManager.CALL_STATE_IDLE), otherwise your overlay will stay on screen.

        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    if(overlay!=null)
    {
        wm.removeView(overlay);
        overlay = null;
    }
Inhabitancy answered 2/8, 2013 at 4:4 Comment(0)
E
1

We were also facing similar issue that the overlay was not displayed on a device with pin lock. The solution that worked for us is below:

mWindowManager = (WindowManager) getSystemService(WINDOW_SERVICE);
    mParams = new LayoutParams(
        LayoutParams.MATCH_PARENT,
        LayoutParams.WRAP_CONTENT,
        LayoutParams.TYPE_SYSTEM_ERROR,
        LayoutParams.FLAG_NOT_FOCUSABLE,
        PixelFormat.TRANSLUCENT);

It was LayoutParams.TYPE_SYSTEM_ERROR that made the difference.

Emigrant answered 14/10, 2015 at 22:47 Comment(0)
L
1

try this

AlertDialog.Builder builder = new AlertDialog.Builder(context.getApplicationContext());
            LayoutInflater inflater = LayoutInflater.from(context);
            View dialogView = inflater.inflate(R.layout.caller_dialog, null);
            ImageView button = dialogView.findViewById(R.id.close_btn);
            builder.setView(dialogView);
            final AlertDialog alert = builder.create();
            alert.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
            alert.getWindow().setType(WindowManager.LayoutParams.TYPE_PHONE);
            alert.setCanceledOnTouchOutside(true);
            alert.show();
            WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
            Window window = alert.getWindow();
            window.addFlags(WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE);
            window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
            window.setGravity(Gravity.TOP);
            lp.copyFrom(window.getAttributes());
            //This makes the dialog take up the full width
            lp.width = WindowManager.LayoutParams.MATCH_PARENT;
            lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
            window.setAttributes(lp);
            button.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    //close the service and remove the from from the window
                    alert.dismiss();
                }
            });
Literati answered 24/1, 2018 at 9:10 Comment(1)
Does it works for all the versions ? What permissions are required in this case ?Savoie
G
0

My method:

  1. use receiver to receive events of phone call
  2. use service to make the overlay

    ps:wmParams.type = WindowManager.LayoutParams.TYPE_PHONE; 
    
Gallop answered 28/10, 2013 at 9:51 Comment(0)
P
0
new Handler().postDelayed(new Runnable() {
    @Override
    public void run() {
        // TODO Auto-generated method stub
        Intent i = new Intent(context, CallingIncoming.class);
        i.putExtras(intent);
        i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK /*| Intent.FLAG_ACTIVITY_CLEAR_TASK*/);
        context.startActivity(i);
    }
}, 450);//It will help you to delay your screen and after it your screen will be top of default app screen
Pruter answered 26/6, 2016 at 6:51 Comment(0)
T
0

Use a simple broadcast receiver and put this code in broadcast receiver:

public void onReceive(final Context context, final Intent intent) {

    Log.v(LOG_TAG, "Receieved notification about network status");
    WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);

    WindowManager.LayoutParams params = new WindowManager.LayoutParams(
            WindowManager.LayoutParams.MATCH_PARENT,
            WindowManager.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 = WindowManager.LayoutParams.MATCH_PARENT;
    params.width = WindowManager.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);

}
Thoron answered 15/7, 2016 at 11:17 Comment(1)
How do I get click event on TYPE_SYSTEM_OVERLAY the button on overlay is not click able ..Pls helpDerekderelict

© 2022 - 2024 — McMap. All rights reserved.