Не удается определить, когда на исходящий вызов отвечает Android

Чтобы определить, когда отвечает исходящий вызов, я попытался создать PhoneStateListener и прослушать TelephonyManager CALL_STATE_RINGING, CALL_STATE_OFFHOOK и CALL_STATE_IDLE, из this вопрос, но, похоже, это не работает, как описано ниже.

Во-первых, я зарегистрировал в манифесте следующее разрешение:

<uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />

Затем a BroadcastReceiver называется OutCallLogger, который захватывает событие NEW_OUTGOING_CALL всякий раз, когда выполняется исходящий вызов:

<receiver android:name=".listener.OutCallLogger">
    <intent-filter>
        <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
    </intent-filter>
</receiver>

Далее, моя реализация OutCallLogger. Я создал логическое имя noCallListenerYet, чтобы избежать привязки нового PhoneStateListener к TelephonyManager при вызове onReceive().

public class OutCallLogger extends BroadcastReceiver {

    private static boolean noCallListenerYet = true;

    @Override
    public void onReceive(final Context context, Intent intent) {
        number = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
        if (noCallListenerYet) {
            final TelephonyManager tm = (TelephonyManager) context.getSystemService(
                    Context.TELEPHONY_SERVICE);
            tm.listen(new PhoneStateListener() {
                @Override
                public void onCallStateChanged(int state, String incomingNumber) {
                    switch (state) {
                        case TelephonyManager.CALL_STATE_RINGING:
                            Log.d(This.LOG_TAG, "RINGING");
                            break;
                        case TelephonyManager.CALL_STATE_OFFHOOK:
                            Log.d(This.LOG_TAG, "OFFHOOK");
                            break;
                        case TelephonyManager.CALL_STATE_IDLE:
                            Log.d(This.LOG_TAG, "IDLE");
                            break;
                        default:
                            Log.d(This.LOG_TAG, "Default: " + state);
                            break;
                    }
                }
            }, PhoneStateListener.LISTEN_CALL_STATE);
            noCallListenerYet = false;
        }
    }

}

Теперь, когда я делаю исходящий вызов на своем устройстве, CALL_STATE_RINGING НИКОГДА не запускается. Я всегда получаю распечатки "IDLE" до "OFFHOOK", когда другая строка начинает звонить, ничего после ответа на вызов и распечатки "IDLE" снова, когда вызов завершен.

Как я могу надежно обнаруживать, когда на исходящий вызов отвечает Android, или это возможно?

Ответ 1

С Android 5.0 это возможно для системных приложений. Но вам нужно использовать скрытый Android API.

У меня это получилось так:

<uses-permission android:name="android.permission.READ_PRECISE_PHONE_STATE" />
<receiver android:name=".listener.OutCallLogger">
    <intent-filter>
        <action android:name="android.intent.action.PRECISE_CALL_STATE" />
    </intent-filter>
</receiver>
public class OutCallLogger extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        switch (intent.getIntExtra(TelephonyManager.EXTRA_FOREGROUND_CALL_STATE, -2) {
            case PreciseCallState.PRECISE_CALL_STATE_IDLE:
                Log.d(This.LOG_TAG, "IDLE");
                break;
            case PreciseCallState.PRECISE_CALL_STATE_DIALING:
                Log.d(This.LOG_TAG, "DIALING");
                break;
            case PreciseCallState.PRECISE_CALL_STATE_ALERTING:
                Log.d(This.LOG_TAG, "ALERTING");
                break;
            case PreciseCallState.PRECISE_CALL_STATE_ACTIVE:
                Log.d(This.LOG_TAG, "ACTIVE");
                break;
        }
    }
}

Вы можете найти все возможные состояния вызовов в PreciseCallState.java и все дополнительные функции, содержащиеся в TelephonyRegistry.java.

Ответ 2

Похоже, что состояние RINGING достигается только входящими вызовами. Исходящие звонки меняются от IDLE до OFFHOOK, поэтому смотреть на состояние телефона возможно невозможно.

Я думаю, что можно было бы использовать внутренние функции, посмотрите на это: Что представляют собой различные состояния вызовов в стеке телефонии Android?

Ответ 3

Возможно, попробуйте использовать CallManager? Проверьте http://grepcode.com/file/repository.grepcode.com/java/ext/com.google.android/android/2.3.3_r1/com/android/internal/telephony/CallManager.java. Я также нашел CallManager.java среди файлов SDK на своем компьютере. Следующий текст со связанной страницы кажется многообещающим:

Register for getting notifications for change in the Call State Call.State This is 
called PreciseCallState because the call state is more precise than the Phone.State 
which can be obtained using the android.telephony.PhoneStateListener Resulting events 
will have an AsyncResult in Message.obj. AsyncResult.userData will be set to the obj 
argument here. The h parameter is held only by a weak reference.

1051
1052    public void registerForPreciseCallStateChanged(Handler h, int what, Object obj){
1053        mPreciseCallStateRegistrants.addUnique(h, what, obj);
1054    }

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

Ответ 4

Обратите внимание:

tm.listen(new PhoneStateListener() {
            @Override
            public void onCallStateChanged(int state, String incomingNumber) {
                switch (state) {
                    case TelephonyManager.CALL_STATE_RINGING:
                        Log.d(This.LOG_TAG, "RINGING");
                        break;
                    case TelephonyManager.CALL_STATE_OFFHOOK:
                        Log.d(This.LOG_TAG, "OFFHOOK");
                        break;
                    case TelephonyManager.CALL_STATE_IDLE:
                        Log.d(This.LOG_TAG, "IDLE");
                        break;
                    default:
                        Log.d(This.LOG_TAG, "Default: " + state);
                        break;
                }
            }
        }, PhoneStateListener.LISTEN_CALL_STATE);

Вы видите аргумент "incomingNumber"? Да, этот код просто может обнаруживать ваше состояние телефонного звонка, когда на ваш телефон поступает входящий звонок.

Ответ 5

Вы могли бы сделать следующее... не очень точное, но могли бы сделать трюк:

  • Вы используете ресивер для действия android.intent.action.NEW_OUTGOING_CALL
  • Когда вызывается получатель, вы храните где-нибудь (например, статический var) статус NEW_OUTGOIN_CALL и время в мс, когда это произошло (т.е. новый Date(). getTime())
  • Вы используете другой приемник для android.intent.action.PHONE_STATE, а в onReceive вы делаете следующее:

    if (intent.getAction().equals("android.intent.action.PHONE_STATE")) {
        TelephonyManager telephony = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE);
        telephony.listen(new PhoneStateListener() {
            public void onCallStateChanged(int state, String incomingNumber) {
                  switch(state) {
                    case TelephonyManager.CALL_STATE_IDLE:
                    break;
                    case TelephonyManager.CALL_STATE_OFFHOOK:
                    break;
                    case TelephonyManager.CALL_STATE_RINGING:
                    break;
                   }
            } 
        }, PhoneStateListener.LISTEN_CALL_STATE);
    
    }
    

В случае CALL_STATE_OFFHOOK вы проверяете, что последнее сохраненное состояние было NEW_OUTGOING_CALL и это не более чем aprox. С момента последнего изменения состояния прошло 10 секунд. Это означает, что телефон инициировал вызов не более 10 секунд назад, и теперь он находится в состоянии offhook (что означает активный вызов), не проходя через простоя или звонка. Это может означать, что на вызов был дан ответ.

Ответ 6

Вот ваш ответ, что вы внедрили CallStateListener в OutGoingCallReceiver, что неверно. Вы должны реализовать CallStateListener в PhoneStateListener

Я также пробовал эту вещь в своем предыдущем проекте, я столкнулся с той же проблемой, а затем решил это, как показано ниже. Я взял 3 класса, как показано ниже.

  • AutoCallReceiver: зарегистрируйте TelephonyManager с помощью PhoneStateListener.LISTEN_CALL_STATE

  • CallStateListener, который прослушивает три состояния как TelephonyManager.CALL_STATE_IDLE, TelephonyManager.CALL_STATE_OFFHOOK, TelephonyManager.CALL_STATE_RINGING

3.OutGoingCallReceiver, который обрабатывает вызов

public class OutGoingCallReceiver extends BroadcastReceiver {  

    /* onReceive will execute on out going call */
    @Override  
    public void onReceive(Context context, Intent intent) {  

         Toast.makeText(context, "OutGoingCallReceiver", Toast.LENGTH_SHORT).show();

    }  
  } 

public class CallStateListener extends PhoneStateListener {  

    String number=""; // variable for storing incoming/outgoing number
    Context mContext; // Application Context


    //Constructor that will accept Application context as argument
    public CallStateListener(Context context) {
        mContext=context;
    }

    // This function will automatically invoke when call state changed
    public void onCallStateChanged(int state,String incomingNumber)
    {  
           boolean end_call_state=false; // this variable when true indicate that call is disconnected
           switch(state)
               {  
                 case TelephonyManager.CALL_STATE_IDLE:  
                       // Handling Call disconnect state after incoming/outgoing call 
                     Toast.makeText(mContext, "idle", Toast.LENGTH_SHORT).show();


                  break;  

                 case TelephonyManager.CALL_STATE_OFFHOOK:     
                     // Handling outgoing call  
                     Toast.makeText(mContext, "OFFHOOK", Toast.LENGTH_SHORT).show();
                      // saving outgoing call state so that after disconnect idle state can act accordingly


                  break;  
                 case TelephonyManager.CALL_STATE_RINGING: 
                     Toast.makeText(mContext, "RINGING", Toast.LENGTH_SHORT).show();

                  break;  

               }  


    }   

  } 

public class AutoCallReceiver extends BroadcastReceiver {  


   /* onReceive will execute on call state change */
    @Override  
    public void onReceive(Context context, Intent intent) { 

        // Instantiating PhoneStateListener
        CallStateListener phoneListener=new CallStateListener(context);  

        // Instantiating TelephonyManager
      TelephonyManager telephony = (TelephonyManager)   
               context.getSystemService(Context.TELEPHONY_SERVICE);  

        // Registering the telephony to listen CALL STATE change
      telephony.listen(phoneListener,PhoneStateListener.LISTEN_CALL_STATE);  
    }  

}

 <receiver android:name=".OutGoingCallReceiver">
    <intent-filter>
      <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
    </intent-filter>
</receiver>
 <receiver android:name=".AutoCallReceiver">
            <intent-filter>
                     <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
         </receiver>
    </application>
 <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS"/>