简体   繁体   English

听取调用Android手机中的广播接收器

[英]Listen to calls Broadcast receiver in Android pie

I'm trying to make an app listen to calls (incoming and outgoing), what I have done for now is just register the receiver in the manifest, which worked perfectly in Android below 9.0. 我正在尝试让应用程序听取呼叫(传入和传出),我现在所做的只是在清单中注册接收器,这在Android 9.0以下完美运行。 However the problem occurred when I tested the app on Android oreo, I know that some of the broadcasts have been disabled. 然而,当我在Android oreo上测试应用程序时出现问题,我知道有些广播被禁用了。 I tried to make a foreground service which register a receiver and listen to this broadcasts, yet again it worked on versions below oreo. 我尝试制作一个前台服务,注册一个接收器并收听这个广播,但它再次在oreo下面的版本上工作。

I don't know what else to do, I have to get it work, I don't care if it will be in an inelegant way, I don't post this app I just need it for private use. 我不知道还能做什么,我必须让它工作,我不在乎它是否会以一种不雅的方式,我不发布这个应用程序我只是需要私人使用。

Summary: I need to listen to calls in Android pie even when the app is not currently active 简介:即使应用程序当前未处于活动状态,我也需要在Android饼中收听电话

manifest 表现

<receiver android:name=".MyReceiver">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE" />
        </intent-filter>
        <intent-filter>
            <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
        </intent-filter>
    </receiver>

MyReceiver MyReceiver

public class MyReceiver extends BroadcastReceiver {


private static Date callStartTime;
private static boolean isIncoming;
private static int lastState;
private static String savedNumber;
private static String savedOutgoingNumber;
private TelephonyManager tm;


@Override
public void onReceive(Context context, Intent intent) {

    //We listen to two intents.  The new outgoing call only tells us of an outgoing call.  We use it to get the number.
    if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
        savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
    }
    else{
        String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
        String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
        int state = 0;
        if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)){
            state = TelephonyManager.CALL_STATE_IDLE;
        }
        else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
            state = TelephonyManager.CALL_STATE_OFFHOOK;
        }
        else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
            state = TelephonyManager.CALL_STATE_RINGING;
        }


        onCallStateChanged(context, state, number);
    }
}

'

The problem is that it isnt even getting called on android oreo Edit : I got it to work on android oreo but it have problems in android pie 问题是它甚至没有在android oreo上调用编辑:我让它在android oreo上工作但它在android pie中有问题

Another edit : 另一个编辑:

  @Override
public void onCreate() {
    super.onCreate();

    createNotificationChannel();

    IntentFilter filter = new IntentFilter();
    filter.addAction("android.intent.action.PHONE_STATE");
    filter.addAction("android.intent.action.NEW_OUTGOING_CALL");

    BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
                CallListenerService.this.onReceive(intent);
        }
    };
    registerReceiver(receiver, filter);


}


public void onReceive(Intent i){
    if (i.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
        savedNumber = i.getExtras().getString("android.intent.extra.PHONE_NUMBER");
    }
    else{
        String stateStr = i.getExtras().getString(TelephonyManager.EXTRA_STATE);
        String number = i.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
        int state = 0;
        if(stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)){
            state = TelephonyManager.CALL_STATE_IDLE;
        }
        else if(stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)){
            state = TelephonyManager.CALL_STATE_OFFHOOK;
        }
        else if(stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)){
            state = TelephonyManager.CALL_STATE_RINGING;
        }


        onCallStateChanged(this, state, number);
    }
}

The rest doesnt matter it just start an activity on call end to test if it wokrs 其余的并不重要,只需在呼叫结束时启动一个活动,以测试它是否吵闹

I have added an fourground service which register recievers , This works on android oreo but still have issues on android pie. 我已经添加了一个注册接收器的四地服务,这适用于android oreo但仍然在android pie上有问题。

Edit : the solution was to mix a bunch of the answers until it worked on every device. 编辑:解决方案是混合一堆答案,直到它在每个设备上工作。 I wrote the decleration manifest like this : 我写了这样的decleration清单:

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

don't really know if that what did it. 真的不知道那是做了什么的。 Further more I used the normal broad cast receiver and not a service, again I don't know what happend but suddenly it worked on some devices. 我使用普通的广播接收器而不是服务,我不知道发生了什么,但突然它在某些设备上工作。

now there is the final touch, I found out that I can get the phone number by reading the call log, so what I did was to add this code : 现在有最后的触摸,我发现我可以通过阅读通话记录获取电话号码,所以我做的是添加此代码:

   protected void onMissedCall(final Context ctx, String number, Date start) {


    if (PreferenceManager.getDefaultSharedPreferences(ctx).getBoolean("missed_calls", false)) {

        if (number == null) {

            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    final String phoneNumber = getLastCallNumber(ctx);
                    if (phoneNumber != null) {
                        Intent intent = new Intent(ctx, Dialog.class);
                        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        intent.putExtra("number", phoneNumber);
                        intent.putExtra("missed_call", true);
                        ctx.startActivity(intent);
                    } else {
                        Toast.makeText(ctx, "cannot get phone number", Toast.LENGTH_SHORT).show();
                    }
                }
            }, 2000);

        } else {
            Intent intent = new Intent(ctx, Dialog.class);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.putExtra("number", number);
            intent.putExtra("missed_call", true);
            ctx.startActivity(intent);
        }
    }


}

private String getLastCallNumber(Context context) {

    Uri contacts = CallLog.Calls.CONTENT_URI;
    if (ActivityCompat.checkSelfPermission(context, android.Manifest.permission.READ_CALL_LOG) != PackageManager.PERMISSION_GRANTED) {
        Toast.makeText(context, "Permission denied can not get phone number", Toast.LENGTH_SHORT).show();
    }
    Cursor managedCursor = context.getContentResolver().query(
            contacts, null, null, null, null);
    int number = managedCursor.getColumnIndex( CallLog.Calls.NUMBER );
    String phoneNumber = null;
    if( managedCursor.moveToLast() == true ) {
        phoneNumber = managedCursor.getString( number );
    }
    managedCursor.close();




    return phoneNumber;

}

I added the if line which checks if the number is null, and if so it launch an handler to run 2 seconds from than, when the handler starts I read the last number from the call log. 我添加了if行来检查数字是否为null,如果是,则启动处理程序运行2秒,当处理程序启动时,我从调用日志中读取最后一个数字。 The 2 seconds delay is for the number to get into the call log, if you try to read it right away it doesn't work. 2秒延迟是进入通话记录的数字,如果你试图立即读取它不起作用。

i also created a CallReceiver :- 我还创建了一个CallReceiver: -

public class CallReceiver extends BroadcastReceiver {

    private static int lastState = TelephonyManager.CALL_STATE_IDLE;
    private static Date callStartTime;
    private static boolean isIncoming;
    private static String savedNumber;
    public static MyCallsAppDatabase myCallsAppDatabase;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
            savedNumber = intent.getExtras().getString("android.intent.extra.PHONE_NUMBER");
        } else {
            String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
            String number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
            int state = 0;
            if (stateStr.equals(TelephonyManager.EXTRA_STATE_IDLE)) {
                state = TelephonyManager.CALL_STATE_IDLE;
            } else if (stateStr.equals(TelephonyManager.EXTRA_STATE_OFFHOOK)) {
                state = TelephonyManager.CALL_STATE_OFFHOOK;
            } else if (stateStr.equals(TelephonyManager.EXTRA_STATE_RINGING)) {
                state = TelephonyManager.CALL_STATE_RINGING;
            }
            onCallStateChanged(context, state, number, intent);
        }
    }

    public void onCallStateChanged(Context context, int state, String number, Intent intent) {
        if (lastState == state) {
            //No change, debounce extras
            return;
        }
        switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                isIncoming = true;
                callStartTime = new Date();
                onIncomingCallStarted(context, number, callStartTime, intent);
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                //Transition of ringing->offhook are pickups of incoming calls.  Nothing done on them
                if (lastState != TelephonyManager.CALL_STATE_RINGING) {
                    isIncoming = false;
                    callStartTime = new Date();
                    onOutgoingCallStarted(context, number, callStartTime, intent);
                }
                break;
            case TelephonyManager.CALL_STATE_IDLE:
                //Went to idle-  this is the end of a call.  What type depends on previous state(s)
                if (lastState == TelephonyManager.CALL_STATE_RINGING) {
                    //Ring but no pickup-  a miss
                    onMissedCall(context, number, callStartTime, intent);
                } else if (isIncoming) {
                    onIncomingCallEnded(context, number, callStartTime, new Date(), intent);
                } else {
                    onOutgoingCallEnded(context, number, callStartTime, new Date(), intent);
                }
                break;
        }
        lastState = state;
        Intent intent1 = new Intent("CallApp");
        context.sendBroadcast(intent1);
    }

    protected void onIncomingCallStarted(Context ctx, String number, Date start, Intent intent) {
//        Toast.makeText(ctx, "calling from " + number, Toast.LENGTH_SHORT).show();
    }

    protected void onOutgoingCallStarted(Context ctx, String number, Date start, Intent intent) {
//        Toast.makeText(ctx, "calling to " + number, Toast.LENGTH_SHORT).show();
    }

    protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end, Intent intent) {
//        Toast.makeText(ctx, "calling from " + number + " ended ", Toast.LENGTH_SHORT).show();
        saveData(ctx, number, intent, "Incoming Call");
    }

    protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end, Intent intent) {
//        Toast.makeText(ctx, "calling to " + number + " ended ", Toast.LENGTH_SHORT).show();
        saveData(ctx, number, intent, "Outgoing Call");
    }

    protected void onMissedCall(Context ctx, String number, Date start, Intent intent) {
//        Toast.makeText(ctx, "missed call from " + number + " sim ", Toast.LENGTH_SHORT).show();
        saveData(ctx, number, intent, "Missed Call");
    }

    @SuppressLint("ServiceCast")
    protected void saveData(Context ctx, String number, Intent intent, String callType) {

        myCallsAppDatabase = Room.databaseBuilder(ctx, MyCallsAppDatabase.class, "calldb")
                .allowMainThreadQueries()
                .build();

        number = intent.getExtras().getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
        number = filterNumber(number);
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss aaa");
        String dateString = dateFormat.format(new Date(System.currentTimeMillis()));
        CallLog callLog = new CallLog();
        callLog.setMobile(number);
        callLog.setCallType(callType);
        callLog.setTime(dateString);
        myCallsAppDatabase.myCallDao().addCallDetails(callLog);
    }
}

in Manifest.xml file :- 在Manifest.xml文件中: -

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

Change code in Manifest file as below and setPriority to high. 更改Manifest文件中的代码如下,setPriority更高。 I thinks this menifest code needs when the app is in background. 当应用程序处于后台时,我认为这个清单代码需要。

 <receiver android:name=".MyReceiver">
    <intent-filter android:priority="1000">
        <action android:name="android.intent.action.PHONE_STATE" />
        <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
    </intent-filter>
</receiver>

This programatic code needs to wake ups the Reciever when app is foreground ie. 当app是前景时,这个程序代码需要唤醒Reciever。 when the the User is dealing with the actions of BroadcasteReciever from Activity. 当用户正在处理来自Activity的BroadcasteReciever的操作时。 (The Reciever needs to registerReceiver() in onResume() && unregisterReceiver() in onDestroy() ) (接收者需要在onDestroy()中的onResume() && unregisterReceiver()registerReceiver() onDestroy()

 IntentFilter filter = new IntentFilter();
 filter.addAction("android.intent.action.PHONE_STATE");
 filter.addAction("android.intent.action.NEW_OUTGOING_CALL");
 filter.setPriority(1000);

 BroadcastReceiver receiver = new BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
            CallListenerService.this.onReceive(intent);
    }
};
registerReceiver(receiver, filter);

Which android device are you using? 你使用的是哪个Android设备? I had also encountered such an error which broadcasts are received only when the app is running. 我也遇到过这样的错误,只有在应用程序运行时才会收到广播。 Then I discovered that my device is in power saving mode which restricts broadcast and we can customize these settings for specific app in settings> Battery Management. 然后我发现我的设备处于省电模式,限制了广播,我们可以在设置>电池管理中为特定应用自定义这些设置。 My device is Honor 9 lite with android version Pie. 我的设备是荣耀9升与Android版Pie。 If the problem still persists, try to make a toast as first line of onReceive() method in broadcast receiver. 如果问题仍然存在,请尝试在广播接收器中将onast作为onReceive()方法的第一行。 if you receive the toast, your app is receiving broadcasts but problem will be with in your code. 如果您收到吐司,您的应用程序正在接收广播,但问题将出现在您的代码中。

Try to run the same code in different devices. 尝试在不同的设备中运行相同的代码。 May be the problem would be with your device settings. 可能是你的设备设置问题。

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

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