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
The MainActivity class is responsible for rendering the list view and contains logic for handling all UI events on the screen. Here is an outline of the class.
public class MainActivity extends ListActivity { private Typeface font; private TextView headingText; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); requestWindowFeature(Window.FEATURE_NO_TITLE); setContentView(R.layout.main); font = Typeface.createFromAsset(getAssets(), "fonts/OpenSans.ttf"); headingText = (TextView) findViewById(R.id.heading_tv); headingText.setTypeface(font); } @Override protected void onResume() { super.onResume(); Cursor c = getContentResolver().query(SmsXp.inboxUri, null, null, null, null); startManagingCursor(c); String[] from = {"address", "date", "body"}; int[] to = {R.id.textView1, R.id.textView2, R.id.textView3}; SimpleCursorAdapter adapter = new SimpleCursorAdapter( this, R.layout.row, c, from, to); setListAdapter(adapter); } @Override protected void onListItemClick(ListView l, View v, int position, long id) { Intent intent = new Intent(); intent.setClass(this, ReadActivity.class); intent.putExtra("id", String.valueOf(id)); startActivity(intent); } }We created a fonts sub-directory under assets directory in the project structure and placed the .ttf file for the font inside it. Now we can use the font by creating a Typeface instance and setting it to the TextView.
The other important thing to notice is how we read SMS using a content resolver. We have already added the required permission in the manifest file for reading SMS. The important ingredient here is the inbox Uri which we defined in the Application class.
public static final Uri INBOX_URI = Uri.parse("content://sms/inbox");ContentProvider is an Android component which makes it possible for one application to access data of another application through a ContentResolver.
The SMS inbox uri does not belong to the documented list of content providers provided by Android. You might be interested in reading this article.
We have omitted a lot of code from the Activity class since we have discussed about them in previous articles. So we won't be covering stuffs like custom list view rows, adapter view binder, saving instance state, showing dialogs, etc. in this tutorial.
Next we will implement the functionality of exporting SMS to a text file and sharing the file.
8. SMS Export AsyncTask
One of the buttons in the toolbar allows user to export the list of SMS to a text file for archiving purposes. This is a time consuming task depending on the size of the SMS list, and hence a good candidate for using an AsyncTask.Create an inner class called ExportTask in MainActivity.
class ExportTask extends AsyncTask<Void, Integer, Uri> { ProgressDialog pDialog; @Override protected void onPreExecute() { super.onPreExecute(); pDialog = new ProgressDialog(MainActivity.this); pDialog.setMessage("Exporting to file ..."); pDialog.setIndeterminate(false); pDialog.setMax(100); pDialog.setProgress(0); pDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); pDialog.setCancelable(false); pDialog.show(); } @Override protected Uri doInBackground(Void... params) { if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) { FileOutputStream fos = null; try { File f = new File(Environment.getExternalStorageDirectory(), TEMP_FILE); fos = new FileOutputStream(f); cursor = ((SimpleCursorAdapter)getListAdapter()).getCursor(); int count = cursor.getCount(), i = 0; StringBuilder sb = new StringBuilder(); if (cursor.moveToFirst()) { do { sb.append(cursor.getString(cursor.getColumnIndex("address"))) .append("\n"); sb.append(cursor.getString(cursor.getColumnIndex("body"))) .append("\n"); sb.append("\n"); publishProgress(++i*100/count); } while (!isCancelled() && cursor.moveToNext()); } fos.write(sb.toString().getBytes()); return Uri.fromFile(f); } catch (Exception e) { } finally { if (fos != null) { try { fos.close(); } catch (IOException e) {} } } } return null; } @Override protected void onProgressUpdate(Integer... values) { super.onProgressUpdate(values); pDialog.setProgress(values[0]); } @Override protected void onPostExecute(Uri result) { super.onPostExecute(result); pDialog.dismiss(); if (result == null) { Toast.makeText(MainActivity.this, "Export task failed!", Toast.LENGTH_LONG).show(); return; } Intent shareIntent = new Intent(); shareIntent.setAction(Intent.ACTION_SEND); shareIntent.setType("text/*"); shareIntent.putExtra(Intent.EXTRA_STREAM, result); startActivity(Intent.createChooser(shareIntent, "Send file to")); } }There are quite a few things to note in the above code snippet. First, we are using a ProgressDialog to notify user about the progress of the export operation. We create a ProgressDialog in onPreExecute() and dismiss it in onPostExecute(). We have overridden onProgressUpdate() method of AsyncTask to set the progress.
Second, the actual work of exporting data is done in doInBackground() method where we first check the availability of SD card. We have already requested permission to write to external storage in the manifest file. Also, we publish progress in each loop which updates the progress bar continously.
Third, in onPostExecute() method we start a new activity for sharing the exported file. We create a chooser so the user will have a choice if multiple methods of sharing is possible.
To execute AsyncTask we need to invoke it in the onclick handler. We have already specified onClick function in main.xml for the export button.
public void onClick(View v) { switch (v.getId()) { case R.id.imageButton1: Intent settingsIntent = new Intent(); settingsIntent.setClass(this, SettingsActivity.class); startActivity(settingsIntent); break; case R.id.imageButton2: Intent composeIntent = new Intent(); composeIntent.setClass(this, ComposeActivity.class); startActivity(composeIntent); break; case R.id.imageButton5: task = new ExportTask(); task.execute(); break; } }And finally we override onPause() method of Mainactivity to cancel the export task if it's still in progress..
@Override protected void onPause() { if (task != null) { task.cancel(false); task.pDialog.dismiss(); } super.onPause(); }
Next we will implement the activities for reading and composing of messages.