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.