บทนำ
ตัวอย่างนี้แสดงวิธีส่งข้อความ SMS โดยใช้ SmsManager รองรับการเลือก SIM บนอุปกรณ์ Dual SIM (Android 5.1+ API 22+)
ขั้นตอนที่ 1: สร้างโปรเจกต์
สร้างโปรเจกต์ Android ใหม่ใน Android Studio
ขั้นตอนที่ 2: Layout (activity_main.xml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="SMS Sender (Dual SIM)"
android:textSize="24sp"
android:textStyle="bold"
android:layout_marginBottom="16dp"
android:textColor="@android:color/holo_green_dark" />
<Spinner
android:id="@+id/spinnerSim"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp" />
<EditText
android:id="@+id/etPhoneNumber"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Phone Number"
android:inputType="phone"
android:layout_marginBottom="16dp" />
<EditText
android:id="@+id/etMessage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Message"
android:inputType="textMultiLine"
android:minHeight="100dp"
android:layout_marginBottom="16dp" />
<Button
android:id="@+id/btnSend"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="Send SMS" />
</LinearLayout>
ขั้นตอนที่ 3: MainActivity.kt
import android.Manifest
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.telephony.SubscriptionInfo
import android.telephony.SubscriptionManager
import android.telephony.SmsManager
import android.telephony.TelephonyManager
import android.text.TextUtils
import android.widget.ArrayAdapter
import android.widget.Button
import android.widget.EditText
import android.widget.Spinner
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
class MainActivity : AppCompatActivity() {
private lateinit var spinnerSim: Spinner
private lateinit var etPhoneNumber: EditText
private lateinit var etMessage: EditText
private lateinit var btnSend: Button
private var subscriptionInfoList: List = emptyList()
private val PERMISSION_REQUEST_SEND_SMS = 101
private val PERMISSION_REQUEST_READ_PHONE_STATE = 102
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "SMS Sender"
spinnerSim = findViewById(R.id.spinnerSim)
etPhoneNumber = findViewById(R.id.etPhoneNumber)
etMessage = findViewById(R.id.etMessage)
btnSend = findViewById(R.id.btnSend)
loadSimCards()
setupSendButton()
}
private fun loadSimCards() {
val subscriptionManager = getSystemService(SubscriptionManager::class.java)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (ContextCompat.checkSelfPermission(this, Manifest.permission.READ_PHONE_STATE)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.READ_PHONE_STATE), PERMISSION_REQUEST_READ_PHONE_STATE)
return
}
}
subscriptionInfoList = subscriptionManager.activeSubscriptionInfoList ?: emptyList()
val simNames = subscriptionInfoList.map {
"${it.displayName} (${it.carrierName ?: "Unknown"}) - Slot ${it.simSlotIndex}"
}.toList()
if (simNames.isEmpty()) {
simNames.add("No SIM detected")
}
val adapter = ArrayAdapter(this, android.R.layout.simple_spinner_item, simNames)
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
spinnerSim.adapter = adapter
}
private fun setupSendButton() {
btnSend.setOnClickListener {
sendSms()
}
}
private fun sendSms() {
val phoneNumber = etPhoneNumber.text.toString().trim()
val message = etMessage.text.toString().trim()
if (phoneNumber.isEmpty() || message.isEmpty()) {
Toast.makeText(this, "Please fill all fields", Toast.LENGTH_SHORT).show()
return
}
if (!TextUtils.isDigitsOnly(phoneNumber)) {
Toast.makeText(this, "Invalid phone number", Toast.LENGTH_SHORT).show()
return
}
// ตรวจสอบ Permission ส่ง SMS
if (ContextCompat.checkSelfPermission(this, Manifest.permission.SEND_SMS)
!= PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(Manifest.permission.SEND_SMS), PERMISSION_REQUEST_SEND_SMS)
return
}
// เลือก Subscription ID ตาม SIM ที่เลือก
val selectedIndex = spinnerSim.selectedItemPosition
if (selectedIndex < 0 || selectedIndex >= subscriptionInfoList.size) {
Toast.makeText(this, "Please select a valid SIM", Toast.LENGTH_SHORT).show()
return
}
val selectedSubInfo = subscriptionInfoList[selectedIndex]
val subscriptionId = selectedSubInfo.subscriptionId
// ส่ง SMS ผ่าน SIM ที่เลือก
val smsManager = SmsManager.getSmsManagerForSubscriptionId(subscriptionId)
try {
// สำหรับข้อความยาว (>160 chars) ใช้ sendMultipartTextMessage
if (message.length > 160) {
val parts = smsManager.divideMessage(message)
smsManager.sendMultipartTextMessage(phoneNumber, null, parts, null, null)
} else {
smsManager.sendTextMessage(phoneNumber, null, message, null, null)
}
Toast.makeText(this, "SMS sent via ${selectedSubInfo.displayName}", Toast.LENGTH_SHORT).show()
etMessage.text.clear()
} catch (e: Exception) {
Toast.makeText(this, "Failed to send: ${e.message}", Toast.LENGTH_LONG).show()
e.printStackTrace()
}
}
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
when (requestCode) {
PERMISSION_REQUEST_SEND_SMS -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
sendSms()
} else {
Toast.makeText(this, "SMS permission required", Toast.LENGTH_SHORT).show()
}
}
PERMISSION_REQUEST_READ_PHONE_STATE -> {
if (grantResults.isNotEmpty() && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
loadSimCards()
}
}
}
}
}
ขั้นตอนที่ 4: Manifest
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="https://schemas.android.com/apk/res/android"
package="com.example.smssender">
<uses-permission android:name="android.permission.SEND_SMS" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<!-- สำหรับ Android 10+ อาจต้องการ READ_PRIVILEGED_PHONE_STATE (System only) -->
<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">
<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>
SmsManager Methods สำคัญ
| Method | คำอธิบาย |
|---|---|
getDefault() | ได้ SmsManager ของ SIM เริ่มต้น (Default) |
getSmsManagerForSubscriptionId(subId) | ได้ SmsManager ของ SIM เฉพาะ (Dual SIM) - API 22+ |
sendTextMessage() | ส่ง SMS ข้อความสั้น (≤160 chars GSM-7) |
sendMultipartTextMessage() | ส่ง SMS ยาว (Concatenated SMS) |
divideMessage(text) | แบ่งข้อความยาวเป็นส่วนๆ อัตโนมัติ |
PendingIntent สำหรับติดตามสถานะ (Delivery/Sent Reports)
val sentIntent = PendingIntent.getBroadcast(this, 0, Intent("SMS_SENT"), PendingIntent.FLAG_IMMUTABLE)
val deliveredIntent = PendingIntent.getBroadcast(this, 0, Intent("SMS_DELIVERED"), PendingIntent.FLAG_IMMUTABLE)
// Register Receivers
registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
when (resultCode) {
Activity.RESULT_OK -> Toast.makeText(context, "SMS Sent", Toast.LENGTH_SHORT).show()
SmsManager.RESULT_ERROR_GENERIC_FAILURE -> Toast.makeText(context, "Generic failure", Toast.LENGTH_SHORT).show()
SmsManager.RESULT_ERROR_NO_SERVICE -> Toast.makeText(context, "No service", Toast.LENGTH_SHORT).show()
SmsManager.RESULT_ERROR_NULL_PDU -> Toast.makeText(context, "Null PDU", Toast.LENGTH_SHORT).show()
SmsManager.RESULT_ERROR_RADIO_OFF -> Toast.makeText(context, "Radio off", Toast.LENGTH_SHORT).show()
}
}
}, IntentFilter("SMS_SENT"))
registerReceiver(object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
Toast.makeText(context, "SMS Delivered", Toast.LENGTH_SHORT).show()
}
}, IntentFilter("SMS_DELIVERED"))
// ส่งพร้อม PendingIntent
smsManager.sendTextMessage(phoneNumber, null, message, sentIntent, deliveredIntent)
หมายเหตุ
- ต้องทดสอบบนอุปกรณ์จริงที่มี SIM การ์ด (Emulator ส่ง SMS ไม่ได้จริง)
- Android 4.4+ (KitKat) ต้องเป็น Default SMS App ถ้าต้องการส่งโดยไม่ผ่าน Intent
- ค่าใช้จ่าย SMS ขึ้นกับแผนผู้ให้บริการ