12/08/2018, 16:45

Android Telephony Api thực hiện gọi điện và giám sát cuộc gọi

Trong bài lần này mình sẽ giới thiệu cho các bạn các về Telephony Api của Android với các ý sau: Tạo cuộc gọi trên thiết bị android Giám sát cuộc gọi đến và đi từ thiết bị. Trong android để tạo cuộc gọi bạn có thể sử dụng intent với 2 action là ACTION_DIAL và ACTION_CALL. Cả 2 actino này ...

Trong bài lần này mình sẽ giới thiệu cho các bạn các về Telephony Api của Android với các ý sau:

  • Tạo cuộc gọi trên thiết bị android
  • Giám sát cuộc gọi đến và đi từ thiết bị.

Trong android để tạo cuộc gọi bạn có thể sử dụng intent với 2 action là ACTION_DIAL và ACTION_CALL. Cả 2 actino này đều sử dụng URI scheme sau Uri.parse("tel:" + phoneNumber).

  • ACTION_DIAL: chỉ sao chép phoneNumber và điền vào màn hình gọi điện chứ KHÔNG gọi trực tiếp.
    Intent intent = new Intent(Intent.ACTION_DIAL);
    intent.setData(Uri.parse("tel:" + phoneNumber));
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }
  • ACTION_CALL: sẽ gọi trực tiếp đến phoneNumber bạn truyền vào. ACTION_CALL yêu cầu bạn cần đăng ký quyền <uses-permission android:name="android.permission.CALL_PHONE"/> trong AndroidManifest.xml.
    Intent intent = new Intent(Intent.ACTION_CALL);
    intent.setData(Uri.parse("tel:" + phoneNumber));
    if (intent.resolveActivity(getPackageManager()) != null) {
        startActivity(intent);
    }

Lưu ý: Nếu bạn thực hiện intent với ACTION_CALL trên Android 6.0 trở lên thì hãy đảm bảo rằng đã check Runtime Permission để tránh trường hợp bị crash do không có quyền nên xảy ra SecurityException.

    private void callPhone(String phoneNumber) {
        if (!isValidPhoneNumber(phoneNumber)) return;

        if (ContextCompat.checkSelfPermission(this, Manifest.permission.CALL_PHONE)
                != PackageManager.PERMISSION_GRANTED) {
            // request the permission.
            ActivityCompat.requestPermissions(this, new String[] { Manifest.permission.CALL_PHONE },
                    REQUEST_CALL_PHONE);
        } else {
            Intent intent = new Intent(Intent.ACTION_CALL);
            intent.setData(Uri.parse("tel:" + phoneNumber));
            if (intent.resolveActivity(getPackageManager()) != null) {
                startActivity(intent);
            }
        }
    }
    
    @Override
    public void onRequestPermissionsResult(int requestCode, String permissions[],
            int[] grantResults) {
        switch (requestCode) {
            case REQUEST_CALL_PHONE:
                // If request is cancelled, the result arrays are empty.
                if (grantResults.length > 0
                        && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    callPhone(phoneNumber.get());
                }
                break;
        }
    }
  • Hơn nữa nếu ứng dụng của bạn có ô nhập phoneNumber là edittext thì hãy đảm bảo các kí tự được nhập là trong giới hạn cho phép qua hàm sau:
    android:digits="+0987654321"
    android:inputType="phone"
  • Kể cả khi bạn đã thêm format lúc nhập số điện thoại thì cũng đừng quên validate lại số điện thoại trước khi thực hiện dial hay call với một regex có sẵn của android như sau:
    android.util.Patterns.PHONE.matcher(phoneNumber).matches()

Ở phần này chúng ta sẽ tìm hiểu cách giám sát sự kiện cuộc gọi trong hệ thống Android. Một chiếc điện thoại có thể ở trong ba trạng thái sau:

  • idle (rảnh rỗi) khi không được sử dụng
  • ringing (đổ chuông) khi có một cuộc gọi đến
  • off-hook (bắt máy) khi cuộc gọi được trả lời

Để có thể thực hiện giám sát chúng ta cần có quyền READ_PHONE_STATE. Các bạn thêm dòng sau vào AndroidManifest.xml

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

Chúng ta tạo một object PhoneStateListener và override hàm onCallStateChanged(). Chúng ta sẽ xử lý việc thay đổi trạng thái của cuộc gọi bằng việc hiển thị một Toast. Lưu ý rằng chúng ta cũng có thể truy cập vào số điện thoại đến khi mà hàm này được gọi.

PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
    @Override
    public void onCallStateChanged(int state, String incomingNumber) {
        super.onCallStateChanged(state, incomingNumber);
 
        switch (state) {
            case TelephonyManager.CALL_STATE_IDLE:
                Toast.makeText(MainActivity.this, "CALL_STATE_IDLE", Toast.LENGTH_SHORT).show();
                break;
            case TelephonyManager.CALL_STATE_RINGING:
                Toast.makeText(MainActivity.this, "CALL_STATE_RINGING", Toast.LENGTH_SHORT).show();
                break;
            case TelephonyManager.CALL_STATE_OFFHOOK:
                Toast.makeText(MainActivity.this, "CALL_STATE_OFFHOOK", Toast.LENGTH_SHORT).show();
                break;
        }
    }
};

Tuỳ thuộc vào ứng dụng bạn cần mà bạn có thể override các methods sau: onCellInfoChanged(), onCallForwardingIndicatorChanged(), onCellLocationChanged(), hoặc onSignalStrengthChanged().

Lắng nghe trạng thái cuộc gọi

Để bắt đầu thực hiện việc lắng nghe trạng thái cuộc gọi, chúng ta cần get TelephonyManager từ system service và khởi tạo nó tỏng onCreate().

private TelephonyManager mTelephonyManager;
@Override
protected void onCreate(Bundle savedInstanceState) {
   // ... 
   mTelephonyManager = (TelephonyManager) getSystemService(getApplicationContext().TELEPHONY_SERVICE);
}

Trong hàm onResume() chúng ta có thể bắt đầu lắng nghe bằng cách gọi hàm TelephonyManager.listen(), với tham số là PhoneStateListener và LISTEN_CALL_STATE. Chúng ta thực hiện dừng việc lắng nghe trong hàn onStop() bằng cách truyền LISTEN_NONE.

@Override
protected void onResume() {
    super.onResume();
    mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
}
@Override
protected void onStop() {
    super.onStop();
    mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE);
}

Ngoài ra còn có các lựa chọn lắng nghe điện thoại khác như: LISTEN_CELL_LOCATION, LISTEN_SIGNAL_STRENGTH, LISTEN_CALL_FORWARDING_INDICATOR, và LISTEN_CELL_INFO.

Với cách này thì quá tình giám sát chỉ hoạt động khi mà ứng dụng chạy foreground. Để thực hiện việc này ở backgrouund (khi ứng dụng của chúng ta không hoạt động), chúng ta cần tạo ra một BroadcastReceiver thì chúng ta mới có thể vẫn giám sát được trạng thái gọi điện dù cho ứng dụng không chạy.

Hiện tại chúng ta mới chỉ giám sát cuộc gọi đến. Để giám sát cuộc gọi đi thì chúng ta cần thêm quyền sau vào file AndroidManifest.xml

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

Tạo một BroadcastReceiver

Cũng như phần trước là chúng ta sẽ tạo một event listener để giám sát thay đổi trạng thái cuộc gọi của điện thoại. Điểm chính ở đây là lần này chúng ta kế thừa BroadcastReceiver để chúng ta có thể lắng nghe trạng thái cuộc gọi cả khi ứng dụng không hề chạy. Đảm bảo rằng không đăng kí listener nhiều hơn một lần.

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.telephony.PhoneStateListener;
import android.telephony.TelephonyManager;
import android.widget.Toast;
 
public class PhoneCallStateReceiver extends BroadcastReceiver {
    private TelephonyManager mTelephonyManager;
    public static boolean isListening = false;
 
    @Override
    public void onReceive(final Context context, Intent intent) {
 
        mTelephonyManager = (TelephonyManager) context.getSystemService(context.TELEPHONY_SERVICE);
 
        PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
            @Override
            public void onCallStateChanged(int state, String incomingNumber) {
                super.onCallStateChanged(state, incomingNumber);
 
                switch (state) {
                    case TelephonyManager.CALL_STATE_IDLE:
                        Toast.makeText(context, "CALL_STATE_IDLE", Toast.LENGTH_SHORT).show();
                        break;
                    case TelephonyManager.CALL_STATE_RINGING:
                        Toast.makeText(context, "CALL_STATE_RINGING", Toast.LENGTH_SHORT).show();
                        break;
                    case TelephonyManager.CALL_STATE_OFFHOOK:
                        Toast.makeText(context, "CALL_STATE_OFFHOOK", Toast.LENGTH_SHORT).show();
                        break;
                }
            }
        };
 
        if(!isListening) {
            mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE);
            isListening = true;
        }
    }
}

Sửa AndroidManifest.xml

Một broadcast receiver hoạt động chỉ khi nó đã được đăng ký. Chúng ta cần nói cho hệ thống Android biết bằng cách đăng ký broadcast receiver của chúng ta trong AndroidManifest.xml như sau

<receiver android:name=".PhoneCallStateReceiver">
        <intent-filter>
            <action android:name="android.intent.action.PHONE_STATE"/>
        </intent-filter>
</receiver>

Giám sát cuộc gọi đi

Với các cuộc gọi đi, bạn cần thêm action <action android:name="android.intent.action.NEW_OUTGOING_CALL"/> vào thẻ <intent-filter>

Để có thể get số điện thoại đang dùng để gọi đi trong hàm onReceive(Context, Intent) chúng ta lấy số từ intent thông qua extra. Để chặn một cuộc ogij đi, chúng ta có thể gọi hàm setResultData() và truyền vào tham số null. resultData được sử dụng như là số thực để thực hiện cuộc gọi.

@Override
public void onReceive(final Context context, Intent intent) {
    // for outgoing call
    String outgoingPhoneNo = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER).toString();
    // prevent outgoing call
    setResultData(null); 
}

How to Make Calls and Use SMS in Android Apps https://code.tutsplus.com/tutorials/how-to-make-calls-and-use-sms-in-android-apps--cms-28168

0