从上一篇获取符合默认短信应用来看,获取思路是正确的,但是还很不完善,但是这归根结底是google整出来的一套规范,如何去找到符合规范的应用呢,这得google告诉我们。
因此,我看了一下android源码,关键是SmsApplication中的SmsApplicationData, 里面有个函数叫getApplicationCollection(Context context),这就是android4.4中系统用于获取符合默认短信应用要求的应用信息的代码,如下:
/** * Returns the list of available SMS apps defined as apps that are registered for both the * SMS_RECEIVED_ACTION (SMS) and WAP_PUSH_RECEIVED_ACTION (MMS) broadcasts (and their broadcast * receivers are enabled) * * Requirements to be an SMS application: * Implement SMS_DELIVER_ACTION broadcast receiver. * Require BROADCAST_SMS permission. * * Implement WAP_PUSH_DELIVER_ACTION broadcast receiver. * Require BROADCAST_WAP_PUSH permission. * * Implement RESPOND_VIA_MESSAGE intent. * Support smsto Uri scheme. * Require SEND_RESPOND_VIA_MESSAGE permission. * * Implement ACTION_SENDTO intent. * Support smsto Uri scheme. */ public static Collection<SmsApplicationData> getApplicationCollection(Context context) { PackageManager packageManager = context.getPackageManager(); // Get the list of apps registered for SMS Intent intent = new Intent(Intents.SMS_DELIVER_ACTION); List<ResolveInfo> smsReceivers = packageManager.queryBroadcastReceivers(intent, 0); HashMap<String, SmsApplicationData> receivers = new HashMap<String, SmsApplicationData>(); // Add one entry to the map for every sms receiver (ignoring duplicate sms receivers) for (ResolveInfo resolveInfo : smsReceivers) { final ActivityInfo activityInfo = resolveInfo.activityInfo; if (activityInfo == null) { continue; } if (!permission.BROADCAST_SMS.equals(activityInfo.permission)) { continue; } final String packageName = activityInfo.packageName; if (!receivers.containsKey(packageName)) { final String applicationName = resolveInfo.loadLabel(packageManager).toString(); final SmsApplicationData smsApplicationData = new SmsApplicationData( applicationName, packageName, activityInfo.applicationInfo.uid); smsApplicationData.mSmsReceiverClass = activityInfo.name; receivers.put(packageName, smsApplicationData); } } // Update any existing entries with mms receiver class intent = new Intent(Intents.WAP_PUSH_DELIVER_ACTION); intent.setDataAndType(null, "application/vnd.wap.mms-message"); List<ResolveInfo> mmsReceivers = packageManager.queryBroadcastReceivers(intent, 0); for (ResolveInfo resolveInfo : mmsReceivers) { final ActivityInfo activityInfo = resolveInfo.activityInfo; if (activityInfo == null) { continue; } if (!permission.BROADCAST_WAP_PUSH.equals(activityInfo.permission)) { continue; } final String packageName = activityInfo.packageName; final SmsApplicationData smsApplicationData = receivers.get(packageName); if (smsApplicationData != null) { smsApplicationData.mMmsReceiverClass = activityInfo.name; } } // Update any existing entries with respond via message intent class. intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, Uri.fromParts("smsto", "", null)); List<ResolveInfo> respondServices = packageManager.queryIntentServices(intent, 0); for (ResolveInfo resolveInfo : respondServices) { final ServiceInfo serviceInfo = resolveInfo.serviceInfo; if (serviceInfo == null) { continue; } if (!permission.SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) { continue; } final String packageName = serviceInfo.packageName; final SmsApplicationData smsApplicationData = receivers.get(packageName); if (smsApplicationData != null) { smsApplicationData.mRespondViaMessageClass = serviceInfo.name; } } // Update any existing entries with supports send to. intent = new Intent(Intent.ACTION_SENDTO, Uri.fromParts("smsto", "", null)); List<ResolveInfo> sendToActivities = packageManager.queryIntentActivities(intent, 0); for (ResolveInfo resolveInfo : sendToActivities) { final ActivityInfo activityInfo = resolveInfo.activityInfo; if (activityInfo == null) { continue; } final String packageName = activityInfo.packageName; final SmsApplicationData smsApplicationData = receivers.get(packageName); if (smsApplicationData != null) { smsApplicationData.mSendToClass = activityInfo.name; } } // Remove any entries for which we did not find all required intents. for (ResolveInfo resolveInfo : smsReceivers) { final ActivityInfo activityInfo = resolveInfo.activityInfo; if (activityInfo == null) { continue; } final String packageName = activityInfo.packageName; final SmsApplicationData smsApplicationData = receivers.get(packageName); if (smsApplicationData != null) { if (!smsApplicationData.isComplete()) { receivers.remove(packageName); } } } return receivers.values(); } /** * Checks to see if we have a valid installed SMS application for the specified package name * @return Data for the specified package name or null if there isn‘t one */ private static SmsApplicationData getApplicationForPackage( Collection<SmsApplicationData> applications, String packageName) { if (packageName == null) { return null; } // Is there an entry in the application list for the specified package? for (SmsApplicationData application : applications) { if (application.mPackageName.contentEquals(packageName)) { return application; } } return null; }
从源码的实现来看,我上一篇关于这个功能的blog思路相当正确,只不过没用让它几个条件同时满足,因此,若我们需要获取符合默认短信应用要求的短信应用包名的话,我们只要原原本本地把源码搞出来用就行了
由于这个是隐藏的类和函数,我们获取不到,用反射又会相当麻烦,直接把实现给拷出来不失为一个良策。
要判断拿到的collection中的某一个是不是完全符合默认短信应用的要求怎么办呢?调用一下SmsApplication.isComplete()就OK了。