以编程方式通过 whatsapp 发送消息
Send message via whatsapp programmatically
我正在尝试以编程方式通过 Whatsapp 发送消息,除了用户需要单击发送按钮外,代码有效。我需要应用程序来做所有事情(所有用户交互)。一种方法如下。
Go to Menu Button > Settings > Chats. and check the "Enter is send option"
这是我使用的代码:
protected void sendwts(){
String smsNumber = "2126123456789"; // E164 format without '+' sign
Intent sendIntent = new Intent(Intent.ACTION_SEND);
// Intent sendIntent = new Intent(Intent.ACTION_SENDTO);
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, "test \n");
sendIntent.putExtra("jid", smsNumber + "@s.whatsapp.net"); //phone number without "+" prefix
sendIntent.setPackage("com.whatsapp");
startActivity(sendIntent);
}
谢谢
你只能使用Android的Accessibility API。
这个想法很简单,你实际上会让 Android 执行点击 Whatsapp 的发送按钮。
所以流程将是:
- 发送一条常规消息(包含您当前使用的意图),并在消息内容末尾添加后缀,例如“
Sent by MY_APP
”。
- 一旦文本出现,您的无障碍服务将收到通知,whatsapp 的
EditText
已填满。
- 如果后缀出现在 whatsapp 的
EditText
上,您的无障碍服务将点击发送按钮。 (这是为了避免在用户自然键入常规消息时执行操作)。
这是一个例子(如果你想让它更具限制性,你可以对其进行调整):
public class WhatsappAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent (AccessibilityEvent event) {
if (getRootInActiveWindow () == null) {
return;
}
AccessibilityNodeInfoCompat rootInActiveWindow = AccessibilityNodeInfoCompat.wrap (getRootInActiveWindow ());
// Whatsapp Message EditText id
List<AccessibilityNodeInfoCompat> messageNodeList = rootInActiveWindow.findAccessibilityNodeInfosByViewId ("com.whatsapp:id/entry");
if (messageNodeList == null || messageNodeList.isEmpty ()) {
return;
}
// check if the whatsapp message EditText field is filled with text and ending with your suffix (explanation above)
AccessibilityNodeInfoCompat messageField = messageNodeList.get (0);
if (messageField.getText () == null || messageField.getText ().length () == 0
|| !messageField.getText ().toString ().endsWith (getApplicationContext ().getString (R.string.whatsapp_suffix))) { // So your service doesn't process any message, but the ones ending your apps suffix
return;
}
// Whatsapp send button id
List<AccessibilityNodeInfoCompat> sendMessageNodeInfoList = rootInActiveWindow.findAccessibilityNodeInfosByViewId ("com.whatsapp:id/send");
if (sendMessageNodeInfoList == null || sendMessageNodeInfoList.isEmpty ()) {
return;
}
AccessibilityNodeInfoCompat sendMessageButton = sendMessageNodeInfoList.get (0);
if (!sendMessageButton.isVisibleToUser ()) {
return;
}
// Now fire a click on the send button
sendMessageButton.performAction (AccessibilityNodeInfo.ACTION_CLICK);
// Now go back to your app by clicking on the Android back button twice:
// First one to leave the conversation screen
// Second one to leave whatsapp
try {
Thread.sleep (500); // hack for certain devices in which the immediate back click is too fast to handle
performGlobalAction (GLOBAL_ACTION_BACK);
Thread.sleep (500); // same hack as above
} catch (InterruptedException ignored) {}
performGlobalAction (GLOBAL_ACTION_BACK);
}
}
然后在res -> xml -> whatsapp_service.xml
中创建它的定义:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged"
android:packageNames="com.whatsapp"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"/>
然后在您的清单中声明它:
<service
android:name=".services.WhatsappAccessibilityService"
android:label="Accessibility Service"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/whatsapp_service"/>
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
</service>
最后一件事,是检查您的应用是否启用了辅助功能服务,如果未启用,则将用户重定向到设置:
private boolean isAccessibilityOn (Context context, Class<? extends AccessibilityService> clazz) {
int accessibilityEnabled = 0;
final String service = context.getPackageName () + "/" + clazz.getCanonicalName ();
try {
accessibilityEnabled = Settings.Secure.getInt (context.getApplicationContext ().getContentResolver (), Settings.Secure.ACCESSIBILITY_ENABLED);
} catch (Settings.SettingNotFoundException ignored) { }
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter (":");
if (accessibilityEnabled == 1) {
String settingValue = Settings.Secure.getString (context.getApplicationContext ().getContentResolver (), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
colonSplitter.setString (settingValue);
while (colonSplitter.hasNext ()) {
String accessibilityService = colonSplitter.next ();
if (accessibilityService.equalsIgnoreCase (service)) {
return true;
}
}
}
}
return false;
}
您将使用的电话:
if (!isAccessibilityOn (context, WhatsappAccessibilityService.class)) {
Intent intent = new Intent (Settings.ACTION_ACCESSIBILITY_SETTINGS);
context.startActivity (intent);
}
这纯粹是解决方案的技术方面。
现在,"should you do that?"的伦理问题,相信答案已经很明确了:
除非您针对的是残障人士(这是辅助功能 API 的真正目的),否则您应该 不 那样做。
我正在尝试以编程方式通过 Whatsapp 发送消息,除了用户需要单击发送按钮外,代码有效。我需要应用程序来做所有事情(所有用户交互)。一种方法如下。
Go to Menu Button > Settings > Chats. and check the "Enter is send option"
这是我使用的代码:
protected void sendwts(){
String smsNumber = "2126123456789"; // E164 format without '+' sign
Intent sendIntent = new Intent(Intent.ACTION_SEND);
// Intent sendIntent = new Intent(Intent.ACTION_SENDTO);
sendIntent.setType("text/plain");
sendIntent.putExtra(Intent.EXTRA_TEXT, "test \n");
sendIntent.putExtra("jid", smsNumber + "@s.whatsapp.net"); //phone number without "+" prefix
sendIntent.setPackage("com.whatsapp");
startActivity(sendIntent);
}
谢谢
你只能使用Android的Accessibility API。
这个想法很简单,你实际上会让 Android 执行点击 Whatsapp 的发送按钮。
所以流程将是:
- 发送一条常规消息(包含您当前使用的意图),并在消息内容末尾添加后缀,例如“
Sent by MY_APP
”。 - 一旦文本出现,您的无障碍服务将收到通知,whatsapp 的
EditText
已填满。 - 如果后缀出现在 whatsapp 的
EditText
上,您的无障碍服务将点击发送按钮。 (这是为了避免在用户自然键入常规消息时执行操作)。
这是一个例子(如果你想让它更具限制性,你可以对其进行调整):
public class WhatsappAccessibilityService extends AccessibilityService {
@Override
public void onAccessibilityEvent (AccessibilityEvent event) {
if (getRootInActiveWindow () == null) {
return;
}
AccessibilityNodeInfoCompat rootInActiveWindow = AccessibilityNodeInfoCompat.wrap (getRootInActiveWindow ());
// Whatsapp Message EditText id
List<AccessibilityNodeInfoCompat> messageNodeList = rootInActiveWindow.findAccessibilityNodeInfosByViewId ("com.whatsapp:id/entry");
if (messageNodeList == null || messageNodeList.isEmpty ()) {
return;
}
// check if the whatsapp message EditText field is filled with text and ending with your suffix (explanation above)
AccessibilityNodeInfoCompat messageField = messageNodeList.get (0);
if (messageField.getText () == null || messageField.getText ().length () == 0
|| !messageField.getText ().toString ().endsWith (getApplicationContext ().getString (R.string.whatsapp_suffix))) { // So your service doesn't process any message, but the ones ending your apps suffix
return;
}
// Whatsapp send button id
List<AccessibilityNodeInfoCompat> sendMessageNodeInfoList = rootInActiveWindow.findAccessibilityNodeInfosByViewId ("com.whatsapp:id/send");
if (sendMessageNodeInfoList == null || sendMessageNodeInfoList.isEmpty ()) {
return;
}
AccessibilityNodeInfoCompat sendMessageButton = sendMessageNodeInfoList.get (0);
if (!sendMessageButton.isVisibleToUser ()) {
return;
}
// Now fire a click on the send button
sendMessageButton.performAction (AccessibilityNodeInfo.ACTION_CLICK);
// Now go back to your app by clicking on the Android back button twice:
// First one to leave the conversation screen
// Second one to leave whatsapp
try {
Thread.sleep (500); // hack for certain devices in which the immediate back click is too fast to handle
performGlobalAction (GLOBAL_ACTION_BACK);
Thread.sleep (500); // same hack as above
} catch (InterruptedException ignored) {}
performGlobalAction (GLOBAL_ACTION_BACK);
}
}
然后在res -> xml -> whatsapp_service.xml
中创建它的定义:
<?xml version="1.0" encoding="utf-8"?>
<accessibility-service
xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged"
android:packageNames="com.whatsapp"
android:accessibilityFeedbackType="feedbackSpoken"
android:notificationTimeout="100"
android:canRetrieveWindowContent="true"/>
然后在您的清单中声明它:
<service
android:name=".services.WhatsappAccessibilityService"
android:label="Accessibility Service"
android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE">
<meta-data
android:name="android.accessibilityservice"
android:resource="@xml/whatsapp_service"/>
<intent-filter>
<action android:name="android.accessibilityservice.AccessibilityService"/>
</intent-filter>
</service>
最后一件事,是检查您的应用是否启用了辅助功能服务,如果未启用,则将用户重定向到设置:
private boolean isAccessibilityOn (Context context, Class<? extends AccessibilityService> clazz) {
int accessibilityEnabled = 0;
final String service = context.getPackageName () + "/" + clazz.getCanonicalName ();
try {
accessibilityEnabled = Settings.Secure.getInt (context.getApplicationContext ().getContentResolver (), Settings.Secure.ACCESSIBILITY_ENABLED);
} catch (Settings.SettingNotFoundException ignored) { }
TextUtils.SimpleStringSplitter colonSplitter = new TextUtils.SimpleStringSplitter (":");
if (accessibilityEnabled == 1) {
String settingValue = Settings.Secure.getString (context.getApplicationContext ().getContentResolver (), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES);
if (settingValue != null) {
colonSplitter.setString (settingValue);
while (colonSplitter.hasNext ()) {
String accessibilityService = colonSplitter.next ();
if (accessibilityService.equalsIgnoreCase (service)) {
return true;
}
}
}
}
return false;
}
您将使用的电话:
if (!isAccessibilityOn (context, WhatsappAccessibilityService.class)) {
Intent intent = new Intent (Settings.ACTION_ACCESSIBILITY_SETTINGS);
context.startActivity (intent);
}
这纯粹是解决方案的技术方面。
现在,"should you do that?"的伦理问题,相信答案已经很明确了:
除非您针对的是残障人士(这是辅助功能 API 的真正目的),否则您应该 不 那样做。