如何將消息發送給Whatsapp聯系人
(由於本人喜歡word文檔編輯,不喜歡網絡編輯,所以仍然提供pdf版文檔,方便查閱http://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有了更進一步的認識了。