Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> Android

Nhận và kết thúc cuộc gọi trong Android

Nhận và kết thúc cuộc gọi trong Android

Hướng dẫn này minh họa cách phát hiện cuộc gọi đến và xử lý (nhận/kết thúc) bằng BroadcastReceiver. Lưu ý: Từ Android 8.0 (API 26), các hạn chế nghiêm ngặt được áp dụng cho BroadcastReceiver tĩnh.

Bước 1: Tạo dự án mới

Mở Android Studio, chọn File → New Project và điền thông tin cần thiết.

Bước 2: Thiết kế giao diện (activity_main.xml)

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="https://schemas.android.com/apk/res/android"
    xmlns:tools="https://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="MainActivity">
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        android:text="Pick up and hung up calls"
        android:textSize="24sp"
        android:textStyle="bold|italic"/>
</RelativeLayout>

Bước 3: Code MainActivity.java (Xin quyền READ_PHONE_STATE)

import android.Manifest;
import android.content.pm.PackageManager;
import android.support.v4.app.ActivityCompat;
import android.support.v4.content.ContextCompat;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.Toast;

public class MainActivity extends AppCompatActivity {
    private static final int PHONE_PERMISSION_REQUEST_CODE = 1;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        requestPhonePermission();
    }
    
    private void requestPhonePermission() {
        if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
                != PackageManager.PERMISSION_GRANTED) {
            
            if (ActivityCompat.shouldShowRequestPermissionRationale(this, 
                    Manifest.permission.READ_PHONE_STATE)) {
                // Giải thích lý do cần quyền
                ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_PHONE_STATE}, PHONE_PERMISSION_REQUEST_CODE);
            } else {
                ActivityCompat.requestPermissions(this,
                    new String[]{Manifest.permission.READ_PHONE_STATE}, PHONE_PERMISSION_REQUEST_CODE);
            }
        } else {
            // Đã có quyền
            Toast.makeText(this, "Permission already granted", Toast.LENGTH_SHORT).show();
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        
        if (requestCode == PHONE_PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                Toast.makeText(this, "Permission Granted", Toast.LENGTH_SHORT).show();
            } else {
                Toast.makeText(this, "Permission Denied", Toast.LENGTH_SHORT).show();
            }
        }
    }
}

Bước 4: Tạo BroadcastReceiver (CallManager.java)

Chuột phải package → New → Java Class → tên CallManager:

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.TelephonyManager;
import android.widget.Toast;

public class CallManager extends BroadcastReceiver {
    @Override
    public void onReceive(Context context, Intent intent) {
        try {
            String state = intent.getStringExtra(TelephonyManager.EXTRA_STATE);
            
            if (TelephonyManager.EXTRA_STATE_RINGING.equals(state)) {
                // Cuộc gọi đến
                String incomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER);
                Toast.makeText(context, "Incoming call: " + incomingNumber, Toast.LENGTH_SHORT).show();
                
                // TODO: Xử lý nhận cuộc gọi (cần quyền MODIFY_PHONE_STATE - chỉ system app)
            } else if (TelephonyManager.EXTRA_STATE_OFFHOOK.equals(state)) {
                // Đang trong cuộc gọi
                Toast.makeText(context, "Call answered", Toast.LENGTH_SHORT).show();
            } else if (TelephonyManager.EXTRA_STATE_IDLE.equals(state)) {
                // Cuộc gọi kết thúc
                Toast.makeText(context, "Call ended", Toast.LENGTH_SHORT).show();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Bước 5: Đăng ký Receiver trong AndroidManifest.xml

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android" package="app.com.sample">
    <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
    <uses-permission android:name="android.permission.ANSWER_PHONE_CALLS"/> <!-- Android 8.0+ -->
    
    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        
        <receiver android:name=".CallManager" android:enabled="true" android:exported="true">
            <intent-filter>
                <action android:name="android.intent.action.PHONE_STATE"/>
            </intent-filter>
        </receiver>
        
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />
                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>
    </application>
</manifest>

Chạy ứng dụng

Cài đặt app, cho phép quyền READ_PHONE_STATE. Khi có cuộc gọi đến, Toast sẽ hiển thị số điện thoại và trạng thái.

Hạn chế quan trọng (Android 8.0+):
  • Không thể đăng ký BroadcastReceiver PHONE_STATE trong Manifest (implicit broadcast)
  • Phải đăng ký runtime: registerReceiver(receiver, new IntentFilter(TelephonyManager.ACTION_PHONE_STATE_CHANGED))
  • Quyền ANSWER_PHONE_CALLS (API 26+) cho phép nhận cuộc gọi tự động
  • Quyền MODIFY_PHONE_STATE chỉ dành cho system apps
  • Để kết thúc cuộc gọi: cần TelecomManager (API 26+) hoặc reflection (không khuyến khích)

Cách hiện đại (Android 8.0+): Sử dụng CallScreeningService

// Manifest
<service android:name=".ScreeningService"
    android:permission="android.permission.BIND_SCREENING_SERVICE">
    <intent-filter>
        <action android:name="android.telecom.CallScreeningService"/>
    </intent-filter>
</service>

// ScreeningService.kt
class ScreeningService : CallScreeningService() {
    override fun onScreenCall(callDetails: Call.Details) {
        // Xử lý chặn/nhận cuộc gọi
    }
}