How to detect incoming calls, in an Android device?

前端 未结 10 1585
一向
一向 2020-11-22 02:27

I\'m trying to make an app like, when a call comes to the phone I want to detect the number. Below is what I tried, but it\'s not detecting incoming calls.

I want t

相关标签:
10条回答
  • 2020-11-22 02:31

    UPDATE: The really awesome code posted by Gabe Sechan no longer works unless you explicitly request the user to grant the necessary permissions. Here is some code that you can place in your main activity to request these permissions:

        if (getApplicationContext().checkSelfPermission(Manifest.permission.READ_PHONE_STATE)
                != PackageManager.PERMISSION_GRANTED) {
            // Permission has not been granted, therefore prompt the user to grant permission
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_PHONE_STATE},
                    MY_PERMISSIONS_REQUEST_READ_PHONE_STATE);
        }
    
        if (getApplicationContext().checkSelfPermission(Manifest.permission.PROCESS_OUTGOING_CALLS)
                != PackageManager.PERMISSION_GRANTED) {
            // Permission has not been granted, therefore prompt the user to grant permission
            ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.PROCESS_OUTGOING_CALLS},
                    MY_PERMISSIONS_REQUEST_PROCESS_OUTGOING_CALLS);
        }
    

    ALSO: As someone mentioned in a comment below Gabe's post, you have to add a little snippet of code, android:enabled="true, to the receiver in order to detect incoming calls when the app is not currently running in the foreground:

        <!--This part is inside the application-->
        <receiver android:name=".CallReceiver" android:enabled="true">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE" />
            </intent-filter>
            <intent-filter>
                <action android:name="android.intent.action.NEW_OUTGOING_CALL" />
            </intent-filter>
        </receiver>
    
    0 讨论(0)
  • 2020-11-22 02:36

    With Android P - Api Level 28: You need to get READ_CALL_LOG permission

    Restricted access to call logs

    Android P moves the CALL_LOG, READ_CALL_LOG, WRITE_CALL_LOG, and PROCESS_OUTGOING_CALLS permissions from the PHONE permission group to the new CALL_LOG permission group. This group gives users better control and visibility to apps that need access to sensitive information about phone calls, such as reading phone call records and identifying phone numbers.

    To read numbers from the PHONE_STATE intent action, you need both the READ_CALL_LOG permission and the READ_PHONE_STATE permission. To read numbers from onCallStateChanged(), you now need the READ_CALL_LOG permission only. You no longer need the READ_PHONE_STATE permission.

    0 讨论(0)
  • 2020-11-22 02:37

    Here is a simple method which can avoid the use of PhonestateListener and other complications.
    So here we are receiving the 3 events from android such as RINGING,OFFHOOK and IDLE. And in order to get the all possible state of call,we need to define our own states like RINGING, OFFHOOK, IDLE, FIRST_CALL_RINGING, SECOND_CALL_RINGING. It can handle every states in a phone call.
    Please think in a way that we are receiving events from android and we will define our on call states. See the code.

    public class CallListening  extends BroadcastReceiver {
        private static final String TAG ="broadcast_intent";
        public static String incoming_number;
        private String current_state,previus_state,event;
        public static Boolean dialog= false;
        private Context context;
        private SharedPreferences sp,sp1;
        private SharedPreferences.Editor spEditor,spEditor1;
        public void onReceive(Context context, Intent intent) {
            //Log.d("intent_log", "Intent" + intent);
            dialog=true;
            this.context = context;
            event = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            incoming_number = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
            Log.d(TAG, "The received event : "+event+", incoming_number : " + incoming_number);
            previus_state = getCallState(context);
            current_state = "IDLE";
            if(incoming_number!=null){
                updateIncomingNumber(incoming_number,context);
            }else {
                incoming_number=getIncomingNumber(context);
            }
            switch (event) {
                case "RINGING":
                    Log.d(TAG, "State : Ringing, incoming_number : " + incoming_number);
                if((previus_state.equals("IDLE")) || (previus_state.equals("FIRST_CALL_RINGING"))){
                        current_state ="FIRST_CALL_RINGING";
                    }
                    if((previus_state.equals("OFFHOOK"))||(previus_state.equals("SECOND_CALL_RINGING"))){
                        current_state = "SECOND_CALL_RINGING";
                    }
    
                    break;
                case "OFFHOOK":
                    Log.d(TAG, "State : offhook, incoming_number : " + incoming_number);
                    if((previus_state.equals("IDLE")) ||(previus_state.equals("FIRST_CALL_RINGING")) || previus_state.equals("OFFHOOK")){
                        current_state = "OFFHOOK";
                    }
                    if(previus_state.equals("SECOND_CALL_RINGING")){
                        current_state ="OFFHOOK";
                        startDialog(context);
                    }
                    break;
                case "IDLE":
                    Log.d(TAG, "State : idle and  incoming_number : " + incoming_number);
                    if((previus_state.equals("OFFHOOK")) || (previus_state.equals("SECOND_CALL_RINGING")) || (previus_state.equals("IDLE"))){
                        current_state="IDLE";
                    }
                    if(previus_state.equals("FIRST_CALL_RINGING")){
                        current_state = "IDLE";
                        startDialog(context);
                    }
                    updateIncomingNumber("no_number",context);
                    Log.d(TAG,"stored incoming number flushed");
                    break;
            }
            if(!current_state.equals(previus_state)){
                Log.d(TAG, "Updating  state from "+previus_state +" to "+current_state);
                updateCallState(current_state,context);
    
            }
        }
        public void startDialog(Context context) {
            Log.d(TAG,"Starting Dialog box");
            Intent intent1 = new Intent(context, NotifyHangup.class);
            intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            context.startActivity(intent1);
    
        }
        public void updateCallState(String state,Context context){
            sp = PreferenceManager.getDefaultSharedPreferences(context);
            spEditor = sp.edit();
            spEditor.putString("call_state", state);
            spEditor.commit();
            Log.d(TAG, "state updated");
    
        }
        public void updateIncomingNumber(String inc_num,Context context){
            sp = PreferenceManager.getDefaultSharedPreferences(context);
            spEditor = sp.edit();
            spEditor.putString("inc_num", inc_num);
            spEditor.commit();
            Log.d(TAG, "incoming number updated");
        }
        public String getCallState(Context context){
            sp1 = PreferenceManager.getDefaultSharedPreferences(context);
            String st =sp1.getString("call_state", "IDLE");
            Log.d(TAG,"get previous state as :"+st);
            return st;
        }
        public String getIncomingNumber(Context context){
            sp1 = PreferenceManager.getDefaultSharedPreferences(context);
            String st =sp1.getString("inc_num", "no_num");
            Log.d(TAG,"get incoming number as :"+st);
            return st;
        }
    }
    
    0 讨论(0)
  • 2020-11-22 02:37

    Please use the below code. It will help you to get the incoming number with other call details.

    activity_main.xml

    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity" >
    
    <TextView
        android:id="@+id/call"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_centerVertical="true"
        android:text="@string/hello_world" />
    
    </RelativeLayout>
    

    MainActivity.java

    public class MainActivity extends Activity {
    
    private static final int MISSED_CALL_TYPE = 0;
    private TextView txtcall;
    
    @Override
        protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
    
        txtcall = (TextView) findViewById(R.id.call);
    
        StringBuffer sb = new StringBuffer();
        Cursor managedCursor = managedQuery(CallLog.Calls.CONTENT_URI, null,
                null, null, null);
        int number = managedCursor.getColumnIndex(CallLog.Calls.NUMBER);
        int type = managedCursor.getColumnIndex(CallLog.Calls.TYPE);
        int date = managedCursor.getColumnIndex(CallLog.Calls.DATE);
        int duration = managedCursor.getColumnIndex(CallLog.Calls.DURATION);
        sb.append("Call Details :");
        while (managedCursor.moveToNext()) {
            String phNumber = managedCursor.getString(number);
            String callType = managedCursor.getString(type);
            String callDate = managedCursor.getString(date);
            Date callDayTime = new Date(Long.valueOf(callDate));
            String callDuration = managedCursor.getString(duration);
            String dir = null;
            int dircode = Integer.parseInt(callType);
            switch (dircode) {
    
            case CallLog.Calls.OUTGOING_TYPE:
                dir = "OUTGOING";
                break;
    
            case CallLog.Calls.INCOMING_TYPE:
                dir = "INCOMING";
                break;
    
            case CallLog.Calls.MISSED_TYPE:
                dir = "MISSED";
                break;
            }
            sb.append("\nPhone Number:--- " + phNumber + " \nCall Type:--- "
                    + dir + " \nCall Date:--- " + callDayTime
                    + " \nCall duration in sec :--- " + callDuration);
            sb.append("\n----------------------------------");
        }
        managedCursor.close();
        txtcall.setText(sb);
    }
    
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.activity_main, menu);
        return true;
    }
    
    } 
    

    and in your manifest request for following permissions:

    <uses-permission android:name="android.permission.READ_CONTACTS"/>
    <uses-permission android:name="android.permission.READ_LOGS"/>
    
    0 讨论(0)
  • 2020-11-22 02:38

    Just to update Gabe Sechan's answer. If your manifest asks for permissions to READ_CALL_LOG and READ_PHONE_STATE, onReceive will called TWICE. One of which has EXTRA_INCOMING_NUMBER in it and the other doesn't. You have to test which has it and it can occur in any order.

    https://developer.android.com/reference/android/telephony/TelephonyManager.html#ACTION_PHONE_STATE_CHANGED

    0 讨论(0)
  • 2020-11-22 02:38

    You need a BroadcastReceiver for ACTION_PHONE_STATE_CHANGED This will call your received whenever the phone-state changes from idle, ringing, offhook so from the previous value and the new value you can detect if this is an incoming/outgoing call.

    Required permission would be:

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

    But if you also want to receive the EXTRA_INCOMING_NUMBER in that broadcast, you'll need another permission: "android.permission.READ_CALL_LOG"

    And the code something like this:

    val receiver: BroadcastReceiver = object : BroadcastReceiver() {
        override fun onReceive(context: Context, intent: Intent) {
            Log.d(TAG, "onReceive")
        }
    }
    
    override fun onResume() {
        val filter = IntentFilter()
        filter.addAction("android.intent.action.PHONE_STATE")
        registerReceiver(receiver, filter)
        super.onResume()
    }
    
    override fun onPause() {
        unregisterReceiver(receiver)
        super.onPause()
    }
    

    and in receiver class, we can get current state by reading intent like this:

    intent.extras["state"]
    

    the result of extras could be:

    RINGING -> If your phone is ringing

    OFFHOOK -> If you are talking with someone (Incoming or Outcoming call)

    IDLE -> if call ended (Incoming or Outcoming call)

    With PHONE_STATE broadcast we don't need to use PROCESS_OUTGOING_CALLS permission or deprecated NEW_OUTGOING_CALL action.

    0 讨论(0)
提交回复
热议问题