Computer >> บทช่วยสอนคอมพิวเตอร์ >  >> การเขียนโปรแกรม >> Android

วิธีดึงหมายเลขโทรศัพท์ใน Android

การเข้าถึงหมายเลขโทรศัพท์ของผู้ใช้ด้วย 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

  1. ไม่บังคับ Permission: ให้ผู้ใช้เลือกได้ว่าให้สิทธิ์หรือไม่
  2. มี Fallback: ให้ผู้ใช้กรอกหมายเลขเองได้
  3. อธิบายเหตุผล: ใช้ Dialog/Overscroll บอกว่าทำไมต้องการสิทธิ์
  4. ทดสอบหลายอุปกรณ์: พฤติกรรมต่างกันไปตาม Carrier และ OEM

สรุป

การดึงหมายเลขโทรศัพท์ใน Android ต้องจัดการ Runtime Permission อย่างถูกต้อง และควรคาดการณ์กรณีที่ดึงไม่ได้ เพื่อประสบการณ์ผู้ใช้ที่ดีที่สุด