简体   繁体   English

在Android原生来电屏幕上弹出窗口,如真正的来电Android应用

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

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. 我正在开发一个广播接收器,用于Android中的来电和接听来电,我想在本地来电屏幕上弹出一个弹出窗口。

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. 但现在的问题是,在Android 4.1(Jelly Bean)API级别17当电话响铃时, PHONE_STATEPHONE_STATE OFF HOOK ,如果我正在调用一个活动,它会被调用,但它下面的代码不会得到执行。 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. 代码没有在Android 4.1(Jelly Bean)中执行,但在其他版本中它正在运行。

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? 如何为Android应用程序实现此功能?

This is how a true caller works: 这是真正的来电者的工作方式:

在此输入图像描述

My present output: 我现在的输出:

在此输入图像描述

Update 1 更新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. 无论如何,此代码适用于大多数Android手机。 If anybody is going to use and catch the solution for it, please write here so that everybody can get the benefit. 如果有人要使用并抓住解决方案,请写在这里,以便每个人都可以获得好处。

Update 2 更新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). 我尝试在广播接收器的onReceive方法中实现Toast,因为toast是Android的本机组件,但它也没有在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. 我的想法是在广播接收器的onReceive方法中实现Toast,然后根据我们的需要改变其设计并调整其显示持续时间。 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. 但还有一个问题是findViewById在广播接收器中不起作用,所以我认为我们必须以编程方式制作一个LinearLayout来自定义toast。

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. 我不确定您的自定义GUI将始终位于默认GUI之上,因为系统广播接收器和接收器都试图在屏幕顶部显示其GUI。 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. 我们不确定哪一个首先被调用,但是在屏幕上设置GUI的一个棘手的工作是当手机响了1-2秒后使用处理程序来响应你的活动。

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. 我希望它可以帮助你。

Try the code before the super.onCreate method. super.onCreate方法之前尝试代码。 I think after calling the super the code is skipped. 我想在调用super之后会跳过代码。 Sometime this type of tricks worked for me. 有时这种技巧对我有用。

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: 我刚刚在Android 4.2(Jelly Bean)模拟器上进行了测试,它通过阻止整个来电屏幕就像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>

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). 您想要实现的是在Android 4.2(Jelly Bean)中显示该活动。 I just placed a delay to display the activity. 我只是延迟显示活动。 I have used PhoneStateListener in different class. 我在不同的类中使用过PhoneStateListener。 I am able to display new activity on caller screen. 我可以在来电屏幕上显示新活动。 Here is my full code: 这是我的完整代码:

在此输入图像描述

File MyBroadcastReceiver.java 文件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 文件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. 和YourActivity将保持原样...注意:我在这里的代码中也遇到了一些问题。

  1. When the call closed is clolse (missed call or rejected) the activity is not being closed. 当关闭的呼叫是clolse(未接来电或拒绝)时,活动未被关闭。
  2. I am not able to click on Activity (I want to put one button there for my app) 我无法点击Activity(我想在我的应用程序中放一个按钮)
  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 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. 当关闭的呼叫是clolse(未接来电或拒绝)时,活动未被关闭。 - SOLVED - 解决了
  2. I am not able to click on Activity (I want to put one button there for my app) - SOLVED 我无法点击Activity(我想在我的应用程序中放一个按钮) - 已解决
  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 当我第二次拨打电话时,我的应用停止了(我认为这是因为当电话被解除时,活动没有被关闭) - 已解决

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. 您需要一个单独的视图,在其布局参数中设置LayoutParams.TYPE_SYSTEM_OVERLAY

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. https://bitbucket.org/gyrussolutions/yaab/src/f01cc8aff690cae1b1107287cb17835b8a3c1643/src/biz/gyrus/yaab/LightMonitorService.java?at=default#cl-338 - 完整源代码,请将其视为示例。

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. Sam Adams发布的答案正在为我工​​作,虽然我正在调用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). 它正在开发模拟器以及HTC One S(运行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. 您需要记住的一点是保持对要添加的叠加视图的引用,并在手机恢复空闲时(当侦听器获取TelephonyManager.CALL_STATE_IDLE时)再次将其删除(在windowmanager实例上调用removeView()),否则您的叠加层将保留在屏幕上。

        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    if(overlay!=null)
    {
        wm.removeView(overlay);
        overlay = null;
    }

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. LayoutParams.TYPE_SYSTEM_ERROR创造了不同之处。

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; 
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

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);

}

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();
                }
            });

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM