Send message via whatsapp programmatically

Nizar picture Nizar · Apr 4, 2018 · Viewed 20.1k times · Source

I'm trying to send messages via Whatsapp programmatically, the code works except the user needs to click the send button. I need the app to do everything (all user interactions). One way to do to it as follows .

Go to Menu Button > Settings > Chats. and check the "Enter is send option"

Here's the code I'm using:

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);
}

Thank you

Answer

user1079425 picture user1079425 · Apr 4, 2018

You can do that only using the Accessibility API of Android.

The idea is quite simple, you'll actually make Android perform the click on Whatsapp's send button.

So the flow will be:

  1. Send a regular message (with the intent you're currently using) with a suffix at the end of your message content such as "Sent by MY_APP".
  2. Once the text there, your accessibility service will be notified that the EditText of whatsapp is filled.
  3. If the suffix is present on the EditText of whatsapp, Your accessibility service will click on the send button. (this is to avoid performing actions as the user types in naturally a regular message).

Here's an example (which you'll have tweak if you wanna make it more restrictive):

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);
    }
}

Then create its definition in 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"/>

Then declare it in your manifest:

<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>

And last thing, is to check if the accessibility services are enabled for your app or not, and redirect the user to the settings if not:

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;
}

which you'll call with:

if (!isAccessibilityOn (context, WhatsappAccessibilityService.class)) {
    Intent intent = new Intent (Settings.ACTION_ACCESSIBILITY_SETTINGS);
    context.startActivity (intent);
}

This is purely on the technical aspect of the solution.

Now, the ethical question of "should you do that?", I believe the answer is quite clear:

Except if you are targeting people with disabilities (which is the very purpose of the Accessibility API), you should probably NOT do that.