การเข้าถึงหมายเลขโทรศัพท์ของผู้ใช้ด้วย TelephonyManager
บทความนี้แนะนำวิธีดึงหมายเลขโทรศัพท์ของอุปกรณ์ Android โดยใช้ TelephonyManager พร้อมจัดการ Runtime Permission ให้ถูกต้องตามมาตรฐาน Android 6.0+ (API 23+)
⚠️ ข้อจำกัดสำคัญ
หมายเลขโทรศัพท์อาจไม่สามารถดึงได้ในกรณีต่อไปนี้:
- SIM ไม่ได้ลงทะเบียนกับเครือข่าย
- ผู้ใช้ใช้ eSIM หรือ Dual SIM
- Carrier ไม่สนับสนุนการรายงานหมายเลข
- Android 10+ (API 29+) จำกัดสิทธิ์ READ_PHONE_STATE อย่างเข้มงวด
- อุปกรณ์ไม่มี SIM (Wi-Fi only tablet)
ควรมีแผน B เช่น ให้ผู้ใช้กรอกหมายเลขเอง
ขั้นตอนที่ 1: สร้างโปรเจกต์และเพิ่ม Permission ใน AndroidManifest.xml
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.phonenumberdemo">
<!-- Permissions สำหรับอ่านหมายเลขโทรศัพท์ -->
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.READ_PHONE_NUMBERS" />
<!-- สำหรับ Android 10+ ต้องการเพิ่ม -->
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE"
tools:ignore="ProtectedPermissions" />
<application ...>
...
</application>
</manifest>
ขั้นตอนที่ 2: Layout - activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="https://schemas.android.com/apk/res/android"
xmlns:app="https://schemas.android.com/apk/res-auto"
xmlns:tools="https://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="24dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="หมายเลขโทรศัพท์ของคุณ"
android:textSize="18sp"
android:textColor="#757575" />
<TextView
android:id="@+id/tvPhoneNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="--"
android:textSize="28sp"
android:textStyle="bold"
android:textColor="#212121" />
<Button
android:id="@+id/btnRetry"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:text="ลองอีกครั้ง"
android:visibility="gone" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
ขั้นตอนที่ 3: MainActivity.java - รองรับ Android 6.0+
package com.example.phonenumberdemo;
import android.Manifest;
import android.content.Context;
import android.content.pm.PackageManager;
import android.os.Build;
import android.os.Bundle;
import android.telephony.TelephonyManager;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
public class MainActivity extends AppCompatActivity {
private static final int PERMISSION_REQUEST_CODE = 100;
private static final String[] REQUIRED_PERMISSIONS = {
Manifest.permission.READ_PHONE_STATE,
Manifest.permission.READ_PHONE_NUMBERS
};
private TextView tvPhoneNumber;
private Button btnRetry;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvPhoneNumber = findViewById(R.id.tvPhoneNumber);
btnRetry = findViewById(R.id.btnRetry);
btnRetry.setOnClickListener(v -> checkPermissionsAndFetchNumber());
checkPermissionsAndFetchNumber();
}
private void checkPermissionsAndFetchNumber() {
boolean allGranted = true;
for (String permission : REQUIRED_PERMISSIONS) {
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
fetchPhoneNumber();
btnRetry.setVisibility(View.GONE);
} else {
requestPermissions();
}
}
private void requestPermissions() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions(REQUIRED_PERMISSIONS, PERMISSION_REQUEST_CODE);
}
}
private void fetchPhoneNumber() {
TelephonyManager telephonyManager = (TelephonyManager) getSystemService(Context.TELEPHONY_SERVICE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) != PackageManager.PERMISSION_GRANTED) {
return;
}
String phoneNumber = null;
// Android 10+ (API 29+) ใช้ getLine1Number() อาจคืนค่า null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// ต้องการ READ_PRIVILEGED_PHONE_STATE (system app เท่านั้น)
// สำหรับแอปทั่วไป ใช้ SubscriptionManager แทน
phoneNumber = telephonyManager.getLine1Number();
} else {
phoneNumber = telephonyManager.getLine1Number();
}
if (phoneNumber != null && !phoneNumber.isEmpty()) {
tvPhoneNumber.setText(formatPhoneNumber(phoneNumber));
} else {
tvPhoneNumber.setText("ไม่สามารถดึงหมายเลขได้");
btnRetry.setVisibility(View.VISIBLE);
showInfoDialog();
}
}
private String formatPhoneNumber(String number) {
// จัดรูปแบบหมายเลขไทย (ตัวอย่าง)
if (number.startsWith("+66")) {
return "0" + number.substring(3);
} else if (number.startsWith("66")) {
return "0" + number.substring(2);
}
return number;
}
private void showInfoDialog() {
new androidx.appcompat.app.AlertDialog.Builder(this)
.setTitle("ไม่พบหมายเลขโทรศัพท์")
.setMessage("อาจเกิดจาก:\n• SIM ไม่ได้ลงทะเบียน\n• Carrier ไม่สนับสนุน\n• Android 10+ จำกัดสิทธิ์\n\nแนะนำให้ผู้ใช้กรอกหมายเลขเอง")
.setPositiveButton("ตกลง", null)
.show();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == PERMISSION_REQUEST_CODE) {
boolean allGranted = true;
for (int result : grantResults) {
if (result != PackageManager.PERMISSION_GRANTED) {
allGranted = false;
break;
}
}
if (allGranted) {
fetchPhoneNumber();
} else {
tvPhoneNumber.setText("ไม่ได้รับอนุญาต");
btnRetry.setVisibility(View.VISIBLE);
Toast.makeText(this, "ต้องการสิทธิ์อ่านสถานะโทรศัพท์", Toast.LENGTH_LONG).show();
}
}
}
}
วิธีทางเลือกสำหรับ Android 10+ (API 29+)
ตั้งแต่ Android 10 การเข้าถึงหมายเลขโทรศัพท์ถูกจำกัด ใช้ SubscriptionManager แทน:
// ต้องการ permission: READ_PHONE_STATE
SubscriptionManager subManager = (SubscriptionManager) getSystemService(Context.TELEPHONY_SUBSCRIPTION_SERVICE);
if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE) == PackageManager.PERMISSION_GRANTED) {
List<SubscriptionInfo> subscriptions = subManager.getActiveSubscriptionInfoList();
if (subscriptions != null) {
for (SubscriptionInfo subInfo : subscriptions) {
String number = subInfo.getNumber();
if (number != null && !number.isEmpty()) {
// พบหมายเลข
break;
}
}
}
}
Best Practices
- ไม่บังคับ Permission: ให้ผู้ใช้เลือกได้ว่าให้สิทธิ์หรือไม่
- มี Fallback: ให้ผู้ใช้กรอกหมายเลขเองได้
- อธิบายเหตุผล: ใช้ Dialog/Overscroll บอกว่าทำไมต้องการสิทธิ์
- ทดสอบหลายอุปกรณ์: พฤติกรรมต่างกันไปตาม Carrier และ OEM
สรุป
การดึงหมายเลขโทรศัพท์ใน Android ต้องจัดการ Runtime Permission อย่างถูกต้อง และควรคาดการณ์กรณีที่ดึงไม่ได้ เพื่อประสบการณ์ผู้ใช้ที่ดีที่สุด