دریافت پیامک (SMS) در برنامه اندرویدی + سورس پروژه
کاربردهای متعددی را برای قابلیت دریافت پیامک (SMS) در برنامه نویسی اندروید میتوان برشمرد. با گسترش کاربرد اپلیکیشنهای موبایلی و بویژه اندرویدی، حجم زیادی از خدمات بخش خصوصی و سازمانی در این محیط به مردم ارائه میگردد.
برای مثال احراز هویت کاربر/مشتری یکی از آیتمهای ضروری در ارائه این خدمات به شمار میرود که ارسال پیامک کد تایید به شماره همراه شخص یکی از رایجترین روشهاست. اما برای بهبود UX (تجربه کاربری) میتوان این بخش را به صورت خودکار انجام داد تا کاربر ملزم به وارد کردن دستی کد دریافتی نبوده و کد موجود در پیامک به طور خوکار وارد برنامه شود.
البته کاربردهای دیگری هم برای دسترسی به پیامکهای دریافتی وجود دارد و محدود به احراز هویت نیست. در این جلسه نحوهی دریافت پیامک (SMS) در برنامه نویسی اندروید را به دو روش متفاوت بررسی میکنیم.
نحوه دریافت پیامک در اپلیکیشن اندرویدی
به نام خدا. به طور کل با استفاده از دو کلاس SmsMessage و Cursor میتوانیم به پیامکهای موجود در دستگاه اندرویدی دسترسی داشته باشیم. البته روش متداول، استفاده از SmsMessage است. در ادامه به بررسی هر دو روش پرداخته و سپس به صورت عملی پیاده سازی میکنیم.
SmsMessage: کلاسی با نام SmsMessage در API 4 به سیستم عامل اندروید اضافه شده که وظیفه دسترسی به پیامک یا همان SMS را در دستگاههای اندرویدی بر عهده دارد.
البته از API 1 هم کلاسی با این نام وجود داشت (android.telephony.gsm.SmsMessage) که تنها از پروتکل GSM پشتیبانی میکرد. اما در API 4 این کلاس با android.telephony.SmsMessage جایگزین شد که از هر دو پروتکل GSM و CDMA پشتیبانی میکند.
همچنین برای شنود رویداد مربوط به دریافت پیامک لازم است از برودکست رسیور استفاده کنیم که قبلا در آموزش BroadcastReceiver در اندروید با این مبحث آشنا شدیم.
Cursor: علاوه بر کلاس SmsMessage با استفاده از کلاس Cursor هم میتوان به آرشیو پیامکها دسترسی داشت که با وجود کلاس SmsMessage استفاده از آن جایگاهی نخواهد داشت با این حال در انتهای آموزش به این مورد نیز اشاره مختصری خواهیم کرد.
ساخت پروژه دریافت SMS در اندروید استودیو
برای بررسی نحوه دریافت پیامک (SMS) در برنامه نویسی اندروید مطابق مبحث آموزش ساخت پروژه در اندروید استودیو یک پروژه اندرویدی با نام ReceiveSMS میسازم. اکتیویتی را از نوع Empty Activity و زبان را Java انتخاب کردم.
دریافت پیامک (SMS) توسط SmsMessage
اگر بخاطر داشته باشید در جلسه نحوه ارسال پیامک (SMS) در برنامه نویسی اندروید در مانیفست پروژه مجوز ارسال پیامک را تعریف کردیم. در این پروژه لازم است عکس آن یعنی مجوز دریافت SMS را تعریف کنیم:
<uses-permission android:name="android.permission.RECEIVE_SMS" />
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ir.android_studio.receivesms"> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <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/Theme.ReceiveSMS"> <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>
در قدم بعد لازم است یک BroadcastReceiver ایجاد کنم. بر خلاف پروژه ارسال SMS که برودکست درون اکتیویتی و به صورت داینامیک رجیستر شد، در این جلسه برودکست را در کلاس مجزا تعریف کرده و همچنین رجیستر یا ثبت آن را به صورت استاتیک و درون Manifest انجام میدهم.
یک کلاس با نام SMSBroadcastReceiver به پروژه اضافه کرده و آنرا از BroadcastReceiver مشتق میکنم. سپس متد onReceive را اضافه میکنم:
SMSBroadcastReceiver.java
package ir.android_studio.receivesms; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; public class SMSBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { } }
سپس برودکست رسیور را در مانیفست پروژه ثبت میکنم:
<receiver android:name=".SMSBroadcastReceiver"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver>
کد کامل مانیفست:
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="ir.android_studio.receivesms"> <uses-permission android:name="android.permission.RECEIVE_SMS" /> <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/Theme.ReceiveSMS"> <activity android:name=".MainActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <receiver android:name=".SMSBroadcastReceiver"> <intent-filter> <action android:name="android.provider.Telephony.SMS_RECEIVED" /> </intent-filter> </receiver> </application> </manifest>
یک اکشن با نام android.provider.Telephony.SMS_RECEIVED در مانیفست تعریف کردیم. این اکشن وظیفه بررسی رویدادهای مربوط به دریافت پیامک را در سیستم عامل بر عهده دارد.
قبل از پیاده سازی کلاس SmsMessage قصد دارم صرفا عملکرد برودکست را بررسی کنم و مطمئن شوم action ای که در مانیفست تعریف کردم میتواند به دریافت پیامک واکنش نشان دهد. بنابراین در متد onReceive برودکست رسیور صرفا یک پیغام Toast تعریف میکنم:
public void onReceive(Context context, Intent intent) { Toast.makeText(context, "یک پیامک دریافت شد", Toast.LENGTH_SHORT).show(); }
با توجه به اینکه قصد دارم پروژه را روی یک دیوایس بالاتر از اندروید ۶ اجرا کنم، لازم است کدهای مربوط به Runtime Permission را مطابق آموزش Runtime Permission در اندروید به اکتیویتی اضافه کنم:
MainActivity.java
package ir.android_studio.receivesms; import androidx.annotation.NonNull; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import android.Manifest; import android.content.DialogInterface; import android.content.pm.PackageManager; import android.os.Bundle; import android.widget.Toast; public class MainActivity extends AppCompatActivity { private final int SMS_REQUEST_CODE = 100; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.RECEIVE_SMS) != PackageManager.PERMISSION_GRANTED) { requestReceiveSMSpermission(); } else { Toast.makeText(this, "مجوز قبلا دریافت شده", Toast.LENGTH_SHORT).show(); } } private void requestReceiveSMSpermission() { if (ActivityCompat.shouldShowRequestPermissionRationale(MainActivity.this, Manifest.permission.RECEIVE_SMS)) { new AlertDialog.Builder(this) .setTitle("درخواست مجوز") .setMessage("برای عملکرد صحیح برنامه باید دسترسی به دریافت پیامکتایید شود") .setPositiveButton("موافقم", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { reqPermission(); } }) .setNegativeButton("لغو", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialogInterface, int i) { dialogInterface.dismiss(); } }) .create() .show(); } else { reqPermission(); } } private void reqPermission() { ActivityCompat.requestPermissions(MainActivity.this, new String[] {Manifest.permission.RECEIVE_SMS}, SMS_REQUEST_CODE); } @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { super.onRequestPermissionsResult(requestCode, permissions, grantResults); if (requestCode == SMS_REQUEST_CODE) { if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { Toast.makeText(this, "مجوز تایید شد", Toast.LENGTH_SHORT).show(); } else { Toast.makeText(this, "مجوز رد شد", Toast.LENGTH_SHORT).show(); } } } }
یکی از دیوایسهای امولاتور اندرویدی Genymotion را اجرا میکنم. یکی از امکانات کاربردی شبیه سازهای اندرویدی از جمله Genymotion و همچنین AVD (شبیه ساز داخلی اندروید استودیو) قابلیت ارسال پیامک به دستگاه و همچنین برقراری تماس است. بدون آنکه نیاز به اجرا و تست برنامه روی یک دستگاه واقعی داشته باشیم.
پروژه را روی شبیه ساز اجرا کرده و مجوز دسترسی به پیامکها را برای برنامه تایید میکنم. سپس روی آیکون Phone منوی سمت راست دیوایس کلیک میکنم:
در کادر Phone یک شماره فرستنده و متن دلخواه وارد کرده و پیام را ارسال میکنم. بلافاصله پیغام Toast ای که در متد onReceive تعریف کرده بودم اجرا شد:
تا اینجای کار مطمئن شدیم برنامه ما میتواند به درستی رویدادهای مربوط به دریافت پیامک را شنود کند. در قدم بعد میخواهیم محتوای SMS دریافتی را به دست بیاوریم که شامل شماره ارسال کننده پیام و همچنین متن پیامک میباشد.
پیغام Toast داخل متد onReceive برودکست رسیور را حذف کرده و کد زیر را جایگزین آن میکنم:
if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) { Bundle mBundle = intent.getExtras(); SmsMessage[] msg; String smsFrom; if (mBundle != null) { try { Object[] mPdus = (Object[]) mBundle.get("pdus"); msg = new SmsMessage[mPdus.length]; for (int i = 0; i < mPdus.length; i++) { msg[i] = SmsMessage.createFromPdu((byte[]) mPdus[i]); smsFrom = msg[i].getOriginatingAddress(); String smsBody = msg[i].getMessageBody(); Toast.makeText(context, "شماره: " + smsFrom + " / پیام: " + smsBody, Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } } }
در ابتدا یک if تعریف شده که بررسی میکند کدها تنها در صورتی اجرا شوند که اینتنت اکشن دریافتی از نوع android.provider.Telephony.SMS_RECEIVED باشد. یعنی دقیقا همان اکشنی که قبلا در AndroidManifest.xml و در تگ receiver ثبت کردیم.
سپس یک نمونه از Bundle و SmsMessage و یک String ساخته شده. در ادامه ابتدا بررسی میشود آیا مقدار ذخیره شده در mBundle نال است یا نه؛ یعنی دادهای که مربوط به محتوای یک پیامک باشد در باندل ذخیره شده یا خیر.
یک نمونه از Object[] با نام دلخواه mPdus ایجاد شده که دادهها با فرمت PDU (مخفف Protocol Data Unit) در آن ذخیره میشود. با استفاده از PDU به ساختار محتوای پیام دریافتی دسترسی داریم.
سپس در حلقه for با استفاده از SmsMessage.createFromPdu() محتوای پیام دریافتی تفکیک میشود. همانطور که ملاحظه میکنید در دو خط بعد توسط getOriginatingAddress و getMessageBody به ترتیب به شماره ارسال کننده پیام و متن پیام دسترسی خواهیم داشت. کاربرد متدها از نحوه نامگذاری آنها هم مشخص است. عبارت Originating Address به معنی آدرس مبدا و Message Body به معنی بدنه یا متن پیام است.
در انتها، این دو آیتم در یک پیغام Toast قرار داده شده است.
کد کامل کلاس برودکست رسیور:
SMSBroadcastReceiver.java
package ir.android_studio.receivesms; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.telephony.SmsMessage; import android.widget.Toast; public class SMSBroadcastReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, Intent intent) { // Toast.makeText(context, "یک پیامک دریافت شد", Toast.LENGTH_SHORT).show(); if (intent.getAction().equals("android.provider.Telephony.SMS_RECEIVED")) { Bundle mBundle = intent.getExtras(); SmsMessage[] msg; String smsFrom; if (mBundle != null) { try { Object[] mPdus = (Object[]) mBundle.get("pdus"); msg = new SmsMessage[mPdus.length]; for (int i = 0; i < mPdus.length; i++) { msg[i] = SmsMessage.createFromPdu((byte[]) mPdus[i]); smsFrom = msg[i].getOriginatingAddress(); String smsBody = msg[i].getMessageBody(); Toast.makeText(context, "شماره: " + smsFrom + " / پیام: " + smsBody, Toast.LENGTH_SHORT).show(); } } catch (Exception e) { e.printStackTrace(); } } } } }
createFromPdu(byte[] pdu, String format)
میگیرد که تا قبل از API 23 دارای یک پارامتر ورودی و به صورت
createFromPdu(byte[] pdu)
بوده است. اما نیازی به تعریف هردو متد بر اساس نسخه اندروید کاربر نیست. من پروژه را بر روی اندروید ۴ به بالا تست کردم و اختلالی در دریافت SMS در نسخههای قدیمی وجود نداشت.
پروژه را دوباره اجرا کرده و با استفاده از گزینه Phone یک پیام جدید ارسال میکنم. حالا شماره ارسال کننده و متن SMS هم در Toast نمایش داده میشود:
دریافت پیامک (SMS) توسط Cursor
همانطور که در ابتدای جلسه اشاره شد، با وجود کلاس SmsMessage استفاده از Cursor برای دریافت پیامک توجیهی نخواهد داشت اما لازم دانستم حداقل برای تمرین به این مورد هم اشاره کنم.
یک پروژه جدید بسازید و TextView پیش فرض اکتیویتی را به Button تبدیل کنید. سپس کد زیر را در رویداد onClickListener دکمه قرار دهید (از تعریف Runtime Permission فراموش نکنید).
Cursor cursor = getContentResolver().query(Uri.parse("content://sms"), null, null, null, null); cursor.moveToFirst(); Toast.makeText(this, cursor.getString(12), Toast.LENGTH_SHORT).show();
بعد از اجرای پروژه و تایید مجوز دریافت SMS، مانند قبل یک پیامک ارسال کرده و سپس روی Button کلیک کنید. متن پیامک در قالب Toast نمایش داده خواهد شد. البته در این روش هم میتوان شماره ارسال کننده پیامک را دریافت کرد که به آن نمیپردازم.
توجه : سورس پروژه درون پوشه Exercises قرار دارد
تعداد صفحات : ۱۳
حجم : ۲ مگابایت
قیمت : رایگان
دانلود رایگان با حجم ۲ مگابایت لینک کمکی
سلام و خسته نباشید من از cursor در یکی از پروژه های قدیمیم استفاده کرده بودم با sdk 30 ولی الان اندروید استودیو کرسر رو روی sdk 31,32,33 نمیشناسه و با تغییر sdk خط مربوط به کرسر ارور میده و پروژه بیلد نمیشه ، اگر ممکنه راهنمایی بفرمایید
با تشکر
ببینید خطایی که میده دقیقا چی هست (ترجمه کنید). اگه بازم متوجه نشدید یه اسکرین شات از ارور همینجا بذارید (روی uupload.ir آپلود کنید)
سلام مثل همیشه عالی واقعا ممنون بابت آموزش های خوبی که میدین یک سوال با همین روش امکان خواندن اطلاعات پیام دهنده هایی مثل اوپراتور همراه اول یا پیام های بانکداری هست ؟ (alphanumeric )
نزدیکترین چیزی که پیدا کردم این بود
https://stackoverflow.com/questions/64872709/get-sms-sender-name-who-is-not-in-contact-list
اما نمیدونم چطور با این کدها که شما زدین مچش کنم .
داداش لطفا بزار اگ برا بار دوم رو باتن کلیک شد کار متفاوتی انجام بده با اولین بار کلیک فرق داشته باشه
سلام و درود.
همین کار دریافت sms رو میخواستم وقتی برنامه بسته هست هم انجام بده و حتما لازم نباشه کاربر تو برنامه باشه.
یعنی یک سرویس از برنامه در پس زمینه فعال باشه که دریافت sms رو تشخیص بده و بتونه متنش رو بخونه.
مثل کاری که برنامه های پیام رسان مثل واتس آپ انجام میدن به این صورت که حتی وقتی بسته هستن یک سرویس در پس زمینه فعاله و اگر پیامی دریافت بشه نمایش میده.
عالییییییییییییییی
سلام. من میخوام کاربر شماره موبایلشو وارد کنه و بعد از بررسی درست بودن شماره موبایل یه پیام با کد تایید براش ارسال بشه که با وارد کردن اون کد به صفحه بعد بره میشه بگید چطوری؟
ممنون
نیاز هست با API ای که سرویس دهنده های پیامک ارائه میدن این کار رو پیاده کنید. مستندات سرویس دهنده پیامک مدنظرتون رو بررسی کنید ببینید چه توابعی لازمه برای اینکار
سلام حاجی خسته نباشی
چجوری میتونیم در همین اموزش متن پیام و شماره دریافت شده رو به یک فایل php در هاست ارسال کنیم؟!
اگر امکانش هست اموزش بزارید تشکر
سلام.ضمن تشکر و قدردانی از زحمات شما استاد گرانقدر.
بفرمایید اگر بخام پیامک درون برنامه ای داشته باشم طوری که دیگه پیام داخل اینباکس گوش نره . و بتونم فقط داخل برنامه خودم ازش استفاده کنم .آیا امکانش هست یا نه و اگر امکانش نباشه ایا امکانش هست به محض دریافت پیامک از شماره ای خاص بتونم اونو از داخل اینباکس گوشی حذف کنم.
پاسخ این سوال برام بسیار حیاتی و مهمه.ممنون میشم لطف بفرمایید راهنمایی بفرمایید.
سلام. در خصوص پیامک هر اطلاعاتی که داشتم رو داخل همین آموزش آوردم و فراتر از این موارد نه تجربه استفاده شو داشتم و نه فرصت برای جستجو
سلام ببخشید یه سوال خارج از بحث داشتم
ایا میشه با اندروید استدیو یه بازی دو بعدی که اجزا گرافیکی خیلی پیچیده ای هم نداره و همچنین مولتی پلیر هست رو بطور استاندارد و ایده ال ساخت
با سپاس
در زمینه بازی هیچ تجربه ای ندارم بزرگوار
از این نظر میگم که طبق تحقیقات خودم و نظر چند تا از دوستان برای این کار اصلا نباید میومدم سمت اندروید استدیو و زبان جاوا و باید موتور های بازیسازی مثل یونیتی و… رو یاد میگرفتم.
خواستم نظر شما رو در این باره بدونم؟
عرض کردم خدمتتون. هیچ تجربه ای تو زمینه بازی و گیم انجین ها ندارم