在Nougat系统中编程接受电话

20

我已经从事物联网产品和相关应用的工作一年了,之前应用程序能够正常接受来电。但是现在在Android的高版本中无法通过编程方式接受来电。这个功能对于产品非常重要,希望得到帮助。

在安全补丁更新2016年11月之前,Runtime.getRunTime.exec("Command")可以正常工作以便通过编程方式接受来电。

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

如何在安卓Nougat版本中实现。

寻找任何类型的hack。

我已经开了一个帖子寻求改进。

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

注意* 如果你也遇到了同样的问题,请请求 Android Dev Team 加入其中并提供让用户获取运行时权限的方法。请按照上面提到的 URL 发出请求。


同样的问题,没有解决方案。 - karanatwal.github.io
已经尝试过并且也使用了ITelephony.Aidl接口。 - AndroidHacker
当然,我已经尝试了这个解决方案。接下来我会尝试加入延迟,或许可以解决问题。无论如何,我会告诉你结果的。 - AndroidHacker
它需要 MODIFY_PHONE_STATE 权限才能正常工作。该权限仅适用于系统应用程序。 - AndroidHacker
自从Marshmallow版本以来,您可以制作一个电话应用程序,并在用户将其选择为默认电话应用程序后,使用公共API进行呼叫和接收等操作。 - nandsito
显示剩余10条评论
4个回答

11

因为我也在做物联网产品,所以这是我面临的最大问题之一。经过一些研究,我认为已经找到了解决这个问题的方法,或者你可以说是一个简单的技巧。 我已经在多个设备和多个版本中测试了这个技巧,并发现大多数设备都会响应。只有三星设备没有响应,还有一些华为设备和一些OPPO设备也没有响应。(我仍在寻找这些设备的解决方案)。

我注意到Android提供了一个访问通知的功能。您可以使用NotificationListenerService读取通知并执行一些操作。 它提供了一些重写方法:

 onNotificationPosted()
    onNotificationRemoved()
    getActiveNotifications()
这里有一段代码: 创建一个扩展NotificationListenerService的服务。
 class NLService extends NotificationListenerService {

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

     @Override
     public void onNotificationRemoved(StatusBarNotification sbn) {
       ....
     }
在AndroidManifest文件中,将此服务添加为:
 <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>

这将允许您的应用程序读取收到的任何通知。

现在,这是主要代码:

在onNotificationPosted(StatusBarNotification sbn)中添加此代码:

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

就是这样!

一切都准备好了,运行应用程序,除了三星设备之外的任何设备,在显示来电通知时,将提供接听和拒绝/拒绝操作按钮,您可以使用它来接听电话。

要打开通知访问设置并允许您的应用程序读取通知,请使用:

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

只需为此创建一个POC,并让我知道它的工作原理。

如果这有帮助,请标记我的回答。

此外,如果您能提供关于三星设备的解决方案,请更新。

谢谢


在我的三星Note 2 Android v7.1.1上无缝运行!谢谢! - ProllyGeek
完美运行,但如果我想用按钮回答它呢?而不是自动回答。 - user7596908
一个简单的场景可以用于此。请查看我的新答案。 - Vishal Sharma
你好,你解决了三星的问题吗?我认为三星使用了他们丰富的通知 SDK。但是我不知道该如何处理它。 - EricZhao

1
这是一种技巧,您可以使用辅助功能服务来接收电话。要启用辅助功能服务,必须在“设置”-“辅助功能”-“您的服务”中启用该服务。
首先,将typeWindowContentChanged添加到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"
/>

并且要对事件、"显示文本"或"内容描述"做一些操作。

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

是的,我知道这不是解决问题的优雅方式。它是一种hack。


@Stanely,我正在尝试你在帖子中写的同样的代码,但是当手机接到电话时,onAccessibilityEvent中没有任何事件。您可以展示完整的例子吗? - user565
@user565 你是否已经从设置菜单中打开了辅助功能选项?您可以查看https://developer.android.com/guide/topics/ui/accessibility/service以了解辅助功能服务。另外,您必须使用Android版本1.6或更高版本。 - Stanley Ko
无障碍服务可以做任何事情,但使用它来响应电话是一种hack。因此,我不建议您这样做。 - Stanley Ko
我实际上已经使用NotificationListenerService进行了测试,它运行良好,但我面临的唯一问题是使用NotificationListenerService时用户需要手动注册侦听器,而我想通过应用程序(我有一个系统应用程序)来完成这个过程。因此,我开始研究使用辅助功能服务的方法,有趣的是,我可以通过使用这种方式以编程方式注册我的应用程序: - user565
Settings.Secure.putString(getContentResolver(),               Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES, "myPackage/myService");        Settings.Secure.putString(getContentResolver(),               Settings.Secure.ACCESSIBILITY_ENABLED, "1"); - user565

0

为了使用按钮接听电话,当检测到来电时设置一个标志:

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

现在,在你的NSLogService类中创建一个列表,
static ArrayList<StatusBarNotification> statusBarNotifications;

在你的onNotificationPosted()方法中,将StatusBarNotification添加到一个列表中。
   @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>();
}

在您的HomeScreen中,当按钮被点击时,调用performNotificationOperation(NLService.getAllNotifications()); 下面是该方法的定义:
 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();
    }

}

0
根据"Vishal Sharma"的回答,我们可以动态获取操作按钮标题,以支持其他语言:
  @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();
    }
  }

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接