简体   繁体   中英

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. However the problem occurred when I tested the app on Android oreo, I know that some of the broadcasts have been disabled. I tried to make a foreground service which register a receiver and listen to this broadcasts, yet again it worked on versions below 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

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

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

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.

Edit : the solution was to mix a bunch of the answers until it worked on every device. I wrote the decleration manifest like this :

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

i also created a 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 :-

<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. 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. when the the User is dealing with the actions of BroadcasteReciever from Activity. (The Reciever needs to registerReceiver() in onResume() && unregisterReceiver() in 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? 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. If the problem still persists, try to make a toast as first line of onReceive() method in broadcast receiver. 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.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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