Create a SMS application with power features
DownloadKeywords: ContentResolver SmsManager AsyncTask ProgressDialog BroadcastReceiver ListActivity AlertDialog Shape drawable PreferenceActivity
Contents- Overview
- Create a new Eclipse Android project
- The Android Manifest file
- Application Theme
- The Application class
- The Preferences screen
- The Main screen
- SMS Export AsyncTask
- Reading SMS
- Sending SMS
9. Reading SMS
In MainActivity class we had overridden onListItemClick() method of ListActivity to launch a new screen for reading a particular SMS message. Now we will implement the ReadActivity class.public class ReadActivity extends Activity { private TextView tv; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.read); tv = (TextView) findViewById(R.id.textView1); String id = getIntent().getStringExtra("id"); String[] projection = {"_id", "address", "date", "body"}; String selection = "_id = ?"; String[] selectionArgs = {id}; Cursor c = getContentResolver().query(SmsXp.INBOX_URI, projection, selection, selectionArgs, null); if (c.moveToFirst()) { setTitle(c.getString(c.getColumnIndex("address"))); tv.setText(c.getString(c.getColumnIndex("body"))); } } }There is not much in the layout either. Here is an outline of the layout shown above.
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="fill_parent" android:layout_height="fill_parent" android:orientation="vertical" > <RelativeLayout android:layout_width="fill_parent" android:layout_height="wrap_content" android:background="@drawable/bar" > </RelativeLayout> <ScrollView android:id="@+id/scrollView1" android:layout_width="fill_parent" android:layout_height="0dip" android:layout_weight="1" > <TextView android:id="@+id/textView1" android:layout_width="fill_parent" android:layout_height="wrap_content" android:text="TextView" /> </ScrollView> </LinearLayout>The RelativeLayout is shown as empty but it just contains a bunch of ImageButtons. We will see how the toolbar was create using shape drawable. Create an xml file inside drawable folder with the following content.
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android" > <gradient android:startColor="#8d95a0" android:endColor="#485260" android:angle="270" /> <stroke android:width="1dp" android:color="#485260" /> </shape>
10. Sending SMS
The layout of compose screen is similar to the one just discussed. Let's take a look at the Activity which implements the functionality of sending SMS.public class ComposeActivity extends Activity { private static final String ACTION_SENT = "com.appsrox.smsxp.SENT"; private static final int DIALOG_SENDTO = 1; private EditText et1; private ImageButton ib3; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.compose); setTitle("New Message"); et1 = (EditText) findViewById(R.id.editText1); ib3 = (ImageButton) findViewById(R.id.imageButton3); } public void onClick(View v) { switch (v.getId()) { case R.id.imageButton3: try { Intent sendIntent = new Intent(Intent.ACTION_VIEW); sendIntent.putExtra("sms_body", et1.getText().toString()); sendIntent.setType("vnd.android-dir/mms-sms"); startActivity(sendIntent); } catch (ActivityNotFoundException e) { showDialog(DIALOG_SENDTO); } break; } } @Override protected Dialog onCreateDialog(int id) { switch (id) { case DIALOG_SENDTO: final EditText et = new EditText(this); et.setInputType(EditorInfo.TYPE_CLASS_PHONE); return new AlertDialog.Builder(this) .setTitle("To") .setView(et) .setCancelable(true) .setPositiveButton("Send", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { String to = et.getText().toString().trim(); sendSMS(to); } }) .setNegativeButton("Cancel", new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialog, int id) { dialog.dismiss(); } }) .create(); } return super.onCreateDialog(id); } private void sendSMS(String to) { SmsManager manager = SmsManager.getDefault() ; manager.sendTextMessage(to, null, et1.getText().toString(), null, null); } }
There are two ways in which an application can perform an activity. Either it can do it itself or it can launch another application which can handle the intent.
We have used both these ways for sending SMS. In the onclick handler, first we check if there is any default application which can send SMS. We set the proper intent type and put the message as an extra to the intent. If no activity is found, we use SmsManager provided by Android to send the SMS.
We can register BroadcastReceivers to get notified of the status of sending and delivery of SMS.
private BroadcastReceiver sent = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { switch(getResultCode()) { case Activity.RESULT_OK: Toast.makeText(ComposeActivity.this, "Sent successfully", Toast.LENGTH_SHORT).show(); break; case SmsManager.RESULT_ERROR_GENERIC_FAILURE: case SmsManager.RESULT_ERROR_NO_SERVICE: case SmsManager.RESULT_ERROR_NULL_PDU: case SmsManager.RESULT_ERROR_RADIO_OFF: Toast.makeText(ComposeActivity.this, "Error sending SMS", Toast.LENGTH_LONG).show(); break; } unregisterReceiver(this); } }; private void sendSMS(String to) { registerReceiver(sent, new IntentFilter(ACTION_SENT)); SmsManager manager = SmsManager.getDefault() ; PendingIntent sentIntent = PendingIntent.getBroadcast(this, 0, new Intent(ACTION_SENT), 0); manager.sendTextMessage(to, null, et1.getText().toString(), sentIntent, null); }The above code snippet shows how we can know the status of sending of message. Similarly we can create a receiver for delivery of message.
There are few other things we can implement in the Activity to improve usability of the application. Like attach a TextWatcher to the EditText field to disable the send button for an empty message.
et1.addTextChangedListener(new TextWatcher() { @Override public void onTextChanged(CharSequence s, int start, int before, int count) {} @Override public void beforeTextChanged(CharSequence s, int start, int count, int after) {} @Override public void afterTextChanged(Editable s) { if (TextUtils.isEmpty(et1.getText().toString())) ib3.setEnabled(false); else ib3.setEnabled(true); } });Add the above piece of code in the onCreate() method. To support this functionality we can create a selector drawable and set it as the src for the ImageButton in the layout file.
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android" > <item android:state_enabled="true" android:drawable="@drawable/send" /> <item android:state_enabled="false" android:drawable="@drawable/send_" /> </selector>
This completes the tutorial. However, we have skipped few details which you can find in our previous tutorials.