移动开发
现在阅读
Android Java: How To Send SMS & Receive SMS & Get SMS Messages
0

Android Java: How To Send SMS & Receive SMS & Get SMS Messages

由 ultracpy2018年1月26日

Download & Run

Below you can download projects for Eclipse or Android Studio, or prebuild binary *.apk-files for install and run demo on your device, and make sure that it really works with your phone.

Comment: all APK files has passed security check using Smali disassembler (for compliance with actual source code). No viruses. No excess permissions.

<!– observer downloads removed because old v4 deprecated –>

Contents

Sending SMS

We can send SMS in 2 ways – with Intent or without Intent.

Any SMS sending requires add android.permission.SEND_SMS permission to the manifest:

<uses-permission android:name="android.permission.SEND_SMS"/>

With Intent (Through SMS Application)

Directly Through Built-In SMS/MMS Application

This code forces built-in Android SMS/MMS application to open with specified phone number & SMS text. Next, user can press “Send” button in this application, and SMS will be sent.

Requires android.permission.SEND_SMS permission (see above).

/* Phone Number is just all digits of the number (including country code)
 * "+" isn't needed, but can appear
 * For example:
 * I have Beeline Phone Number +7 (962) 000-00-00,
 * in my case, it should be 79620000000 or +79620000000 */
strPhone = "XXXXXXXXXXX";
strMessage = "Lorem\nIpsum";
            
Intent sendIntent = new Intent(Intent.ACTION_VIEW);
sendIntent.setType("vnd.android-dir/mms-sms");
sendIntent.putExtra("address", strPhone);
sendIntent.putExtra("sms_body", strMessage); 
startActivity(sendIntent);

Through Default Application, Which Handling Smsto: Protocol

This code runs the “smsto:+XXXXXXXXXXX” url, Android processes it, and runs default application, which is set up to handle SmsTo protocol.

In default, it is a built-in Android SMS/MMS application (see Direct Intent above). But if we install any third-party application that also can handle SMS and sets itself as smsto handler (i.e. Skype), we at first time will see dialog where we should choose between built-in application and this 3rd-party application. It is inconvenient.

Requires android.permission.SEND_SMS permission (see above).

/* Phone Number is just all digits of the number (including country code)
 * "+" isn't recommended, because there it is already included into Uri (see below)
 * For example:
 * I have Beeline Phone Number +7 (962) 000-00-00,
 * in my case, it should be 79620000000 */
strPhone = "XXXXXXXXXXX";
strMessage = "Lorem\nIpsum";
            
Uri sms_uri = Uri.parse("smsto:+" + strPhone); 
Intent sms_intent = new Intent(Intent.ACTION_SENDTO, sms_uri); 
sms_intent.putExtra("sms_body", txtMessage.getText().toString()); 
startActivity(sms_intent); 

Without Intent (Silently, Without UI)

SmsManager.sendTextMessage()

Requires android.permission.SEND_SMS permission (see above).

/* Phone Number is just all digits of the number (including country code)
 * "+" isn't needed, but can appear
 * For example:
 * I have Beeline Phone Number +7 (962) 000-00-00,
 * in my case, it should be 79620000000 or +79620000000 */
String strPhone = "XXXXXXXXXXX";

String strMessage = "Lorem\nIpsum";
            
SmsManager sms = SmsManager.getDefault();
            
sms.sendTextMessage(strPhone, null, strMessage, null, null);
            
Toast.makeText(this, "Sent.", Toast.LENGTH_SHORT).show();

SmsManager.sendMultipartTextMessage() – If SMS Is Longer Than 160 Symbols

If length of SMS message exceeds some limit (usually it is 160 symbols), sendTextMessage() just not sends it.
We should use sendMultipartTextMessage() in such cases.

Requires android.permission.SEND_SMS permission (see above).

String strPhone = "XXXXXXXXXXX";
String strMessage = "12345678901234567890123456789012345678901234567890123456789012345678901234567890
		12345678901234567890123456789012345678901234567890123456789012345678901234567890TEST";
            
SmsManager sms = SmsManager.getDefault();
            
ArrayList<String> messageParts = sms.divideMessage(strMessage);
            
sms.sendMultipartTextMessage(strPhone, null, messageParts, null, null);
            
Toast.makeText(this, "Sent.", Toast.LENGTH_SHORT).show();

SendIntent & DeliveredIntent – Make Sure That SMS Is Sent & Delivered

Requires android.permission.SEND_SMS permission (see above).

For sendTextMessage():

String strPhone = "XXXXXXXXXXX";

String strMessage = "Lorem\nIpsum";
            
SmsManager sms = SmsManager.getDefault();

/* ---- Preparing Intents To Check While Sms Sent & Delivered ---- */

Context curContext = this.getApplicationContext();

PendingIntent sentPending = PendingIntent.getBroadcast(curContext,
        0, new Intent("SENT"), 0);

curContext.registerReceiver(new BroadcastReceiver() 
{
    @Override
    public void onReceive(Context arg0, Intent arg1) 
    {
        switch (getResultCode()) {
        case Activity.RESULT_OK:
            Toast.makeText(getBaseContext(), "Sent.",
                    Toast.LENGTH_LONG).show();
            break;
        case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
            Toast.makeText(getBaseContext(), "Not Sent: Generic failure.",
                    Toast.LENGTH_LONG).show();
            break;
        case SmsManager.RESULT_ERROR_NO_SERVICE:
            Toast.makeText(getBaseContext(), "Not Sent: No service (possibly, no SIM-card).",
                    Toast.LENGTH_LONG).show();
            break;
        case SmsManager.RESULT_ERROR_NULL_PDU:
            Toast.makeText(getBaseContext(), "Not Sent: Null PDU.",
                    Toast.LENGTH_LONG).show();
            break;
        case SmsManager.RESULT_ERROR_RADIO_OFF:
            Toast.makeText(getBaseContext(), "Not Sent: Radio off (possibly, Airplane mode enabled in Settings).",
                    Toast.LENGTH_LONG).show();
            break;
        }
    }
}, new IntentFilter("SENT"));

PendingIntent deliveredPending = PendingIntent.getBroadcast(curContext,
        0, new Intent("DELIVERED"), 0);

curContext.registerReceiver(
        new BroadcastReceiver()
        {
            @Override
            public void onReceive(Context arg0,Intent arg1)
            {
                switch (getResultCode())
                {
                case Activity.RESULT_OK:
                    Toast.makeText(getBaseContext(), "Delivered.",
                            Toast.LENGTH_LONG).show();
                    break;
                case Activity.RESULT_CANCELED:
                    Toast.makeText(getBaseContext(), "Not Delivered: Canceled.",
                            Toast.LENGTH_LONG).show();
                    break;
                }
            }
        }, new IntentFilter("DELIVERED"));

/* --------------------------------------------------------------- */

sms.sendTextMessage(strPhone, null, strMessage,
        sentPending, deliveredPending);

For sendMultipartTextMessage():

String strPhone = "XXXXXXXXXXX";

String strMessage = "1234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890TEST";

SmsManager sms = SmsManager.getDefault();

ArrayList<String> messageParts = sms.divideMessage(strMessage);

/* ---- Preparing Intents To Check While Sms Sent & Delivered ---- */

Context curContext = this.getApplicationContext();

int partsCount = messageParts.size();

ArrayList<PendingIntent> sentPendings = new ArrayList<PendingIntent>(partsCount);

ArrayList<PendingIntent> deliveredPendings = new ArrayList<PendingIntent>(partsCount);

for (int i = 0; i < partsCount; i++) {

    /* Adding Sent PendingIntent For Message Part */
    
    PendingIntent sentPending = PendingIntent.getBroadcast(curContext,
            0, new Intent("SENT"), 0);

    curContext.registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent arg1) {
            switch (getResultCode()) {
            case Activity.RESULT_OK:
                Toast.makeText(getBaseContext(), "Sent.",
                        Toast.LENGTH_LONG).show();
                break;
            case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
                Toast.makeText(getBaseContext(),
                        "Not Sent: Generic failure.", Toast.LENGTH_LONG)
                        .show();
                break;
            case SmsManager.RESULT_ERROR_NO_SERVICE:
                Toast.makeText(
                        getBaseContext(),
                        "Not Sent: No service (possibly, no SIM-card).",
                        Toast.LENGTH_LONG).show();
                break;
            case SmsManager.RESULT_ERROR_NULL_PDU:
                Toast.makeText(getBaseContext(), "Not Sent: Null PDU.",
                        Toast.LENGTH_LONG).show();
                break;
            case SmsManager.RESULT_ERROR_RADIO_OFF:
                Toast.makeText(
                        getBaseContext(),
                        "Not Sent: Radio off (possibly, Airplane mode enabled in Settings).",
                        Toast.LENGTH_LONG).show();
                break;
            }
        }
    }, new IntentFilter("SENT"));

    sentPendings.add(sentPending);

    /* Adding Delivered PendingIntent For Message Part */
    
    PendingIntent deliveredPending = PendingIntent.getBroadcast(
            curContext, 0, new Intent("DELIVERED"), 0);

    curContext.registerReceiver(new BroadcastReceiver() {
        @Override
        public void onReceive(Context arg0, Intent arg1) {
            switch (getResultCode()) {
            case Activity.RESULT_OK:
                Toast.makeText(getBaseContext(), "Delivered.",
                        Toast.LENGTH_LONG).show();
                break;
            case Activity.RESULT_CANCELED:
                Toast.makeText(getBaseContext(),
                        "Not Delivered: Canceled.", Toast.LENGTH_LONG)
                        .show();
                break;
            }
        }
    }, new IntentFilter("DELIVERED"));

    deliveredPendings.add(deliveredPending);
}

/* --------------------------------------------------------------- */

sms.sendMultipartTextMessage(strPhone, null, messageParts,
        sentPendings, deliveredPendings);

Check If Device Can Send SMS

PackageManager pm = this.getPackageManager();

if (!pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY) &&
    !pm.hasSystemFeature(PackageManager.FEATURE_TELEPHONY_CDMA)) {
    Toast.makeText(this, "Sorry, your device probably can't send SMS...", Toast.LENGTH_SHORT).show();
 }

Sending MMS

MMS is an SMS with attached file, i.e. image, video, audio, etc.

Unfortunately, there are no official simply API for sending MMS without user interaction (i.e. SmsManager.sendTextMessage()) on currently stable Android Versions (lower than Android 5.0 – API Level 21). There can be a “dirty” low-level trick, i.e. here, here and here; you can discover the sources of com.android.mms application (tip: it’s more simpler to start from old versions to newer).

And, currently I doing it here! Check updates, if interested.

And before this work finished, we can just send MMS via intents. But it seems more device-dependently and unstable than SMSes, we can’t be 100% sure that code bellow will work on any device.

With Intent (Through SMS Application)

Requires android.permission.SEND_SMS permission (see above).

/* Phone Number is just all digits of the number (including country code)
 * "+" isn't needed, but can appear
 * For example:
 * I have Beeline Phone Number +7 (962) 000-00-00,
 * in my case, it should be 79620000000 or +79620000000 */
strPhone = "XXXXXXXXXXX";
strMessage = "Lorem\nIpsum";

/* Attach Url is local (!) URL to file which should be sent */
strAttachUrl = "file:///sdcard/Test.png";

/* Attach Type is a content type of file which should be sent */
strAttachType = "image/png";

Intent sendIntent = new Intent(Intent.ACTION_SEND);
sendIntent.setClassName("com.android.mms", "com.android.mms.ui.ComposeMessageActivity");
sendIntent.putExtra("address", strPhone);
sendIntent.putExtra("sms_body", strMessage);

/* Adding The Attach */
sendIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(strAttachUrl));
sendIntent.setType(strAttachType);

sendIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);

startActivity(sendIntent);

Without Intent (Silently, Programmatically)

To send MMS programmatically, we need following permissions:

<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_APN_SETTINGS" />

Roughly speaking, MMS message is HTTP request to MMSC provider through the network.

It means we firstly should obtain URL of our MMSC center. To perform it, we will use some like following:

final Cursor apnCursor = this.getContentResolver().query(Uri.withAppendedPath(Telephony.Carriers.CONTENT_URI, "current"), null, null, null, null);
if (apnCursor != null && apnCursor.moveToFirst()) {
        do {
            final String type = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.TYPE));
            if (!TextUtils.isEmpty(type) && (type.equalsIgnoreCase(Phone.APN_TYPE_ALL) || type.equalsIgnoreCase(Phone.APN_TYPE_MMS))) {
                String mmsc = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSC));
                String mmsProxy = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSPROXY));
                String port = apnCursor.getString(apnCursor.getColumnIndex(Telephony.Carriers.MMSPORT));   
                
                //TODO
            }
        } while (apnCursor.moveToNext());         
        apnCursor.close();
    }

Next, we will just combine need HTTP request from message PDUs, and send it using this class.

To be continued.

Receiving SMS

1. Let’s add this permission to AndroidManifest.xml:

<manifest ...>
    
    <uses-sdk .../>
    
    <uses-permission android:name="android.permission.RECEIVE_SMS"/>

    <application
        ...>
        ...

</manifest>

2. Let’s create an SmsReceiver class, based on internal BroadcastReceiver class:

public class SmsReceiver extends BroadcastReceiver {

    @Override
    public void onReceive(Context context, Intent intent) {
        Bundle intentExtras = intent.getExtras();
        
        if (intentExtras != null) {
            /* Get Messages */
            Object[] sms = (Object[]) intentExtras.get("pdus");
            
            for (int i = 0; i < sms.length; ++i) {
                /* Parse Each Message */
                SmsMessage smsMessage = SmsMessage.createFromPdu((byte[]) sms[i]);

                String phone = smsMessage.getOriginatingAddress();
                String message = smsMessage.getMessageBody().toString();
                
                Toast.makeText(context, phone + ": " + message, Toast.LENGTH_SHORT).show();
            }
        }
    }
}

3. Let’s declare our SmsReceiver in AndroidManifest.xml (in “application” section):

<manifest ...>
    
    ...

    <application
        ...>

        ...

       <receiver android:name=".SmsReceiver" android:exported="true" >
            <intent-filter android:priority="2147483647" >
                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
            </intent-filter>
        </receiver>

    </application>

</manifest>

android:name is path to receiver class, it can be absolute (i.e. “com.example.app.SmsReceiver”) or relational to main package (i.e. “.SmsReceiver” equals to “your.package.name.SmsReceiver” in our case).

Reading SMS History

Permissions

<manifest ...>
    
    <uses-sdk .../>
    
    <uses-permission android:name="android.permission.READ_SMS"/>

    <application
        ...>
        ...

</manifest>

Short Code Piece: All

Cursor cur = getContentResolver().query(Uri.parse("content://sms/"), null, null, null, null);

if (cur.moveToFirst()) { /* false = no sms */
    do {
       String msgInfo = "";

       for (int i = 0; i < cur.getColumnCount(); i++) {
           msgInfo += " " + cur.getColumnName(i) + ":" + cur.getString(i);
       }

       Toast.makeText(this, msgInfo, Toast.LENGTH_SHORT).show();
    } while (cur.moveToNext());
}

Short Code Piece: Inbox

Replace content://sms/ to content://sms/inbox

Short Code Piece: Sent

Replace content://sms/ to content://sms/sent

Short Code Piece: Drafts

Replace content://sms/ to content://sms/draft

Big Code Piece (You can download this project from the attachment above)

AndroidManifest.xml

Permission should be added, see above.

activity_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    android:padding="3dp"
    tools:context="${relativePackage}.${activityClass}" >

    <LinearLayout
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:orientation="horizontal" >

        <Button
            android:id="@+id/btnAll"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="All" />

        <Button
            android:id="@+id/btnInbox"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="3dp"
            android:text="Inbox" />

        <Button
            android:id="@+id/btnSent"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="3dp"
            android:text="Sent" />

        <Button
            android:id="@+id/btnDraft"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginLeft="3dp"
            android:text="Draft" />

    </LinearLayout>

    <ScrollView
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_marginTop="3dp"
        android:scrollbars="vertical" >
    
        <HorizontalScrollView
            android:layout_width="fill_parent"
            android:layout_height="fill_parent"
            android:scrollbars="horizontal" >
        
            <TableLayout
                android:id="@+id/tblMain"
                android:layout_width="fill_parent"
                android:layout_height="fill_parent">
        
            </TableLayout>
        
        </HorizontalScrollView>
    
    </ScrollView>

</LinearLayout>
MainActivity.java
package com.example.androidreadsms;

import android.app.Activity;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.view.Gravity;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.Toast;

public class MainActivity extends Activity
    implements OnClickListener {

    Button btnAll;
    Button btnInbox;
    Button btnSent;
    Button btnDraft;
    TableLayout tblMain;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        
        try {
            /*
             * Initializing Widgets
             */

            btnAll = (Button) findViewById(R.id.btnAll);
            btnAll.setOnClickListener(this);
            
            btnInbox = (Button) findViewById(R.id.btnInbox);
            btnInbox.setOnClickListener(this);
            
            btnSent = (Button) findViewById(R.id.btnSent);
            btnSent.setOnClickListener(this);
            
            btnDraft = (Button) findViewById(R.id.btnDraft);
            btnDraft.setOnClickListener(this);
            
            tblMain = (TableLayout) findViewById(R.id.tblMain);
            
        } catch (Exception ex) {
            Toast.makeText(this,
                    "Error in MainActivity.onCreate: " + ex.getMessage(),
                    Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public void onClick(View v) {
        Uri smsUri = Uri.parse("content://sms/");
        
        switch (v.getId()) {
        case R.id.btnInbox:
            smsUri = Uri.parse("content://sms/inbox");
            break;
        case R.id.btnSent:
            smsUri = Uri.parse("content://sms/sent");
            break;
        case R.id.btnDraft:
            smsUri = Uri.parse("content://sms/draft");
            break;
        }
        
        Cursor cursor = getContentResolver().query(smsUri, null, null, null, null);

        Cursor2TableLayout(cursor, tblMain);
    }
    
    public void Cursor2TableLayout(Cursor cur, TableLayout tblLayout) {
        
        /* Clearing Table If Contains Any Rows/Headers */
        tblLayout.removeAllViews();
        
        /* Moving To First */
        if (!cur.moveToFirst()) { /* false = cursor is empty */
            return;
        }

        /* Column Headers */
        
        TableRow headersRow = new TableRow(this);
        
        for (int j = 0; j < cur.getColumnCount(); j++) {
            TextView textView = new TextView(this);

            textView.setGravity(Gravity.CENTER_HORIZONTAL);
            
            textView.setText(cur.getColumnName(j));

            textView.setPadding(0, 0, 5, 0);
            
            if (Build.VERSION.SDK_INT >= 11) { /* If running Android 3.0+ */
                textView.setAlpha(0.8f);
            } else {
                /* Phones with Android 2.x are a rarity now,
                 * but until recently (June, 2015) I have seen them
                 * in the store...
                 * Also, I myself have such gadget and using it sometimes... */
                AlphaAnimation animation = new AlphaAnimation(0.8f, 0.8f);
                animation.setDuration(0);
                animation.setFillAfter(true);
                textView.startAnimation(animation);
            }
        
            headersRow.addView(textView);
        }

        headersRow.setPadding(10, 10, 10, 10);

        tblLayout.addView(headersRow);
        
        /* Rows */
    
        for (int i = 0; i < cur.getCount(); i++) {

            TableRow tableRow = new TableRow(this);

            for (int j = 0; j < cur.getColumnCount(); j++) {
                    TextView textView = new TextView(this);

                    textView.setGravity(Gravity.CENTER_HORIZONTAL);

                    textView.setText(cur.getString(j));

                    textView.setPadding(0, 0, 5, 0);
                
                    tableRow.addView(textView);
            }

            tableRow.setPadding(10, 10, 10, 10);

            tblLayout.addView(tableRow);
            cur.moveToNext();
        }

        cur.close();
    }
}

出处:https://www.codeproject.com/Articles/1044639/Android-Java-How-To-Send-SMS-Receive-SMS-Get-SMS-M

关于作者
ultracpy
评论

你必须 登录 提交评论