Broadcast receiver for Phone State changed not working

一个人想着一个人 提交于 2020-08-25 07:42:15

问题


i have created a broadcast receiver for Phone state change. but the broadcast is not working. i have been trying from couple of hours and tried 2,3 solutions but still its not working. other guys over internet have same code and the is working fine for them. i don't know where i am making mistake. need your help! Here are my manifest File

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="veclar.map.callandsmsblocking">
    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />

            </intent-filter>
        </activity>
        <receiver android:name=".PhoneCallReceiver" >
            <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>
    </application>
</manifest> 

and here is my PhoneCallReceiver class

public abstract class PhoneCallReceiver extends BroadcastReceiver {

    //The receiver will be recreated whenever android feels like it.  We need a static variable to remember data between instantiations

    private static int lastState = TelephonyManager.CALL_STATE_IDLE;
    private static Date callStartTime;
    private static boolean isIncoming;
    private static String savedNumber;  //because the passed incoming is only valid in ringing

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

    //Incoming call-  goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
    //Outgoing call-  goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
    public void onCallStateChanged(Context context, int state, String number) {
        if(lastState == state){
            //No change, debounce extras
            return;
        }
        switch (state) {
            case TelephonyManager.CALL_STATE_RINGING:
                isIncoming = true;
                callStartTime = new Date();
                savedNumber = number;

                Toast.makeText(context, "Incoming Call Ringing" , Toast.LENGTH_SHORT).show();
                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();
                    Toast.makeText(context, "Outgoing Call Started" , Toast.LENGTH_SHORT).show();
                }

                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
                    Toast.makeText(context, "Ringing but no pickup" + savedNumber + " Call time " + callStartTime +" Date " + new Date() , Toast.LENGTH_SHORT).show();
                }
                else if(isIncoming){

                    Toast.makeText(context, "Incoming " + savedNumber + " Call time " + callStartTime  , Toast.LENGTH_SHORT).show();
                }
                else{

                    Toast.makeText(context, "outgoing " + savedNumber + " Call time " + callStartTime +" Date " + new Date() , Toast.LENGTH_SHORT).show();

                }

                break;
        }

        lastState = state;
    }
}

回答1:


You canno longer receive PHONE_STATE_CHANGED broadcast this way.

From official Android developer guide https://developer.android.com/guide/components/broadcasts :

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.

You must use explicit broadcast receivers (registered from your activity) to receive PHONE_STATE_CHANGED broadcast.

public class ToastDisplay extends Activity {

    private BroadcastReceiver receiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Toast.makeText(getApplicationContext(), "received", Toast.LENGTH_SHORT);
        }
    };

    @Override
    protected void onResume() {
        IntentFilter filter = new IntentFilter();
        filter.addAction("android.intent.action.PHONE_STATE");
        registerReceiver(receiver, filter);
        super.onResume();
    }

    @Override
    protected void onPause() {
        unregisterReceiver(receiver);
        super.onPause();
    }
}

Also, you in addition to declare required permission like android.permission.READ_PHONE_STATE , android.permission.PROCESS_OUTGOING_CALLS in the manifest , you must obtain those permissions explicitly from user at run-time. Otherwise you will not receive some(most) system broadcasts. Android developer guide has a nice explanation on requesting permissions from user and code sample. https://developer.android.com/training/permissions/requesting




回答2:


I my case phone state permission (android.permission.READ_PHONE_STATE) defined in manifest was not enough so when I give permission to app manually from app setting it start receiving Phone_State broadcast. I think run time permission from user is required.




回答3:


  1. First, define permission in the manifest

    <uses-permission android:name="android.permission.RECEIVE_SMS" />
    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
    <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>
    
  2. inside the application tag register the receiver

    <receiver android:name=".PhoneCallReceiver" >
         <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>
    
  3. Ask the permission the startup Activity/Runtime Permission Usually in the MainActivity

    @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.activity_main);
    
           checkAndRequestPermissions();
    
          }
        private  boolean checkAndRequestPermissions() {
     int readPhoneState = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE);
     int read_call_log = ContextCompat.checkSelfPermission(this, Manifest.permission.READ_CALL_LOG);
    
     List listPermissionsNeeded = new ArrayList<>();
    
     if (readPhoneState != PackageManager.PERMISSION_GRANTED) {
         listPermissionsNeeded.add(Manifest.permission.READ_PHONE_STATE);
     }
    
     if (read_call_log != PackageManager.PERMISSION_GRANTED) {
         listPermissionsNeeded.add(Manifest.permission.READ_CALL_LOG);
     }
    
     if (read_call_log != PackageManager.PERMISSION_GRANTED) {
         listPermissionsNeeded.add(Manifest.permission.PROCESS_OUTGOING_CALLS);
     }
    
     if (read_call_log != PackageManager.PERMISSION_GRANTED) {
         listPermissionsNeeded.add(Manifest.permission.INTERNET);
     }
    
     if (!listPermissionsNeeded.isEmpty()) {
         ActivityCompat.requestPermissions(this,
                 (String[]) listPermissionsNeeded.toArray(new String[listPermissionsNeeded.size()]),
                 REQUEST_ID_MULTIPLE_PERMISSIONS);
    
         return false;
     }
     return true;
    }
    
  4. inside the PhoneCallReciver class

     public class CallReceiver extends BroadcastReceiver{
    
    
     @Override
         public void onReceive(Context context, Intent intent) {
    
             try {
    
                 runfirstTime(context,intent);
    
             } catch (Exception ex) {
                 try {
    
                 }
                 catch (Exception e)
                 {
    
                 }
             }
         }
    
    
     private void runfirstTime(Context context, Intent intent) {
    
         TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE);
         MyPhoneStateListener customPhoneListener = new MyPhoneStateListener();
    
         telephony.listen(customPhoneListener, PhoneStateListener.LISTEN_CALL_STATE);
    
         Bundle bundle = intent.getExtras();
         String phone_number = bundle.getString("incoming_number");
    
         String stateStr = intent.getExtras().getString(TelephonyManager.EXTRA_STATE);
         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;
         }
    
         if (phone_number == null || "".equals(phone_number)) {
             return;
         }
         customPhoneListener.onCallStateChanged(context, state, phone_number);
        // Here customPhoneListener is a object of the  use context,state,phone_number to get the MyCustomPhonestatelistener class Which extends PhoneStateListener class.
    
     }
    }
    


来源:https://stackoverflow.com/questions/52369874/broadcast-receiver-for-phone-state-changed-not-working

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!