【问题标题】:Android - INCOMING & OUTGOING callsAndroid - 来电和去电
【发布时间】:2013-06-02 01:10:45
【问题描述】:

我试图在每次开始通话或每次接听电话时显示一条消息。我制作了一个适用于传入呼叫但不适用于传出呼叫的代码。 我确实阅读了有关此主题的不同帖子。

谁能告诉我为什么下面的代码向我展示了(当然,这段代码并没有完成我之前提到的所有事情):
- 当我接到电话时“外出”
- 当我开始通话时,“INCOMING”然后“OUTGOING”

/* 来自 MainActivity */

protected void onCreate(Bundle savedInstanceState) 
 {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  context = getApplicationContext();
  context.startService(new Intent(context, SvcCall.class));
 }

/* 来自 SvcCall */

public class SvcCall extends Service 
 {
  private static final String ACTION_OUT = "android.intent.action.PHONE_STATE";
  private static final String ACTION_IN = "android.intent.action.NEW_OUTGOING_CALL";
  private CallBr br_call;

  @Override
  public void onCreate()
   {
    super.onCreate();   
   }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId)
   {
    final IntentFilter filter = new IntentFilter();
    filter.addAction(ACTION_OUT);
    filter.addAction(ACTION_IN); 
    this.br_call = new CallBr();
    this.registerReceiver(this.br_call, filter);
    return (START_STICKY);
   }

  public class CallBr extends BroadcastReceiver  
   {
    @Override
    public void onReceive(Context context, Intent intent) 
 {
  if (intent.getAction().equals(ACTION_IN))
       Toast.makeText(context, "INCOMING", Toast.LENGTH_LONG).show();       
  else if (intent.getAction().equals(ACTION_OUT))
   Toast.makeText(context, "OUTGOING", Toast.LENGTH_LONG).show();
     }
   }

 @Override
 public IBinder onBind(Intent intent) 
 {
  return null;
 }
} 

/* 来自清单 */

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

【问题讨论】:

  • 看文字:ACTION_INACTION_OUT的定义是不是颠倒了?
  • +1。谢啦。我猜这就是它。要检查它。但我决定注册两个不同的广播接收器。
  • 一旦你得到它的工作,包括第二个问题(当你开始通话时祝酒两次),发布更正作为答案并接受它。这样做是完全可以的,所以如果其他人遇到同样的问题,他们就会找到答案。

标签: android


【解决方案1】:

这是我用来对来电和去电做出反应的类。它的设置使您只需要派生和覆盖您关心的那些。它还告诉您呼叫是开始、结束还是未被接听:

package com.gabesechan.android.reusable.receivers;

import java.util.Date;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;

public abstract class PhonecallReceiver extends BroadcastReceiver {

    //The receiver will be recreated whenever android feels like it.  We need a static variable to remember data between instantiations
    static PhonecallStartEndDetector listener;
    String outgoingSavedNumber;
    protected Context savedContext;


    @Override
    public void onReceive(Context context, Intent intent) {
        savedContext = context;
        if(listener == null){
            listener = new PhonecallStartEndDetector();
        }

        //We listen to two intents.  The new outgoing call only tells us of an outgoing call.  We use it to get the number.
        if (intent.getAction().equals("android.intent.action.NEW_OUTGOING_CALL")) {
            listener.setOutgoingNumber(intent.getExtras().getString("android.intent.extra.PHONE_NUMBER"));
            return;
        }

        //The other intent tells us the phone state changed.  Here we set a listener to deal with it
        TelephonyManager telephony = (TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE); 
        telephony.listen(listener, PhoneStateListener.LISTEN_CALL_STATE);
    }

    //Derived classes should override these to respond to specific events of interest
    protected abstract void onIncomingCallStarted(String number, Date start);
    protected abstract void onOutgoingCallStarted(String number, Date start);
    protected abstract void onIncomingCallEnded(String number, Date start, Date end); 
    protected abstract void onOutgoingCallEnded(String number, Date start, Date end);
    protected abstract void onMissedCall(String number, Date start);

    //Deals with actual events
    public class PhonecallStartEndDetector extends PhoneStateListener {
        int lastState = TelephonyManager.CALL_STATE_IDLE;
        Date callStartTime;
        boolean isIncoming;
        String savedNumber;  //because the passed incoming is only valid in ringing

        public PhonecallStartEndDetector() {}

        //The outgoing number is only sent via a separate intent, so we need to store it out of band
        public void setOutgoingNumber(String number){
            savedNumber = number;
        }

        //Incoming call-  goes from IDLE to RINGING when it rings, to OFFHOOK when it's answered, to IDLE when its hung up
        //Outgoing call-  goes from IDLE to OFFHOOK when it dials out, to IDLE when hung up
        @Override
        public void onCallStateChanged(int state, String incomingNumber) {
            super.onCallStateChanged(state, incomingNumber);
            if(lastState == state){
                //No change, debounce extras
                return;
            }
            switch (state) {
                case TelephonyManager.CALL_STATE_RINGING:
                    isIncoming = true;
                    callStartTime = new Date();
                    savedNumber = incomingNumber;
                    onIncomingCallStarted(incomingNumber, callStartTime);
                    break;
                case TelephonyManager.CALL_STATE_OFFHOOK:
                    //Transition of ringing->offhook are pickups of incoming calls.  Nothing donw on them
                    if(lastState != TelephonyManager.CALL_STATE_RINGING){
                        isIncoming = false;
                        callStartTime = new Date();
                        onOutgoingCallStarted(savedNumber, callStartTime);                      
                    }
                    break;
                case TelephonyManager.CALL_STATE_IDLE:
                    //Went to idle-  this is the end of a call.  What type depends on previous state(s)
                    if(lastState == TelephonyManager.CALL_STATE_RINGING){
                        //Ring but no pickup-  a miss
                        onMissedCall(savedNumber, callStartTime);
                    }
                    else if(isIncoming){
                        onIncomingCallEnded(savedNumber, callStartTime, new Date());                        
                    }
                    else{
                        onOutgoingCallEnded(savedNumber, callStartTime, new Date());                                                
                    }
                    break;
            }
            lastState = state;
        }

    }



}

【讨论】:

  • +1。感谢这堂课。我会研究它。只是一个问题:你为什么使用抽象类?我不太了解 Java 的这一部分。它是否暗示/提供了什么?
  • 我试过了但是不能用registerReceiver动态注册BR ------ final IntentFilter filter = new IntentFilter(); filter.addAction(ACTION_OUT); filter.addAction(ACTION_IN); this.br_call = new PhonecallReceiver(); this.registerReceiver(this.br_call, filter);. 我的错误:“无法实例化类型 PhonecallReceiver”。
  • 一个抽象类只是意味着它没有被直接实例化——你创建一个孩子并让它覆盖抽象函数。这通常是在您不知道在某些功能中做什么但孩子知道的情况下完成的 - 例如,在这里您需要指定当出现来电、去电等时要做什么。虽然它们都可以是空的什么都不做。
  • 你不能直接实例化,因为它的抽象——创建一个孩子,并覆盖 5 个抽象函数(你只需要真正实现你关心的那些)。然后实例化并注册孩子。 parent 中的代码实际上会完成确定电话呼叫的工作,并在发生时调用这 5 个函数。
  • 感谢您的回答,但正如我所写,我不熟悉抽象概念。例如,即使我非常了解您所说的内容,我仍然无法使用 Activity 中的 registerReceiver 动态注册我的 BR。我知道你试图向我解释,但你能在我的情况下更具体吗?
【解决方案2】:

这是我的更新。

实际上,我尝试了两种方式(一个 BR 与两个 BR)并且由于您的回答,两者都运行良好。此刻一切都不完美。我正在做。我将向您展示我如何处理一个 BR(因为它是我的问题的对象)。

public class SvcCall extends Service 
 {
  Context _context;
  private static final String ACTION_IN = "android.intent.action.PHONE_STATE";
  private static final String ACTION_OUT = "android.intent.action.NEW_OUTGOING_CALL";
  private CallBr br_call;

  @Override
  public void onCreate()
   {
    super.onCreate();
    this._context = getApplicationContext();   
   }

  @Override
  public int onStartCommand(Intent intent, int flags, int startId)
   {
    final IntentFilter filter = new IntentFilter();
    filter.addAction(ACTION_OUT);
    filter.addAction(ACTION_IN); 
    this.br_call = new CallBr();
    this.registerReceiver(this.br_call, filter);
    return (START_STICKY);
   }

  public class CallBr extends BroadcastReceiver  
   {
    Bundle bundle;
    String state;
    String inCall, outCall;
    public boolean wasRinging = false;

   @Override
   public void onReceive(Context context, Intent intent) 
    {
     if (intent.getAction().equals(ACTION_IN))
      {
       if ((bundle = intent.getExtras()) != null)
        {
         state = bundle.getString(TelephonyManager.EXTRA_STATE);
         if (state.equals(TelephonyManager.EXTRA_STATE_RINGING))
          {          
           inCall = bundle.getString(TelephonyManager.EXTRA_INCOMING_NUMBER);
           wasRinging = true;
           Toast.makeText(context, "IN : " + inCall, Toast.LENGTH_LONG).show();        
          }
         else if (state.equals(TelephonyManager.EXTRA_STATE_OFFHOOK))
          {        
           if (wasRinging == true)
            Toast.makeText(context, "ANSWERED", Toast.LENGTH_LONG).show();
          }
         else if (state.equals(TelephonyManager.EXTRA_STATE_IDLE))
          {
           wasRinging = false;
           Toast.makeText(context, "REJECT || DISCO", Toast.LENGTH_LONG).show();
          }
        }
      }
     else if (intent.getAction().equals(ACTION_OUT))
      {
       if ((bundle = intent.getExtras()) != null)
        {  
         outCall = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER);
         Toast.makeText(context, "OUT : " + outCall, Toast.LENGTH_LONG).show();
        }
      }
     }
    }

   @Override
   public IBinder onBind(Intent intent)
    {
     return null;
    }
  }

【讨论】:

    【解决方案3】:
    switch (state) {
    case TelephonyManager.CALL_STATE_IDLE:                      
        //CALL_STATE_IDLE;
        Toast.makeText(getApplicationContext(), "CALL_STATE_IDLE", Toast.LENGTH_LONG).show();
        break;
    case TelephonyManager.CALL_STATE_OFFHOOK:
        //CALL_STATE_OFFHOOK;
        Toast.makeText(getApplicationContext(), "CALL_STATE_OFFHOOK", Toast.LENGTH_LONG).show();
        break;
    case TelephonyManager.CALL_STATE_RINGING:
        //CALL_STATE_RINGING
        Toast.makeText(getApplicationContext(), incomingNumber, Toast.LENGTH_LONG).show();   
        Toast.makeText(getApplicationContext(), "CALL_STATE_RINGING", Toast.LENGTH_LONG).show();
         break;
    default:
         break;
    }
    

    别忘了授权..

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

    【讨论】:

      猜你喜欢
      • 2011-10-05
      • 2012-09-10
      • 2020-10-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多