电话状态的广播接收器更改无效

时间:2018-09-17 14:18:39

标签: android broadcastreceiver android-broadcastreceiver telephony telephonymanager

我已经创建了一个用于更改电话状态的广播接收器。但广播无法正常工作。我已经尝试了几个小时并尝试了2,3个解决方案,但仍然无法正常工作。互联网上的其他人具有相同的代码,对于他们来说工作正常。我不知道我在哪里犯错。需要你的帮助! 这是我的清单文件

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

这是我的PhoneCallReceiver类

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

3 个答案:

答案 0 :(得分:5)

您无法再通过这种方式接收PHONE_STATE_CHANGED广播。

摘自官方Android开发人员指南https://developer.android.com/guide/components/broadcasts

  

从Android 8.0(API级别26)开始,系统强加   对清单声明的接收者的其他限制。

     

如果您的应用程序针对Android 8.0或更高版本,则不能使用清单   声明大多数隐式广播的接收方(   (不专门针对您的应用)。您仍然可以使用   用户正在积极使用您的应用程序时,上下文注册的接收器。

您必须使用显式广播接收器(从您的活动中注册)来接收PHONE_STATE_CHANGED广播。

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

此外,除了在清单中声明诸如android.permission.READ_PHONE_STATEandroid.permission.PROCESS_OUTGOING_CALLS之类的必需权限外,还必须在运行时从用户显式获得这些权限。否则,您将不会收到某些(大多数)系统广播。 Android开发人员指南很好地解释了如何向用户和代码示例请求权限。 https://developer.android.com/training/permissions/requesting

答案 1 :(得分:1)

  1. 首先,在清单中定义权限

    <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. 在应用标签内注册接收者

    <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. 向启动活动/ 运行时权限询问权限,通常在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. 在PhoneCallReciver类内部

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

答案 2 :(得分:0)

我在清单中定义的案例电话状态权限(android.permission.READ_PHONE_STATE)还不够,因此当我从应用设置手动授予应用权限时,它开始接收Phone_State广播。我认为需要用户的运行时许可。