如何将消息发送给Whatsapp联系人
(由于本人喜欢word文档编辑,不喜欢网络编辑,所以仍然提供pdf版文档,方便查阅https://files.cnblogs.com/franksunny/send_msg_to_Whatsapp.pdf)
Whatsapp官网上没有找到在Android上进行消息发送相关的信息,但是有一个iOS相关的帖子https://www.whatsapp.com/faq/iphone/23559013,原以为用它的URL在Android上也是可以使用的,结果试了下不行,看来错误地把URL当作URI了。
基础功能实现
后来根据Android Intent和Intent Filters的官方文档说明(如果英文不太好,参考中文的翻译文档,链接为下面的第二个地址):
http://developer.android.com/guide/components/intents-filters.html
http://wiki.eoe.cn/page/Intents_and_Intent_Filters.html#comment
就想着查看了下Whatsapp的Manifest配置文件,看看能不能找出点蛛丝马迹,结果发现有如下的Activity信息
<activity
android:name=".ContactPicker"
android:configChanges="0x00000FB0"
>
<intent-filter
>
<action
android:name="android.intent.action.PICK"
>
</action>
<category
android:name="android.intent.category.DEFAULT"
>
</category>
<category
android:name="com.whatsapp"
>
</category>
</intent-filter>
<intent-filter
>
<action
android:name="android.intent.action.CREATE_SHORTCUT"
>
</action>
</intent-filter>
<intent-filter
>
<action
android:name="android.intent.action.SEND"
>
</action>
<category
android:name="android.intent.category.DEFAULT"
>
</category>
<data
android:mimeType="audio/*"
>
</data>
<data
android:mimeType="video/*"
>
</data>
<data
android:mimeType="image/*"
>
</data>
<data
android:mimeType="text/plain"
>
</data>
<data
android:mimeType="text/x-vcard"
>
</data>
</intent-filter>
<intent-filter
>
<action
android:name="android.intent.action.SEND_MULTIPLE"
>
</action>
<category
android:name="android.intent.category.DEFAULT"
>
</category>
<data
android:mimeType="audio/*"
>
</data>
<data
android:mimeType="video/*"
>
</data>
<data
android:mimeType="image/*"
>
</data>
</intent-filter>
</activity>
至于是不是这个Activity,简单写个测试就验证了,既然找到了这个对象,怎么将信息发送进去,继续google下“com.whatsapp”和“com.whatsapp.ContactPicker”字符串,结果就发现如下一些热帖:
http://stackoverflow.com/questions/6394173/androidpick-action-in-intent
http://stackoverflow.com/questions/19081654/send-to-specific-contact-whatsapp
http://stackoverflow.com/questions/15462874/sending-message-through-whatsapp
从中发现如果想要发送文字信息给Whatsapp进行Whatsapp内消息的发送,可以利用的intent中两个缺省的字段Intent.EXTRA_TEXT和Intent.EXTRA_SUBJECT,于是就按照“android.intent.action.SEND”这个Intent Filter做下如下的代码尝试。
public static boolean sendWhatsApp(Context ctx, String text){
boolean sendOk = false;
if(checkApkExist(ctx, "com.whatsapp")){
Intent vIt = new Intent("android.intent.action.SEND");
vIt.setPackage("com.whatsapp");
vIt.setType("text/plain");
if(!Util.IsNullOrEmpty(text)){
vIt.putExtra(Intent.EXTRA_TEXT, "This is a simple test");
vIt.putExtra(Intent.EXTRA_SUBJECT, "Subject");
}
ctx.startActivity(vIt);
sendOk = true;
}
return sendOk;
}
private static boolean checkApkExist(Context ctx, String packageName) {
if (packageName == null || "".equals(packageName))
return false;
try {
ApplicationInfo info = ctx.getPackageManager().getApplicationInfo(packageName, PackageManager.GET_UNINSTALLED_PACKAGES);
if(info != null){
return true;
}else{
return false;
}
}catch (NameNotFoundException e){
return false;
}
}
上述的checkApkExist方法是用于判断当前是否有安装相应包程序的,发送效果如下:
假如上述代码中,去除vIt.setPackage("com.whatsapp");这段代码之后,就会出现一个选择框的效果,这个就和很多程序中做分享的效果一致了,下面选择环聊做了简单测试。
假如要发图片的话也可以通过分享Deja头像的方式,来实现,下面将代码简单整理了下,就先省略了具体的判断代码:
public static void shareImageToWhatsapp(Activity ctx, Bitmap shareBitmap, String subject, String text) {
Intent intent = new Intent(Intent.ACTION_SEND);
intent.setPackage("com.whatsapp");
intent.setType("image/*");
Uri imageUri = Uri.parse(MediaStore.Images.Media.insertImage(ctx.getContentResolver(), shareBitmap, null, null));
intent.putExtra(Intent.EXTRA_SUBJECT, subject);
intent.putExtra(Intent.EXTRA_TEXT, text);
intent.putExtra(Intent.EXTRA_STREAM, imageUri);
ctx.startActivity(Intent.createChooser(intent, ctx.getTitle()));
}
扩展深入
以前对Intent和Intent Filters可以说是一知半解的,对于官方的提供的上面的那个API文档,也没有吃透,不过经过这次的机会,对于Intent和Intent Filter可以说有了一个比较进一步的认识。
Intent分为显式和隐式两种,简而言之,显式就是已经明显告知了所要调用的组件名称,而隐式是没有明确告知组件名称的调用方式,所以说显式调用时虽然也是通过Intent,但是显式调用不进行Intent Filter条件过滤;隐式调用组件时,就要通过组建在Manifest中设置的Intent Filter来进行匹配了。
显式调用有两种,一种是程序内部明确知道是具体的Activity,然后类似下述的直接调用:
startActivity(new Intent(this, ShareTargetActivity.class));
这种调用显然是能够通过lib或源码import到现有文件的方式。另外一种显式调用就是通过Intent的setComponent方法来显示调用,比如我们已经知道Whatsapp的组件名了,我们就可以通过以下方式调用:
public static boolean sendWhatsApp(Context ctx, String text){
boolean sendOk = false;
if(checkApkExist(ctx, "com.whatsapp")){
Intent vIt = new Intent();
ComponentName comp = new ComponentName( "com.whatsapp", "com.whatsapp.ContactPicker");
vIt.setComponent(comp);
if(!Util.IsNullOrEmpty(text)){
vIt.putExtra(Intent.EXTRA_TEXT, text);
vIt.putExtra(Intent.EXTRA_SUBJECT, "Subject\n");
}
ctx.startActivity(vIt);
sendOk = true;
}
return sendOk;
}
显式Intent时,压根就不用设置任何跟Intent Filter相关的参数,如果写了反而证明是不明真相的画蛇添足。
隐式Intent的调用就必须查看Manifest里面的配置了,而且根据API文档的说明,隐式Intent在进行过滤时,action、category和、data是必须进行匹配的,至于extra和flag选项只是负责传参数,不作为过滤条件。而一个Intent Filter只有一个action,category则是可以叠加的,至于data还是看API文档吧,不做过多展看了,通常我们都是忽略掉category,在这里正好给我们提供了一个category的例子,因为Whatsapp里面的第一个Intent Filter,即如下内容
<intent-filter
>
<action
android:name="android.intent.action.PICK"
>
</action>
<category
android:name="android.intent.category.DEFAULT"
>
</category>
<category
android:name="com.whatsapp"
>
</category>
</intent-filter>
我们可以通过下述方法进行隐式调用,就可以通过这个Filter的过滤,将参数正确传给ContactPicker这个Activity,以下给出简单代码:
public static boolean sendWhatsApp(Context ctx, String text) {
boolean sendOk = false;
if (checkApkExist(ctx, "com.whatsapp")) {
Intent vIt = new Intent("android.intent.action.PICK");
vIt.addCategory("com.whatsapp");
// vIt.setType("text/plain");
if (!Util.IsNullOrEmpty(text)) {
vIt.putExtra(Intent.EXTRA_TEXT, text);
vIt.putExtra(Intent.EXTRA_SUBJECT, "Subject\n");
}
ctx.startActivity(vIt);
sendOk = true;
}
return sendOk;
}
注意在上述这个例子中,注释掉的setType代码是必须注释的,否则程序将因为找不到Activity组件而报android.content.ActivityNotFoundException的异常错误。
同样我们也不能通过以下方式进行调用
public static boolean sendWhatsApp(Context ctx, String text){
boolean sendOk = false;
if(checkApkExist(ctx, "com.whatsapp")){
Intent vIt = new Intent("android.intent.action.SEND_MULTIPLE");
vIt.setPackage("com.whatsapp");
vIt.setType("text/plain");
if(!Util.IsNullOrEmpty(text)){
vIt.putExtra(Intent.EXTRA_TEXT, text);
vIt.putExtra(Intent.EXTRA_SUBJECT, "Subject");
}
ctx.startActivity(vIt);
sendOk = true;
}
return sendOk;
}
对照下Manifest,应该就一目了然了,总体上,通过这个例子,应该能对Intent和Intent Filter有了更进一步的认识了。