جلسه 10 ارسال و دریافت پیامک با SMS APIاندروید
مقدمه
پیامک و اهمیت آن در ارتباطات
پیامک (SMS - Short Message Service) یکی از قدیمیترین و پرکاربردترین روشهای ارتباطی در دنیای موبایل است. با وجود ظهور پیامرسانهای پیشرفتهتر، پیامک همچنان به دلیل دسترسی گسترده، سادگی و قابلیت اطمینان در مواقع اضطراری یا در شرایطی که دسترسی به اینترنت محدود است، جایگاه خود را حفظ کرده است. از اطلاعرسانیهای بانکی و کدهای تأیید دو مرحلهای گرفته تا پیامهای شخصی، پیامک همچنان نقشی حیاتی در زندگی روزمره ایفا میکند.
معرفی SMS API و کاربردهای آن در اندروید
اندروید با ارائه SMS API، امکان دسترسی برنامهنویسان به قابلیت ارسال و دریافت پیامک را فراهم کرده است. این API به توسعهدهندگان اجازه میدهد تا برنامههای اندرویدی خود را با قابلیتهای پیامکی ترکیب کنند. برخی از کاربردهای رایج SMS API در اندروید عبارتند از:
ارسال اعلانها و هشدارها: ارسال پیامک برای اطلاعرسانی رویدادهای مهم، مانند رسیدن سفارش، وضعیت تحویل، یا هشدارهای امنیتی.
کدهای تأیید و احراز هویت: ارسال کدهای یکبار مصرف (OTP) برای تأیید هویت کاربران در فرآیندهای ثبتنام یا ورود.
ارسال پیامکهای تبلیغاتی یا اطلاعرسانی: برای کسبوکارها جهت اطلاعرسانی به مشتریان خود.
دستورات کنترلی از راه دور: ارسال پیامک به یک دستگاه اندرویدی برای اجرای دستورات خاص (مثلاً فعال کردن یک سرویس).
دریافت و پردازش پیامکهای ورودی: مانند دریافت کد فعالسازی، پاسخ به نظرسنجیها، یا هرگونه پیام ورودی که نیاز به پردازش دارد.
پیشنیازها
برای شروع کار با SMS API در اندروید، نیاز به موارد زیر دارید:
محیط توسعه اندروید (Android Studio): آخرین نسخه Android Studio را نصب و راهاندازی کنید.
آشنایی با جاوا یا کاتلین: این دو زبان برنامهنویسی اصلی برای توسعه اندروید هستند. مفاهیم اولیه مانند متغیرها، توابع، کلاسها، و مدیریت رویدادها باید درک شده باشند.
درک مفاهیم پایهای اندروید: مفاهیمی مانند Activity، Context، Intent، و Manifest.xml.
دستگاه اندرویدی یا شبیهساز: برای تست و اجرای برنامههای خود.
فصل ۲: ارسال پیامک
مفهوم Intent برای ارسال پیامک
یکی از سادهترین راهها برای ارسال پیامک در اندروید، استفاده از Intent است. Intent ها پیامهایی هستند که بین کامپوننتهای مختلف برنامه یا بین برنامههای مختلف ارسال میشوند. برای ارسال پیامک، میتوانیم یک Intent با اکشن ACTION_SENDTO بسازیم و URI آن را با فرمت smsto: و شماره تلفن مقصد تنظیم کنیم. سپس، متن پیام را به صورت extras اضافه کرده و این Intent را با استفاده از startActivity() اجرا کنیم. این روش معمولاً پنجره پیشفرض ارسال پیامک سیستم عامل را باز میکند و کاربر میتواند قبل از ارسال، پیام را بررسی و تأیید کند.
استفاده از برای ارسال پیامک
برای کنترل بیشتر بر فرآیند ارسال پیامک و ارسال مستقیم آن بدون نیاز به دخالت کاربر در پنجره پیشفرض، از کلاس SmsManager استفاده میشود. SmsManager کلاس اصلی برای کار با پیامکها در اندروید است.
برای استفاده از SmsManager:
دریافت نمونه
SmsManager:SmsManager smsManager = SmsManager.getDefault();
ارسال پیامک: متد
sendTextMessage()یاsendMultipartTextMessage()برای ارسال پیام استفاده میشود.sendTextMessage(String destinationAddress, String scAddress, String text, PendingIntent sentIntent, PendingIntent deliveryIntent): برای ارسال پیامکهای کوتاه که در یک پیام جای میگیرند.sendMultipartTextMessage(String destinationAddress, String scAddress, String[] parts, PendingIntent sentIntent, PendingIntent deliveryIntent): برای ارسال پیامکهای طولانی که به چند قسمت تقسیم میشوند.
پارامترها:
destinationAddress: شماره تلفن گیرنده.scAddress: آدرس مرکز پیام (معمولاً null).text(یاparts): متن پیام (یا آرایهای از رشتهها برای پیامهای چند قسمتی).sentIntent: یکPendingIntentکه پس از ارسال موفقیتآمیز پیام (ارسال به مرکز پیام) اجرا میشود.deliveryIntent: یکPendingIntentکه پس از رسیدن پیام به دستگاه گیرنده اجرا میشود.
کد نمونه برای ارسال پیامک تکی و انبوه
ارسال پیامک تکی:
String phoneNumber = "0912xxxxxxx"; // شماره تلفن گیرنده
String message = "سلام، این یک پیام تست است."; // متن پیام
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, message, null, null);
// میتوانید PendingIntent ها را برای دریافت نتیجه ارسال اضافه کنید
Toast.makeText(getApplicationContext(), "پیامک ارسال شد.", Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "ارسال پیامک ناموفق بود.", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
ارسال پیامک انبوه (چند قسمتی):
اگر متن پیام شما طولانی است و بیش از یک پیامک را اشغال میکند، باید از sendMultipartTextMessage استفاده کنید. SmsManager به طور خودکار متن را به قسمتهای لازم تقسیم میکند.
String phoneNumber = "0912xxxxxxx";
String message = "این یک پیام تست طولانی است که برای نشان دادن قابلیت ارسال پیامک چند قسمتی طراحی شده است. طولانی بودن پیام باعث میشود که SmsManager آن را به چندین بخش تقسیم کند.";
try {
SmsManager smsManager = SmsManager.getDefault();
ArrayList<String> parts = smsManager.divideMessage(message); // تقسیم پیام به قسمتهای کوچکتر
smsManager.sendMultipartTextMessage(phoneNumber, null, parts, null, null);
Toast.makeText(getApplicationContext(), "پیامک انبوه ارسال شد.", Toast.LENGTH_LONG).show();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), "ارسال پیامک انبوه ناموفق بود.", Toast.LENGTH_LONG).show();
e.printStackTrace();
}
مدیریت مجوزهای لازم (SEND_SMS)
برای اینکه برنامه شما بتواند پیامک ارسال کند، باید مجوز SEND_SMS را در فایل AndroidManifest.xml خود اعلام کنید.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.yourapp">
<uses-permission android:name="android.permission.SEND_SMS"/>
<application
... >
...
</application>
</manifest>
نکته مهم: از اندروید ۶.۰ (API level 23) به بعد، مجوزهای حساس مانند SEND_SMS نیاز به درخواست در زمان اجرا دارند. بنابراین، علاوه بر اعلام در Manifest، باید به صورت برنامهای نیز از کاربر درخواست مجوز کنید.
// در Activity یا Fragment خود
private static final int MY_PERMISSIONS_REQUEST_SEND_SMS = 0;
private void sendSms(String phoneNumber, String message) {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.SEND_SMS)
!= PackageManager.PERMISSION_GRANTED) {
// مجوز هنوز اعطا نشده است، درخواست مجوز
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.SEND_SMS},
MY_PERMISSIONS_REQUEST_SEND_SMS);
} else {
// مجوز از قبل اعطا شده است، ارسال پیامک
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, message, null, null);
Toast.makeText(getApplicationContext(), "پیامک ارسال شد.", Toast.LENGTH_LONG).show();
}
}
@Override
public void onRequestPermissionsResult(int requestCode,String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_SEND_SMS: {
// بررسی اینکه آیا درخواست لغو شده و نتیجه خالی است
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// مجوز اعطا شد، حالا میتوانید پیامک ارسال کنید
sendSms("0912xxxxxxx", "پیام پس از اعطای مجوز.");
} else {
// مجوز رد شد، نمایش یک پیام به کاربر
Toast.makeText(getApplicationContext(), "مجوز ارسال پیامک رد شد.", Toast.LENGTH_LONG).show();
}
return;
}
}
}
فصل ۳: دریافت پیامک
مفهوم BroadcastReceiver برای دریافت پیامک
برای دریافت پیامکهای ورودی در اندروید، از مکانیزم BroadcastReceiver استفاده میشود. BroadcastReceiver یک کامپوننت اندرویدی است که به رویدادهای سیستمی یا پخشهای سفارشی (custom broadcasts) پاسخ میدهد. هنگامی که یک پیامک جدید دریافت میشود، سیستم عامل اندروید یک broadcast با اکشن android.provider.Telephony.SMS_RECEIVED منتشر میکند. ما میتوانیم یک BroadcastReceiver سفارشی پیادهسازی کنیم تا این broadcast را دریافت کرده و پیامک را پردازش کند.
پیادهسازی برای دریافت پیامکهای ورودی
برای پیادهسازی، ابتدا باید یک کلاس که از BroadcastReceiver ارث میبرد، ایجاد کنید. در متد onReceive() این کلاس، منطق پردازش پیامک قرار میگیرد.
public class SmsBroadcastReceiver extends BroadcastReceiver {
private static final String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
@Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_SMS_RECEIVED)) {
// استخراج اطلاعات پیامک
Bundle bundle = intent.getExtras();
if (bundle != null) {
Object[] pdus = (Object[]) bundle.get("pdus");
if (pdus != null) {
SmsMessage[] messages = new SmsMessage[pdus.length];
for (int i = 0; i < pdus.length; i++) {
messages[i] = SmsMessage.createFromPdu((byte[]) pdus[i]);
}
StringBuilder sender = new StringBuilder();
StringBuilder messageBody = new StringBuilder();
Date messageDate = new Date();
for (SmsMessage message : messages) {
sender.append(message.getOriginatingAddress());
messageBody.append(message.getMessageBody());
messageDate.setTime(message.getTimestampMillis()); // زمان دریافت پیام
}
String senderNumber = sender.toString();
String smsContent = messageBody.toString();
// String formattedDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(messageDate);
// حال میتوانید senderNumber و smsContent را پردازش کنید
// مثلاً نمایش در یک Toast یا ذخیره در دیتابیس
Log.d("SmsReceiver", "Sender: " + senderNumber + ", Message: " + smsContent);
// اگر نمیخواهید پیامک دریافت شده در برنامه پیامک پیشفرض نمایش داده شود،
// میتوانید processOrderedBroadcast را فراخوانی کنید و abortBroadcast() را صدا بزنید.
// اما این کار ممکن است رفتار سیستم را مختل کند و برای همه موارد توصیه نمیشود.
// abortBroadcast();
}
}
}
}
}
استخراج اطلاعات پیامک (فرستنده، متن پیام، زمان)
همانطور که در کد بالا مشاهده میکنید، اطلاعات پیامک از Bundle ارسالی به onReceive استخراج میشود.
pdus: آرایهای از byte array ها که حاوی دادههای پیامک (Protocol Data Units) است.SmsMessage.createFromPdu(): این متد یکSmsMessageرا از یک PDU داده شده ایجاد میکند.getOriginatingAddress(): شماره تلفن فرستنده پیامک را برمیگرداند.getMessageBody(): متن پیام را برمیگرداند.getTimestampMillis(): زمان دریافت پیام را بر حسب میلیثانیه برمیگرداند.
مدیریت مجوزهای لازم (RECEIVE_SMS)
برای دریافت پیامک، نیاز به مجوز RECEIVE_SMS دارید.
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.yourcompany.yourapp">
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
<application
... >
<receiver android:name=".SmsBroadcastReceiver"
android:exported="true">
<intent-filter>
<action android:name="android.provider.Telephony.SMS_RECEIVED"/>
</intent-filter>
</receiver>
...
</application>
</manifest>
نکته مهم:
برای اندروید ۶.۰ به بعد، مجوز
RECEIVE_SMSنیز یک مجوز زمان اجرا است و باید از کاربر درخواست شود.لازم است
BroadcastReceiverخود را در فایلAndroidManifest.xmlثبت کنید و اکشنandroid.provider.Telephony.SMS_RECEIVEDرا بهintent-filterآن اضافه کنید.ویژگی
android:exported="true"برایreceiverضروری است تا بتواند broadcast های سیستمی را دریافت کند.
درخواست مجوز در زمان اجرا برای RECEIVE_SMS:
private static final int MY_PERMISSIONS_REQUEST_RECEIVE_SMS = 1;
private void checkSmsReceivePermission() {
if (ContextCompat.checkSelfPermission(this,
Manifest.permission.RECEIVE_SMS)
!= PackageManager.PERMISSION_GRANTED) {
// مجوز هنوز اعطا نشده است، درخواست مجوز
ActivityCompat.requestPermissions(this,
new String[]{Manifest.permission.RECEIVE_SMS},
MY_PERMISSIONS_REQUEST_RECEIVE_SMS);
} else {
// مجوز از قبل اعطا شده است، receiver آماده دریافت است.
Log.d("Permission", "RECEIVE_SMS permission granted.");
}
}
@Override
public void onRequestPermissionsResult(int requestCode,String[] permissions, int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_RECEIVE_SMS: {
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
Log.d("Permission", "RECEIVE_SMS permission granted after request.");
} else {
Toast.makeText(getApplicationContext(), "مجوز دریافت پیامک رد شد. قادر به دریافت پیامک نخواهید بود.", Toast.LENGTH_LONG).show();
}
return;
}
// ... case های دیگر برای مجوزهای دیگر
}
}
فصل ۴: نکات پیشرفته و مدیریت خطا
ارسال پیامک به صورت برنامهریزی شده (Scheduled SMS)
ارسال پیامک به صورت برنامهریزی شده به طور مستقیم توسط SMS API اندروید پشتیبانی نمیشود. برای این منظور، نیاز به استفاده از AlarmManager برای زمانبندی ارسال و BroadcastReceiver یا Service برای اجرای منطق ارسال در زمان مقرر دارید.
استفاده از
AlarmManager: برای تنظیم یک زمانبندی مشخص.یک
BroadcastReceiverیاService: که توسطAlarmManagerفراخوانی میشود. این کامپوننت وظیفه ارسال پیامک با استفاده ازSmsManagerرا بر عهده دارد.ذخیره اطلاعات پیامک: شماره تلفن، متن پیام، و زمان ارسال باید در جایی (مانند
SharedPreferencesیاRoom Database) ذخیره شوند تا در زمان مقرر قابل بازیابی باشند.
مثال ساده (با استفاده از AlarmManager و PendingIntent):
// در Activity یا Service
private void scheduleSms(Context context, String phoneNumber, String message, long triggerAtMillis) {
Intent intent = new Intent(context, SendSmsAlarmReceiver.class);
intent.putExtra("phoneNumber", phoneNumber);
intent.putExtra("message", message);
PendingIntent pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
AlarmManager alarmManager = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
if (alarmManager != null) {
// استفاده از setExact برای زمانبندی دقیق (نیاز به مجوز SCHEDULE_EXACT_ALARM در API 31+)
// برای نسخههای قدیمیتر یا ارسال غیردقیقتر میتوانید از set() یا setInexactRepeating() استفاده کنید.
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
alarmManager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
} else {
alarmManager.set(AlarmManager.RTC_WAKEUP, triggerAtMillis, pendingIntent);
}
Toast.makeText(context, "زمانبندی ارسال پیامک انجام شد.", Toast.LENGTH_SHORT).show();
}
}
// BroadcastReceiver برای دریافت آلارم و ارسال پیامک
public class SendSmsAlarmReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
String phoneNumber = intent.getStringExtra("phoneNumber");
String message = intent.getStringExtra("message");
if (phoneNumber != null && message != null) {
try {
SmsManager smsManager = SmsManager.getDefault();
smsManager.sendTextMessage(phoneNumber, null, message, null, null);
Toast.makeText(context, "پیامک برنامهریزی شده ارسال شد.", Toast.LENGTH_SHORT).show();
} catch (Exception e) {
Toast.makeText(context, "خطا در ارسال پیامک برنامهریزی شده.", Toast.LENGTH_SHORT).show();
e.printStackTrace();
}
}
}
}
مجوزهای مورد نیاز:
android.permission.SEND_SMSandroid.permission.SCHEDULE_EXACT_ALARM(برای API 31 به بعد، در Manifest اعلام شود و در زمان اجرا درخواست شود).
مدیریت خطاهای احتمالی در ارسال و دریافت
ارسال پیامک:
مجوز رد شده: کاربر اجازه ارسال پیامک را نداده است.
خطای
SmsManager: ممکن است به دلیل عدم وجود سیمکارت، اختلال در شبکه، یا محدودیتهای اپراتور رخ دهد. استفاده ازtry-catchبرای مدیریت استثنائات ضروری است.پیام ارسال نشد (
sentIntent): اگرsentIntentرا تنظیم کرده باشید، میتوانید وضعیت ارسال (موفقیت یا شکست) را از طریقresultCodeآن بررسی کنید.پیام تحویل نشد (
deliveryIntent): اگرdeliveryIntentرا تنظیم کرده باشید، وضعیت تحویل به دستگاه گیرنده را دریافت خواهید کرد.
دریافت پیامک:
مجوز رد شده: کاربر اجازه دریافت پیامک را نداده است.
فیلتر نشدن Broadcast: اطمینان حاصل کنید که
intent-filterدرAndroidManifest.xmlبه درستی تنظیم شده است.پایان دادن به Broadcast: در برخی موارد، ممکن است بخواهید پیامک دریافتی را از پردازش توسط برنامههای دیگر جلوگیری کنید. این کار با فراخوانی
abortBroadcast()درonReceive()انجام میشود (نیاز به دقت و درک پیامدهای آن دارد).پیامهای چندبخشی: برای پیامهای طولانی،
pdusممکن است شامل چندینSmsMessageباشد که باید همه آنها را پردازش کنید.
ذخیرهسازی پیامکها
پیامکهای ارسالی: پس از ارسال موفقیتآمیز (با استفاده از
SmsManagerوsentIntent)، میتوانید پیام را در دیتابیس برنامه خود (مانندRoom) ذخیره کنید.پیامکهای دریافتی: پیامهای دریافتی توسط
SmsBroadcastReceiverرا میتوان بلافاصله پس از استخراج اطلاعات، در دیتابیس برنامه ذخیره کرد. این کار امکان نمایش تاریخچه پیامها و دسترسی به آنها در هر زمان را فراهم میکند.
ملاحظات امنیتی
مجوزها: همیشه فقط مجوزهای لازم را درخواست کنید. درخواست مجوزهای غیرضروری میتواند اعتماد کاربر را از بین ببرد.
دسترسی به اطلاعات حساس: اطلاعات پیامک میتواند حاوی اطلاعات شخصی باشد. با این دادهها با دقت رفتار کنید و از نمایش آنها در مکانهای ناامن خودداری کنید.
دریافت پیامکهای ناخواسته: کاربران ممکن است از اپلیکیشن شما برای ارسال هرزنامه (spam) استفاده کنند. برای جلوگیری از این امر، میتوانید محدودیتهایی برای تعداد پیامکهای ارسالی در یک بازه زمانی مشخص اعمال کنید.
پیامکهای مخرب: اگر برنامه شما به دریافت پیامک میپردازد، مراقب باشید که پیامهای مخرب را به درستی مدیریت کنید و اجازه اجرای کدهای ناخواسته را ندهید.
جلوگیری از دسترسی غیرمجاز: اگر اپلیکیشن شما قابلیت ارسال پیامک در پسزمینه یا بدون تأیید کاربر دارد، حتماً باید منطق امنیتی قوی برای جلوگیری از سوءاستفاده داشته باشد.
خلاصه نکات کلیدی:
برای ارسال پیامک از
SmsManagerو برای دریافت ازBroadcastReceiverاستفاده میشود.مدیریت صحیح مجوزها در
AndroidManifest.xmlو درخواست آنها در زمان اجرا، حیاتی است.استفاده از
Intentها روشی ساده برای باز کردن پنجره پیشفرض ارسال پیامک است.SmsManagerکنترل بیشتری بر فرآیند ارسال پیامک ارائه میدهد و امکان ارسال مستقیم را فراهم میکند.دریافت پیامک نیازمند ثبت یک
BroadcastReceiverدر Manifest و مدیریت اکشنandroid.provider.Telephony.SMS_RECEIVEDاست.برای پیامکهای طولانی، از
sendMultipartTextMessageاستفاده کنید.برای ارسال پیامک برنامهریزی شده، از
AlarmManagerدر کنارBroadcastReceiverیاServiceاستفاده کنید.مدیریت خطا و ملاحظات امنیتی در کار با SMS API بسیار مهم هستند.
تمرین پایان فصل:
۱. اپلیکیشن ارسال پیامک: یک اپلیکیشن اندروید ساده بسازید که شامل دو فیلد EditText برای وارد کردن شماره تلفن و متن پیام باشد. یک دکمه "ارسال" نیز قرار دهید. با کلیک بر روی دکمه، پیامک با استفاده از SmsManager و با مدیریت مجوزهای لازم ارسال شود.
۲. اپلیکیشن نمایش پیامکهای دریافتی: یک اپلیکیشن طراحی کنید که پس از دریافت پیامک، اطلاعات فرستنده و متن پیام را استخراج کرده و در یک لیست (مثلاً RecyclerView یا ListView) نمایش دهد. این کار نیازمند پیادهسازی BroadcastReceiver برای دریافت پیامک و ذخیرهسازی موقت یا دائمی اطلاعات است.