Android uses BroadcastReceiver to monitor incoming phone calls

1. Demand background

When the customer used our company's Flutter plug-in, he insisted on a call notification function, so he was very speechless. I found some Flutter plug-ins, and found that none of them could realize this function. In the end, it could only be realized through Android. I This front end suffered what it shouldn't.

2. Implementation steps

1. Add permissions

2. Create a class to inherit BroadcastReceiver

3. Dynamic registration and deregistration of broadcast

4. Detect phone status and get phone number

5. Encapsulate a method to obtain the mobile phone address book

6. Determine whether the phone number is included in the address book, and if so, return the note name, otherwise return the phone number

3. Code sharing

1. Registration rights

Add permissions in the AndroidManifest.xml file, you may not need so many permissions, but I added so many just in case.

    <!--监听电话状态-->
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>

    <uses-permission android:name="android.permission.READ_CALL_LOG" />

    <uses-permission android:name="android.permission.WRITE_CONTACTS"/>
    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.GET_ACCOUNTS"/>
    <uses-permission android:name="android.permission.CALL_PHONE" />

2. Create a class to inherit BroadcastReceiver

public class PhoneStateReceiver extends BroadcastReceiver {

    private static final String PHONE_STATE_RECEIVED = "android.intent.action.PHONE_STATE";
    private static int previousState = TelephonyManager.CALL_STATE_IDLE;

    private static PhoneStateReceiver receiver = new PhoneStateReceiver();

    /**
     * 注册
     *
     * @param context
     */
    public static void register(Context context) {
        IntentFilter filter = new IntentFilter();
        filter.setPriority(Integer.MAX_VALUE);
        filter.addAction(PHONE_STATE_RECEIVED);
        context.registerReceiver(receiver, filter);
    }

    /**
     * 注销
     *
     * @param context
     */
    public static void unregister(Context context) {
        context.unregisterReceiver(receiver);
    }

    @Override
    public void onReceive(final Context context, Intent intent) {
        // 来电处理逻辑
    }
}

 3. Dynamic registration and deregistration of broadcast

I am here to register in the ActivityAware life cycle, you can register according to the actual situation, as long as you register when the application is running.

public class MoyoungBlePlugin implements ActivityAware {
    
    private Activity activity;

    @Override
    public void onAttachedToActivity(@NonNull ActivityPluginBinding binding) {
        activity = binding.getActivity();

        // 注册广播
        PhoneStateReceiver.register(activity);
    }

    @Override
    public void onDetachedFromActivityForConfigChanges() {
    }

    @Override
    public void onReattachedToActivityForConfigChanges(@NonNull ActivityPluginBinding binding) {
    }

    @Override
    public void onDetachedFromActivity() {
        // 注销广播
        PhoneStateReceiver.unregister(activity);
        activity = null;
    }

}

 4. Detect phone status and get phone number

public class PhoneStateReceiver extends BroadcastReceiver {


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

    /**
     * 检测电话状态
     *
     * @param context
     * @param intent
     */
    private synchronized void checkPhoneState(Context context, Intent intent) {
        TelephonyManager telephonyManager = (TelephonyManager) context
                .getSystemService(Service.TELEPHONY_SERVICE);
        int callState = telephonyManager.getCallState();
        Log.i(PhoneStateReceiver.class.getName(), "callState: " + callState);
        switch (callState) {
            case TelephonyManager.CALL_STATE_RINGING:   // 电话进来时
                // 获取电话号码
                String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
                IncomingNumberManager.getInstance().sendIncomingNumber(context, incomingNumber);
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:   // 接起电话时
                Log.i("checkPhoneState", "接电话");
                break;
            case TelephonyManager.CALL_STATE_IDLE:      //无任何状态时
                Log.i("checkPhoneState", "无任何状态");
                break;
        }
    }

}

5. Encapsulate a method to obtain the mobile phone address book

public class ContactUtils {

    private ContactUtils() {
    }

    /**
     * 通过号码查询联系人名字
     *
     * @param context
     * @param number
     * @return
     */
    public static String getContactName(Context context, String number) {
        // 判断是否有通讯录权限
        if (!hasSelfPermissions(context, Manifest.permission.READ_CONTACTS)) {
            return number;
        }

        if (TextUtils.isEmpty(number)) {
            return null;
        }

        final ContentResolver resolver = context.getContentResolver();

        Uri lookupUri;
        String[] projection = new String[]{ContactsContract.PhoneLookup._ID,
                ContactsContract.PhoneLookup.DISPLAY_NAME};
        Cursor cursor = null;
        try {
            lookupUri = Uri.withAppendedPath(ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
                    Uri.encode(number));
            cursor = resolver.query(lookupUri, projection, null, null, null);
        } catch (Exception ex) {
            ex.printStackTrace();
            try {
                lookupUri = Uri.withAppendedPath(
                        android.provider.Contacts.Phones.CONTENT_FILTER_URL,
                        Uri.encode(number));
                cursor = resolver.query(lookupUri, projection, null, null, null);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }

        String ret = null;
        if (cursor != null && cursor.moveToFirst()) {
            ret = cursor.getString(1);
            cursor.close();
        }

        return ret;
    }

    public static boolean hasSelfPermissions(Context context, String... permissions) {
        for (String permission : permissions) {
            if (permissionExists(permission) && !hasSelfPermission(context, permission)) {
                return false;
            }
        }
        return true;
    }

    private static boolean permissionExists(String permission) {
        // Check if the permission could potentially be missing on this device
        Integer minVersion = MIN_SDK_PERMISSIONS.get(permission);
        // If null was returned from the above call, there is no need for a device API level check for the permission;
        // otherwise, we check if its minimum API level requirement is met
        return minVersion == null || Build.VERSION.SDK_INT >= minVersion;
    }

    @SuppressLint("WrongConstant")
    private static boolean hasSelfPermission(Context context, String permission) {
        try {
            if (permission.equals(PERMISSION_UPDATE_BAND_CONFIG[0])) {//android 11需要单独判断存储权限
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {//30 android 11
                    return Environment.isExternalStorageManager();
                }
            }

            return PermissionChecker.checkSelfPermission(context, permission) == PackageManager.PERMISSION_GRANTED;
        } catch (RuntimeException t) {
            return false;
        }
    }

}

6. Determine whether the phone number is included in the address book, and if so, return the note name, otherwise return the phone number

public class IncomingNumberManager {

    private IncomingNumberManager() {
    }

    public static IncomingNumberManager getInstance() {
        return Holder.INSTANCE;
    }

    private static class Holder {
        private static final IncomingNumberManager INSTANCE = new IncomingNumberManager();
    }

    /**
     * 来电号码
     *
     * @param context
     * @param number
     */
    public void sendIncomingNumber(Context context, final String number) {
        if (TextUtils.isEmpty(number)) {
            return;
        }

        String contactName = null;
        try {
            contactName = ContactUtils.getContactName(context, number);
        } catch (Exception e) {
            e.printStackTrace();
        }

        if (!TextUtils.isEmpty(contactName)) {
            // 如果通讯录中有该电话号码的逻辑
        } else {
            // 如果没有该电话号码的逻辑
        }
    }


    /**
     * 挂断电话
     */
    public void endCall() {
        // 挂断电话的逻辑
    }
}

Note: When using it, you must enable the phone, contacts and call history permissions of the application, otherwise you will not be able to monitor it.

4. Reference text

Realization of the functions of making calls, monitoring incoming calls, and monitoring outgoing calls in android

Android reads the name and phone number of the contact_Android related information of the system contact database above, write a program to read the name of the system contact_NULL____'s blog-CSDN blog

Android get address book - short book

Guess you like

Origin blog.csdn.net/m0_68349563/article/details/130559232