20

From one year, I have been working over IOT product and the application attached was working fine. Now I am not able to accept call programmatically in higher versions of android. Feature is very important to product. Any help is highly appreciated.

Before security patch update November 2016, Runtime.getRunTime.exec("Command") was working fine to accept call programmatically.

Runtime.getRuntime().exec("input keyevent " +Integer.toString(KeyEvent.KEYCODE_HEADSETHOOK));

How to make it possible in Nougat version of android.

Looking for any sort of hack.

I have opened a thread for the enhancements.

https://code.google.com/p/android/issues/detail?can=2&start=0&num=100&q=&colspec=ID%20Status%20Priority%20Owner%20Summary%20Stars%20Reporter%20Opened&groupby=&sort=&id=231938

Note* If any one of you is facing same issue, then please request to Android Dev Team to get in it and provide provision to get run-time permission by user. Follow above mention URL to request.

OneCricketeer
  • 179,855
  • 19
  • 132
  • 245
AndroidHacker
  • 3,596
  • 1
  • 25
  • 45

4 Answers4

11

As I am also working on IOT product, this was one of the biggest issue I faced but after some Research, I think I have found some solution for this problem, or you can say a simple hack. I have tested this hack in several devices with several versions, and found that most of the devices are responding. Only Samsung devices are not responding, some Huawei devices and some Oppo devices are also not responding.(I am still looking something for these devices too).

I noticed that Android provides one feature of Accessing Notifications. You can use NotificationListenerService to read notifications and perform some actions over them. It provides some override methods:

 onNotificationPosted()
    onNotificationRemoved()
    getActiveNotifications()

... etc

Here is a code: Create a Service that extends NotificationListenerService

 class NLService extends NotificationListenerService {

     @Override
     public void onNotificationPosted(StatusBarNotification sbn) {
       ....
     }

     @Override
     public void onNotificationRemoved(StatusBarNotification sbn) {
       ....
     }

In AndroidMenifest, add this service as:

 <service
        android:name=".NLService"
        android:label="@string/app_name"
android:permission="android.permission.BIND_NOTIFICATION_LISTENER_SERVICE">
        <intent-filter>
            <action 
android:name="android.service.notification.NotificationListenerService" />
        </intent-filter>
    </service>

This will allow your application to read any notification received.

Now, here is the main code:

In onNotificationPosted(StatusBarNotification sbn) add this code:

 @Override
    public void onNotificationPosted(StatusBarNotification sbn) {
        try {
            if (sbn.getNotification().actions != null) {
                for (Notification.Action action : sbn.getNotification().actions) 
                  {
                    Log.e(TAG, "" + action.title);
                    if (action.title.toString().equalsIgnoreCase("Answer")) {
                        Log.e(TAG, "" + true);
                        PendingIntent intent = action.actionIntent;

                        try {
                            intent.send();
                        } catch (PendingIntent.CanceledException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
          } catch (Exception e) {
              e.printStackTrace();
          }
     }

Thats it!

All is set, Run the application and the devices except Samsung, whichever shows a notification for Incoming call, with Answer and Reject/Decline Action buttons, will allow you to answer a call.

To open Notification Access Settings and allowing your application to read notification, use:

 Intent intent = new 
    Intent("android.settings.ACTION_NOTIFICATION_LISTENER_SETTINGS");
        startActivity(intent); 

Just create a POC for this and let me know about how it works.

Please mark my answer if this helps.

Also, if you could provide some solution for the same regarding Samsung Devices, please update.

Thanks

Vishal Sharma
  • 616
  • 1
  • 6
  • 21
1

It is sort of hack, you can use accessibility service to receive call. To enable accessibility service, you must enable your service on Setting - Accessibility - Your service.

First, add typeWindowContentChanged to accessibilityEventTypes.

<accessibility-service
     android:accessibilityEventTypes="typeViewClicked|typeViewFocused|typeViewScrolled|typeWindowContentChanged|typeWindowStateChanged"
     android:packageNames="com.example.android.myFirstApp, com.example.android.mySecondApp"
     android:accessibilityFeedbackType="feedbackSpoken"
     android:notificationTimeout="100"
     android:settingsActivity="com.example.android.apis.accessibility.TestBackActivity"
     android:canRetrieveWindowContent="true"
/>

And do something with event or "displayed text" or "contents description".

@Override
public void onAccessibilityEvent(AccessibilityEvent event) {

    // Do something with Click or Focused event
    final int eventType = event.getEventType();
    String eventText = null;
    switch(eventType) {
        case AccessibilityEvent.TYPE_VIEW_CLICKED:
            eventText = "Focused: ";
            break;
        case AccessibilityEvent.TYPE_VIEW_FOCUSED:
            eventText = "Focused: ";
            break;
    }
    eventText = eventText + event.getContentDescription();


    // Traverse all items in screen. 
    // Do something with text.

    AccessibilityNodeInfo info = getRootInActiveWindow();

    int index;
    int count = info.getChildCount();
    AccessibilityNodeInfo child;

    for (index = 0; index < count; index++) {
        child = info.getChild(index);
        if (child.getText() != null)
            Log.d(TAG, "text: " + child.getText().toString() + " " + child.getContentDescription());

        // perform Click
        //if (child.isClickable());
            //child.performAction(AccessibilityNodeInfo.ACTION_CLICK);
    }
}

Yes, I know this is not a graceful way to solve your problem. It is a kind of hack.

Stanley Ko
  • 3,383
  • 3
  • 34
  • 60
  • @Stanely I am trying the same as you written in your post but i am not getting any event in onAccessibilityEvent when phone call received. Can you please show your complete example. – user565 Jul 01 '19 at 13:18
  • @user565 Did you turn on your Accessibility option from settinng menu? You may check https://developer.android.com/guide/topics/ui/accessibility/service to learn about Accessibility Service. And also, you must use android version 1.6 or more. – Stanley Ko Jul 01 '19 at 14:20
  • Accessibility service can do anything, but using this to response call is a hack. So I am not suggest you to do so. – Stanley Ko Jul 01 '19 at 14:24
  • Well I actually tested with using NotificationListenerService and it is working fine but the only problem which i am facing using NotificationListenerService is that user should need to register the listener manually and i want to do that by application(I have a system app). So then i start looking into your way by using Accessibility Service and interesting thing here is that i can register my application programatically by using this way: – user565 Jul 01 '19 at 14:31
  • Settings.Secure.putString(getContentResolver(), Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "myPackage/myService"); Settings.Secure.putString(getContentResolver(), Settings.Secure.ACCESSIBILITY_ENABLED, "1"); – user565 Jul 01 '19 at 14:31
0

For answering a call using a button, set a flag whenever an incoming call is detected:

 if(state==TelephonyManager.CALL_STATE_RINGING){
           shouldAnswerCallViaNotification = true;
        } else {
            shouldAnswerCallViaNotification = false;
        }

Now, create a list in your NSLogService class,

static ArrayList<StatusBarNotification> statusBarNotifications;

and in your onNotificationPosted() add StatusBarNotification to a list,

   @Override
public void onNotificationPosted(StatusBarNotification sbn) {

    if (HomeScreen.shouldAnswerCallViaNotification) {
        if (statusBarNotifications == null) {
            updateNotificationList();
        }
       statusBarNotifications.add(sbn);

    } else {
        updateNotificationList();
    }

}
 public static ArrayList<StatusBarNotification> getAllNotifications() {
    return statusBarNotifications;
}

public static void updateNotificationList() {

    if (statusBarNotifications != null)
        statusBarNotifications = null;
    statusBarNotifications = new ArrayList<StatusBarNotification>();
}

In your HomeScreen, on Button click, call performNotificationOperation(NLService.getAllNotifications());

here is a definition for this method:

 private void performNotificationOperation(ArrayList<StatusBarNotification> activeNotifications) {

    if (activeNotifications.size()> 0) {
        main_Loop:
        for (StatusBarNotification notification : activeNotifications) {
            try {
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
                    if (notification.getNotification().actions != null) {
                        for (Notification.Action action : notification.getNotification().actions) {
                            //            Log.e(TAG, "" + action);

                            Log.e(TAG, "" + action.title);
                            if (action.title.toString().equalsIgnoreCase("Answer")) {
                                Log.e(TAG, "" + true);
                                PendingIntent intent = action.actionIntent;

                                try {
                                    intent.send();
                                } catch (PendingIntent.CanceledException e) {
                                    e.printStackTrace();
                                }
                                break main_Loop;
                            }


                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }

        }
    }
    try {
        NLService.updateNotificationList();
    } catch (Exception e) {
        e.printStackTrace();
    }

}
Vishal Sharma
  • 616
  • 1
  • 6
  • 21
0

According to "Vishal Sharma" answer we can get action button title dynamically ,for supporting other languages :

  @Override
  public void onNotificationPosted(StatusBarNotification sbn) {
    try {
      if (sbn.getNotification().actions != null) {
        Notification.Action[] notificationAction=sbn.getNotification().actions;
        //Log.e(G.TAG, "" +notificationAction  + " =>"+notificationAction.length);
           String rejectTitle=""+notificationAction[0].title;
           String acceptTitle=""+notificationAction[1].title;

        for (Notification.Action action : notificationAction){
          if (action.title.toString().equalsIgnoreCase(acceptTitle)) {
            PendingIntent intent = action.actionIntent;
            try {
              intent.send();
            } catch (PendingIntent.CanceledException e) {
              e.printStackTrace();
            }
          }
        }


      }
    } catch (Exception e) {
      e.printStackTrace();
    }
  }
Mikael
  • 39
  • 3