27

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

udit7395
  • 626
  • 5
  • 16
Nizar
  • 1,112
  • 4
  • 17
  • 29
  • 1
    Possible duplicate of [Android: How to send message programmatically by using WhatsApp, WeChat?](https://stackoverflow.com/questions/24774595/android-how-to-send-message-programmatically-by-using-whatsapp-wechat) – ADM Apr 04 '18 at 15:17
  • You can only send the user and their message to WhatsApp, you cannot push the buttons for them. – WoogieNoogie Apr 04 '18 at 15:18
  • 1
    You can not directly send message without send acknowledgement from user. Whats app does not provide such Feature i think none of app provide such feature unless you are authenticated with a SDK . – ADM Apr 04 '18 at 15:19
  • this is the expected behaviour , you can not send msgs directly , user need to click the whatsapp send button – Sachin Rajput Apr 04 '18 at 15:20
  • I know, i want the app to run 24/7 to send some data, the client want to get notified when power loses accures via sms or whtsapp – Nizar Apr 04 '18 at 15:21
  • Whats App avoids sending the message without the user interaction, that is the good behavior – Khalid Taha Apr 04 '18 at 15:22
  • There is no API so send messages without user input (there **NEVER** will be, that would be a **very bad** thing, on all social media). Send a real S.M.S. ? Probably could hack it, until they find out....then you get banned ;O). goes against the *spirit * of the app... – Jon Goodwin Apr 04 '18 at 15:28
  • 1
    suddenly the answer is coming below. LoL. – gumuruh Jul 31 '18 at 10:26

1 Answers1

56

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.