Search code examples
androidandroid-intentbroadcastreceiverandroid-serviceandroid-8.0-oreo

Broadcast Receivers not working in background in android oreo and pie


In my app, I have used broadcast receivers to fetch incoming phone number and show it in a dialog box and then save it in the app on button click. The number is get after every call is ended. All android versions below Oreo and Pie are working fine. In Oreo and Pie, dialog box doen not appear after ending call when the app is closed/killed. But when app is running dialog box appears perfectly. I think broadcast receiver is also killed when we kill the app. I have been searching for days but nothing helpful.

Here is some introduction to my code :

Manifest.xml :

<receiver
            android:name="com.softixtechnologies.phonemanager.callhelpers.CallReciever"
        android:enabled="true">
        <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>

MainActivity.java :

public class MainActivity extends AppCompatActivity {

LinearLayout btncat, btnlog, btnIgnore, btnExit;
SwitchCompat checkBoxMissed, checkBoxUnknown ;
boolean isCheckedValue = true;
boolean isCheckedUnknown = true;
DatabaseHelper databaseHelper ;
public static final String SHARED_PREFS = "shared_prefs" ;
public final static int MY_PERMISSIONS_REQUEST_READ_PHONE_STATE = 11;
public static int ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 5469;
public static final int CALL_LOG_PERMISSION = 1 ;
PhoneCallReceiver phoneCallReceiver ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    databaseHelper = new DatabaseHelper(this) ;
    btncat = findViewById(R.id.button_Categories);
    btnlog = findViewById(R.id.button_logs) ;
    btnIgnore = findViewById(R.id.button_Ignore_contacts) ;
    checkBoxMissed= findViewById(R.id.checkbox_missed) ;
    checkBoxUnknown= findViewById(R.id.checkbox_unknown);
    btnExit = findViewById(R.id.button_exit);
    phoneCallReceiver = new PhoneCallReceiver();

    IntentFilter intentFilter = new IntentFilter();
//        intentFilter.addAction("android.intent.action.PHONE_STATE");
//        intentFilter.addAction("android.intent.action.NEW_OUTGOING_CALL");
    IntentFilter intentFilter1 = new IntentFilter("android.intent.action.NEW_OUTGOING_CALL");
    intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
    intentFilter1.addCategory(Intent.CATEGORY_DEFAULT);
    registerReceiver(phoneCallReceiver, intentFilter);
    registerReceiver(phoneCallReceiver, intentFilter1);

    checkPermission();
    requestCallLogPermission();

    //permission check for pie 23-july-2019
    if (ContextCompat.checkSelfPermission(this,Manifest.permission.READ_CALL_LOG) == PackageManager.PERMISSION_GRANTED){
    }else {
        requestCallLogPermission();
    }

    if (ContextCompat.checkSelfPermission(getApplicationContext(),
            Manifest.permission.READ_PHONE_STATE)
            != PackageManager.PERMISSION_GRANTED)
        if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                Manifest.permission.READ_CONTACTS)) {
        } else {
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_PHONE_STATE, Manifest.permission.SYSTEM_ALERT_WINDOW},
                    MY_PERMISSIONS_REQUEST_READ_PHONE_STATE);
        }

    SharedPreferences sharedPreferenceCAT = getSharedPreferences(SHARED_PREFS, MODE_PRIVATE);
    boolean isFirstTimeCAT =sharedPreferenceCAT.getBoolean("isFirstTimeCAT", true);
    if (isFirstTimeCAT){
        try{
        databaseHelper.insertCAT("New Customer");
        databaseHelper.insertCAT("Complaint");
        databaseHelper.insertCAT("Reminder");
        sharedPreferenceCAT.edit().putBoolean("isFirstTimeCAT",false).commit();}catch (Exception e){
            Toast.makeText(this, ""+ e.getMessage(), Toast.LENGTH_SHORT).show();
        }
    }

    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
    final SharedPreferences.Editor editor = preferences.edit();
    checkBoxMissed.setChecked(preferences.getBoolean("checked",false));
    checkBoxMissed.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            isCheckedValue = isChecked;
            editor.putBoolean("checked", isChecked);
            editor.apply();
        }
    });

    SharedPreferences preferencesUnknown = PreferenceManager.getDefaultSharedPreferences(this);
    final SharedPreferences.Editor editorUnknown = preferencesUnknown.edit();
    checkBoxUnknown.setChecked(preferencesUnknown.getBoolean("checkedunknown",false));
    checkBoxUnknown.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            isCheckedUnknown = isChecked;
            editorUnknown.putBoolean("checkedunknown", isChecked);
            editorUnknown.apply();
        }
    });
private void requestCallLogPermission() {
    if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CALL_LOG)){
        new AlertDialog.Builder(this).setTitle("Permission Needed")
                .setMessage("This permission is needed by App to work properly !")
                .setPositiveButton("OK", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MainActivity.this, new String[] {Manifest.permission.READ_CALL_LOG},
                                CALL_LOG_PERMISSION);
                    }
                }).setCancelable(false)
                .create().show();
    }else {
        ActivityCompat.requestPermissions(this, new String[] {Manifest.permission.READ_CALL_LOG},
                CALL_LOG_PERMISSION);
    }
}

@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
    super.onRequestPermissionsResult(requestCode, permissions, grantResults);
    switch(requestCode){
        case MY_PERMISSIONS_REQUEST_READ_PHONE_STATE:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED){

            }else {
                return;
            }
        case CALL_LOG_PERMISSION:
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Call Log Permission Granted !", Toast.LENGTH_SHORT).show();
            }
    }
}
@TargetApi(Build.VERSION_CODES.M)
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE) {
        if (!Settings.canDrawOverlays(this)) {
            // You don't have permission
            checkPermission();
        } else {
            // Do as per your logic
        }

    }
    if (requestCode == CALL_LOG_PERMISSION) {
        if (!Settings.canDrawOverlays(this)) {
            // You don't have permission
            requestCallLogPermission();
        } else {
            // Do as per your logic
        }
    }
}

public void checkPermission() {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
        if (!Settings.canDrawOverlays(this)) {
            Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
                    Uri.parse("package:" + getPackageName()));
            startActivityForResult(intent, ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE);
        }
    }
}

@Override
protected void onStart() {
    super.onStart();
    IntentFilter intentFilter = new IntentFilter("android.intent.action.PHONE_STATE");
    IntentFilter intentFilter1 = new IntentFilter("android.intent.action.NEW_OUTGOING_CALL");
    intentFilter.addCategory(Intent.CATEGORY_DEFAULT);
    intentFilter1.addCategory(Intent.CATEGORY_DEFAULT);
    registerReceiver(phoneCallReceiver, intentFilter);
    registerReceiver(phoneCallReceiver, intentFilter1);
}

@Override
protected void onStop() {
    super.onStop();
//        unregisterReceiver(phoneCallReceiver);
}

@Override
protected void onDestroy() {
    super.onDestroy();
}
}

BroadCast Receiver:

public class PhoneCallReceiver extends BroadcastReceiver {
private static int lastState = TelephonyManager.CALL_STATE_IDLE;
private static Date callStartTime;
private static boolean isIncoming;
private static String savedNumber;

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

    if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.P){
        TelephonyManager tm = (TelephonyManager)context.getSystemService(Service.TELEPHONY_SERVICE);

        switch (tm.getCallState()) {

            case TelephonyManager.CALL_STATE_IDLE:
                Toast.makeText(context, "IDLE", Toast.LENGTH_SHORT).show();
                final String phoneNr= intent.getStringExtra("incoming_number");
                Toast.makeText(context, phoneNr,Toast.LENGTH_LONG).show();

                SharedPreferences preferencesUnknown = PreferenceManager.getDefaultSharedPreferences(context);
                final SharedPreferences.Editor editorUnknown = preferencesUnknown.edit();
                boolean checkedUnknown = preferencesUnknown.getBoolean("checkedunknown", false);
                if (checkedUnknown) {
                    boolean isUnknown = contactExists(context, phoneNr);
                    if (isUnknown == false) {
                        AlertDialog.Builder alerDialog = new AlertDialog.Builder(context);
                        alerDialog.setIcon(R.drawable.calllogo);
                        alerDialog.setTitle(phoneNr);
                        alerDialog.setMessage("Save this number ?");
                        alerDialog.setCancelable(false);
                        alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                Intent intent = new Intent(context, CategoryPhoneActivity.class);
                                intent.putExtra("Phone", phoneNr);
                                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                                context.startActivity(intent);
                            }
                        });
                        alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                dialog.cancel();
                            }
                        });
                        AlertDialog aldialog = alerDialog.create();
                        aldialog.setCanceledOnTouchOutside(false);
                        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                            aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                            aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                        }
                        aldialog.show(); } else {
                        Toast.makeText(context, "Number is already saved", Toast.LENGTH_SHORT).show(); }
                }else{
                    AlertDialog.Builder alerDialog = new AlertDialog.Builder(context);
                    alerDialog.setIcon(R.drawable.calllogo);
                    alerDialog.setTitle(phoneNr);
                    alerDialog.setCancelable(false);
                    alerDialog.setMessage("Save this number ?");
                    alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            Intent intent = new Intent(context, CategoryPhoneActivity.class);
                            intent.putExtra("Phone", phoneNr);
                            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                            context.startActivity(intent);
                        }
                    });
                    alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            dialog.cancel();
                        }
                    });
                    AlertDialog aldialog = alerDialog.create();
                    aldialog.setCanceledOnTouchOutside(false);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                        aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                        aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                    }
                    aldialog.show(); }
                break;
        }
    }else {

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

protected void onIncomingCallStarted(Context ctx, String number, Date start){}

protected void onOutgoingCallStarted(Context ctx, String number, Date start){}

protected void onIncomingCallEnded(Context ctx, String number, Date start, Date end){}

protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end){}

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

public void onCallStateChanged(Context context, int state, String number) {
    if(lastState == state){
        return;
    }
    switch (state) {
        case TelephonyManager.CALL_STATE_RINGING:
            isIncoming = true;
            callStartTime = new Date();
            savedNumber = number;
            onIncomingCallStarted(context, number, callStartTime);
            break;

        case TelephonyManager.CALL_STATE_OFFHOOK:
            if(lastState != TelephonyManager.CALL_STATE_RINGING){
                isIncoming = false;
                callStartTime = new Date();
                onOutgoingCallStarted(context, savedNumber, callStartTime);
            }
            break;

        case TelephonyManager.CALL_STATE_IDLE:
            if(lastState == TelephonyManager.CALL_STATE_RINGING){
                onMissedCall(context, savedNumber, callStartTime);
            }
            else if(isIncoming){
                onIncomingCallEnded(context, savedNumber, callStartTime, new Date());
            }
            else{
                onOutgoingCallEnded(context, savedNumber, callStartTime, new Date());
            }
            break;
    }
    lastState = state;
}

CallReceiver :

public class CallReciever extends PhoneCallReceiver {

DatabaseHelper databaseHelper ;

@Override
protected void onIncomingCallStarted(final Context ctx, final String number, Date start) {
}

@Override
protected void onOutgoingCallStarted(Context ctx, String number, Date start) {
}

@Override
protected void onIncomingCallEnded(final Context ctx, final String number, Date start, Date end) {

        SharedPreferences preferencesUnknown = PreferenceManager.getDefaultSharedPreferences(ctx);
        final SharedPreferences.Editor editorUnknown = preferencesUnknown.edit();
        boolean checkedUnknown = preferencesUnknown.getBoolean("checkedunknown", false);
        if (checkedUnknown) {
            boolean isUnknown = contactExists(ctx, number);
            if (isUnknown == false) {
                AlertDialog.Builder alerDialog = new AlertDialog.Builder(ctx);
                alerDialog.setIcon(R.drawable.calllogo);
                alerDialog.setTitle(number);
                alerDialog.setMessage("Save this number ?");
                alerDialog.setCancelable(false);
                alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(ctx, CategoryPhoneActivity.class);
                        intent.putExtra("Phone", number);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        ctx.startActivity(intent);
                    }
                });
                alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                AlertDialog aldialog = alerDialog.create();
                aldialog.setCanceledOnTouchOutside(false);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                    aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                }
                aldialog.show(); } else {
                Toast.makeText(ctx, "Number is already saved", Toast.LENGTH_SHORT).show(); }
        }else{
            AlertDialog.Builder alerDialog = new AlertDialog.Builder(ctx);
            alerDialog.setIcon(R.drawable.calllogo);
            alerDialog.setTitle(number);
            alerDialog.setCancelable(false);
            alerDialog.setMessage("Save this number ?");
            alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(ctx, CategoryPhoneActivity.class);
                    intent.putExtra("Phone", number);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    ctx.startActivity(intent);
                }
            });
            alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });
            AlertDialog aldialog = alerDialog.create();
            aldialog.setCanceledOnTouchOutside(false);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            }
            aldialog.show(); }
    }
@Override
protected void onOutgoingCallEnded(Context ctx, String number, Date start, Date end) {
}

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

    Toast.makeText(ctx, "Missed call from " + number, Toast.LENGTH_SHORT).show();

    databaseHelper = new DatabaseHelper(ctx);

    if (databaseHelper.searchContact(number)){
        return;
    }

    SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(ctx);
    final SharedPreferences.Editor editor = preferences.edit();
    boolean checked = preferences.getBoolean("checked", false);
    if (checked) {
        SharedPreferences preferencesUnknown = PreferenceManager.getDefaultSharedPreferences(ctx);
        final SharedPreferences.Editor editorUnknown = preferencesUnknown.edit();
        boolean checkedUnknown = preferencesUnknown.getBoolean("checkedunknown", false);
        if (checkedUnknown) {
            boolean isUnknown = contactExists(ctx, number);
            if (isUnknown == false) {
                AlertDialog.Builder alerDialog = new AlertDialog.Builder(ctx);
                alerDialog.setIcon(R.drawable.calllogo);
                alerDialog.setTitle(number);
                alerDialog.setMessage("Save this number ?");
                alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent(ctx, CategoryPhoneActivity.class);
                        intent.putExtra("Phone", number);
                        intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                        ctx.startActivity(intent);
                    }
                });
                alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        dialog.cancel();
                    }
                });
                AlertDialog aldialog = alerDialog.create();
                aldialog.setCanceledOnTouchOutside(false);
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                    aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
                }
                aldialog.show(); } else {
                Toast.makeText(ctx, "Number is already saved", Toast.LENGTH_SHORT).show(); }
        }else{
            AlertDialog.Builder alerDialog = new AlertDialog.Builder(ctx);
            alerDialog.setIcon(R.drawable.calllogo);
            alerDialog.setTitle(number);
            alerDialog.setMessage("Save this number ?");
            alerDialog.setPositiveButton("YES", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    Intent intent = new Intent(ctx, CategoryPhoneActivity.class);
                    intent.putExtra("Phone", number);
                    intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
                    ctx.startActivity(intent);
                }
            });
            alerDialog.setNegativeButton("NO", new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialog, int which) {
                    dialog.cancel();
                }
            });
            AlertDialog aldialog = alerDialog.create();
            aldialog.setCanceledOnTouchOutside(false);
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY); } else {
                aldialog.getWindow().setType(WindowManager.LayoutParams.TYPE_SYSTEM_ALERT);
            }
            aldialog.show(); }
    }else{
        Toast.makeText(ctx, "", Toast.LENGTH_SHORT).show(); }

}


public boolean contactExists(Context context, String number) {
    Uri lookupUri = Uri.withAppendedPath(
            ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
            Uri.encode(number));
    String[] mPhoneNumberProjection = { ContactsContract.PhoneLookup._ID, ContactsContract.PhoneLookup.NUMBER, ContactsContract.PhoneLookup.DISPLAY_NAME };
    Cursor cur = context.getContentResolver().query(lookupUri,mPhoneNumberProjection, null, null, null);
    try {
        if (cur.moveToFirst()) {
            return true;
        }
    } finally {
        if (cur != null)
            cur.close();
    }
    return false;
}
}

Solution

  • Beginning with Android 8.0 (API level 26), the system imposes additional restrictions on manifest-declared receivers.

    If your app targets Android 8.0 or higher, you cannot use the manifest to declare a receiver for most implicit broadcasts (broadcasts that don't target your app specifically). You can still use a context-registered receiver when the user is actively using your app.

    I guess system automatically unregisters your broadcast receivers when you close the app. Solution: Create sticky or foreground service and register your broadcast receiver in service. When you receive broadcast, launch activity and show dialog box from broadcast receiver onReceive.