Программно принимать вызов в Нуге

С одного года я работаю над продуктом IOT, и приложение прилагается к работе. Теперь я не могу принять вызов программно в более высоких версиях Android. Особенность очень важна для продукта. Любая помощь приветствуется.

До обновления обновления безопасности ноябрь 2016, Runtime.getRunTime.exec("Command") работал нормально, чтобы принимать вызов программно.

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

Как сделать это возможным в версии Nougat для Android.

Ищете какой-нибудь взлом.

Я открыл поток для улучшений.

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-адрес для запроса.

Ответ 1

Поскольку я также работаю над продуктом IOT, это была одна из самых больших проблем, с которыми я столкнулся, но после некоторых исследований я считаю, что нашел какое-то решение этой проблемы, или вы можете сказать простой взлом. Я протестировал этот хак на нескольких устройствах с несколькими версиями и обнаружил, что большинство устройств реагируют. Только устройства Samsung не отвечают, некоторые устройства Huawei и некоторые устройства Oppo также не отвечают (я все еще смотрю что-то для этих устройств тоже).

Я заметил, что Android предоставляет одну из функций "Доступ к уведомлениям". Вы можете использовать NotificationListenerService для чтения уведомлений и выполнения некоторых действий над ними. Он предоставляет некоторые методы переопределения:

 onNotificationPosted()
    onNotificationRemoved()
    getActiveNotifications()

... и т.д.

Вот код: Создайте службу, которая расширяет NotificationListenerService

 class NLService extends NotificationListenerService {

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

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

В AndroidMenifest добавьте эту услугу как:

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

Вот оно!

Все установлено. Запустите приложение и устройства, за исключением Samsung, в зависимости от того, что отображает уведомление о входящем вызове с кнопками "Ответ" и "Отклонить/отменить действие", вы сможете ответить на вызов.

Чтобы открыть Настройки доступа к уведомлению и позволить вашему приложению читать уведомление, используйте:

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

Просто создайте POC для этого и сообщите мне, как это работает.

Пожалуйста, отметьте мой ответ, если это поможет.

Кроме того, если вы можете предоставить некоторое решение по этому вопросу в отношении устройств Samsung, пожалуйста, обновите.

Спасибо

Ответ 2

Для ответа на вызов с помощью кнопки установите флаг при обнаружении входящего вызова:

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

}

Ответ 3

Это своего рода хак, вы можете использовать службу доступности для приема вызова. Чтобы включить службу доступности, вы должны включить свою службу в настройке - Доступность - Ваша услуга.

Сначала добавьте типWindowContentChanged для 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);
    }
}

Да, я знаю, что это не изящный способ решить вашу проблему. Это своего рода хак.