52

我正在为Android中的来电开发一个广播接收器,并且在接到来电时,我想在本地来电屏幕上弹出一个弹出窗口。

我完成了那个代码。但现在的问题是,在Android 4.1 (Jelly Bean) API 级别 17中,当电话响起时,将PHONE_STATE作为OFF HOOK,如果我正在调用一个活动,它会被调用,但它下面的代码不会被执行。我列出了代码:

我的广播接收器

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

我正在调用的活动:

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

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

该代码不在 Android 4.1 (Jelly Bean) 中执行,但在其他版本中它正在运行。

我已经尝试了几乎所有我能做到的方法。此代码在本机呼叫屏幕上显示半透明活动,并且它不会阻止背景控件,例如拿起电话。但我希望它像真正的来电者一样。我附上了关于真正来电者如何在来电屏幕上显示窗口的快照。

如何为 Android 应用程序实现此功能?

这就是真正的调用者的工作方式:

在此处输入图像描述

我现在的输出:

在此处输入图像描述

更新 1

在赏金之后,我也没有得到我正在寻找的确切东西,但我会回复所有人;我正在努力。无论如何,此代码适用于大多数 Android 手机。如果有人要使用并捕获它的解决方案,请在此处写下,以便每个人都能从中受益。

更新 2

我尝试在广播接收器的 onReceive 方法中实现 Toast,因为 toast 是 Android 的本机组件,但它也没有在 Android 4.1 (Jelly Bean) 中显示。

我的想法是在广播接收器的 onReceive 方法中实现 Toast,然后根据我们的需要更改其设计并调整其显示持续时间。但另一个问题是findViewById在广播接收器中不起作用,所以我认为我们必须以编程方式制作一个 LinearLayout 来自定义 toast。

4

11 回答 11

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.

于 2013-04-20T05:32:55.480 回答
9

在方法之前尝试代码super.onCreate。我认为在调用 super 之后代码会被跳过。有时这种技巧对我有用。

于 2013-04-18T10:14:27.750 回答
9

我刚刚在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);
}

在清单中:

<receiver android:name=""  android:enabled="true" >
    <intent-filter android:priority="-1">
        <action android:name="android.intent.action.PHONE_STATE" />
    </intent-filter>
</receiver>
于 2013-05-18T06:38:15.927 回答
7

我也在努力(我在​​这里理解你可能是错误的)。您想要实现的是在 Android 4.2 (Jelly Bean) 中显示该活动。我只是延迟显示活动。我在不同的班级中使用过 PhoneStateListener。我能够在呼叫者屏幕上显示新活动。这是我的完整代码:

在此处输入图像描述

文件 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);
    }
}

文件 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;
        }
    }

并且 YourActivity 将保留为您创建的...注意:我在此代码中也遇到了一些问题,它们在这里。

  1. 当呼叫关闭时(未接来电或被拒绝),活动不会被关闭。
  2. 我无法点击 Activity(我想为我的应用放一个按钮)
  3. 它只在第一次工作。当我第二次拨打电话时,我的应用程序停止了(我认为这是因为呼叫被解除时 Activity 没有被关闭)

(帮助接受这些问题。谢谢。可能会帮助一些人)

更新

这是如何实现此目标的小演示链接。

  1. 当呼叫关闭时(未接来电或被拒绝),活动不会被关闭。- 解决了
  2. 我无法点击活动(我想为我的应用程序放一个按钮) - 已解决
  3. 它只在第一次工作。当我第二次拨打电话时,我的应用程序停止了(我认为这是因为呼叫被解除时 Activity 没有被关闭) - 已解决
于 2013-06-15T07:22:30.187 回答
6

我认为你不应该开始活动来达到所描述的结果。LayoutParams.TYPE_SYSTEM_OVERLAY您需要一个在其布局参数中设置的单独视图。

您可以将此视图放置在屏幕上的任何位置,或仅覆盖整个屏幕。

下面是几行代码:

 _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 - 完整的源代码,将其视为示例。

于 2013-04-20T07:01:38.177 回答
6

我正在尝试类似的方法,在来电屏幕上添加一个额外的按钮。

尽管我从 PhoneStateListener 调用代码,但 Sam Adams 发布的答案对我有用。除此之外,他的代码唯一真正的区别是我正在夸大一个布局:

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

它适用于模拟器以及 HTC One S(运行 Android 4.1.1)。

您需要记住的是保留对您正在添加的覆盖视图的引用,并在电话回到空闲状态(当侦听器获取 TelephonyManager.CALL_STATE_IDLE 时)再次删除它(在 windowmanager 实例上调用 removeView()),否则您的叠加层将留在屏幕上。

        WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    if(overlay!=null)
    {
        wm.removeView(overlay);
        overlay = null;
    }
于 2013-08-02T04:04:44.950 回答
1

尝试这个

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();
                }
            });
于 2018-01-24T09:10:26.100 回答
1

我们也遇到了类似的问题,即覆盖没有显示在带有 pin 锁的设备上。对我们有用的解决方案如下:

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

正是它LayoutParams.TYPE_SYSTEM_ERROR造成了差异。

于 2015-10-14T22:47:20.333 回答
0

我的方法:

  1. 使用接收器接收电话事件
  2. 使用服务制作覆盖

    ps:wmParams.type = WindowManager.LayoutParams.TYPE_PHONE; 
    
于 2013-10-28T09:51:41.810 回答
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
于 2016-06-26T06:51:55.177 回答
0

使用一个简单的广播接收器并将此代码放入广播接收器中:

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

}
于 2016-07-15T11:17:55.983 回答