Add SMS manager demo
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 6615d71..1e3f66b 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -29,6 +29,8 @@
     <uses-permission android:name="android.permission.INTERNET" />
     <uses-permission android:name="android.permission.SET_WALLPAPER" />
     <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.SEND_SMS" />
+    <uses-permission android:name="android.permission.RECEIVE_SMS" />
 
     <!-- We will request access to the camera, saying we require a camera
          of some sort but not one with autofocus capability. -->
@@ -651,6 +653,23 @@
             </intent-filter>
         </activity>
 
+        <activity android:name=".os.SmsMessagingDemo" android:label="OS/SMS Messaging">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.SAMPLE_CODE" />
+            </intent-filter>
+        </activity>
+
+        <activity android:name=".os.SmsReceivedDialog"
+                  android:theme="@android:style/Theme.Translucent.NoTitleBar"
+                  android:launchMode="singleInstance" />
+
+        <receiver android:name=".os.SmsMessageReceiver" android:enabled="false">
+            <intent-filter>
+                <action android:name="android.provider.Telephony.SMS_RECEIVED" />
+            </intent-filter>
+        </receiver>
+
         <!-- ************************************* -->
         <!--     ANIMATION PACKAGE SAMPLES         -->
         <!-- ************************************* -->
diff --git a/samples/ApiDemos/res/layout/sms_demo.xml b/samples/ApiDemos/res/layout/sms_demo.xml
new file mode 100644
index 0000000..1074f52
--- /dev/null
+++ b/samples/ApiDemos/res/layout/sms_demo.xml
@@ -0,0 +1,67 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2010 The Android Open Source Project
+
+     Licensed under the Apache License, Version 2.0 (the "License");
+     you may not use this file except in compliance with the License.
+     You may obtain a copy of the License at
+
+          http://www.apache.org/licenses/LICENSE-2.0
+
+     Unless required by applicable law or agreed to in writing, software
+     distributed under the License is distributed on an "AS IS" BASIS,
+     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+     See the License for the specific language governing permissions and
+     limitations under the License.
+-->
+
+<!-- Demonstrates sending and receiving SMS messages.
+     See corresponding Java code SmsMessagingDemo.java
+-->
+
+<ScrollView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent">
+    <LinearLayout
+        android:orientation="vertical"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:padding="6dip">
+        <TextView
+            android:textColor="#ff0000"
+            android:textStyle="bold"
+            android:text="@string/sms_warning"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content" />
+        <CheckBox
+            android:id="@+id/sms_enable_receiver"
+            android:text="@string/sms_enable_receiver"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+        <TableLayout
+            android:padding="6dip"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:stretchColumns="1">
+            <TableRow android:layout_width="match_parent">
+                <TextView android:text="@string/sms_recipient_label" />
+                <EditText android:id="@+id/sms_recipient" />
+            </TableRow>
+            <TableRow>
+                <TextView android:text="@string/sms_content_label" />
+                <EditText android:id="@+id/sms_content" />
+            </TableRow>
+            <TableRow>
+                <Button
+                    android:id="@+id/sms_send_message"
+                    android:text="@string/sms_send_message"
+                    android:layout_column="1" />
+            </TableRow>
+            <TableRow>
+                <TextView
+                    android:id="@+id/sms_status"
+                    android:layout_column="1" />
+            </TableRow>
+        </TableLayout>
+    </LinearLayout>
+</ScrollView>
diff --git a/samples/ApiDemos/res/values/strings.xml b/samples/ApiDemos/res/values/strings.xml
index c7bb84e..799d67f 100644
--- a/samples/ApiDemos/res/values/strings.xml
+++ b/samples/ApiDemos/res/values/strings.xml
@@ -879,5 +879,21 @@
     <string name="appwidget_configure_instructions">This text will be shown before the date in our example widget.</string>
     <string name="appwidget_prefix_default">Oh hai</string>
     <string name="appwidget_text_format"><xliff:g id="prefix">%1$s</xliff:g>: <xliff:g id="time">%2$s</xliff:g></string>
+
+    <!-- ============================ -->
+    <!--  SMS Messaging examples strings  -->
+    <!-- ============================ -->
+
+    <string name="sms_warning">
+      WARNING: this demo can send actual text messages (one at a time), so be sure to
+      test with the Android emulator or have a text messaging plan with your carrier.
+    </string>
+    <string name="sms_enable_receiver">Enable SMS broadcast receiver</string>
+    <string name="sms_recipient_label">Recipient #</string>
+    <string name="sms_content_label">Message Body</string>
+    <string name="sms_send_message">Send</string>
+    <string name="sms_speak_string_format">Message from "%1$s": %2$s</string>
+    <string name="reply">Reply</string>
+    <string name="dismiss">Dismiss</string>
 </resources>
 
diff --git a/samples/ApiDemos/src/com/example/android/apis/os/SmsMessageReceiver.java b/samples/ApiDemos/src/com/example/android/apis/os/SmsMessageReceiver.java
new file mode 100644
index 0000000..ec80955
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/os/SmsMessageReceiver.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.os;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract;
+import android.telephony.SmsMessage;
+
+public class SmsMessageReceiver extends BroadcastReceiver {
+    /** Tag string for our debug logs */
+    private static final String TAG = "SmsMessageReceiver";
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Bundle extras = intent.getExtras();
+        if (extras == null)
+            return;
+
+        Object[] pdus = (Object[]) extras.get("pdus");
+
+        for (int i = 0; i < pdus.length; i++) {
+            SmsMessage message = SmsMessage.createFromPdu((byte[]) pdus[i]);
+            String fromAddress = message.getOriginatingAddress();
+            String fromDisplayName = fromAddress;
+
+            Uri uri;
+            String[] projection;
+
+            // If targeting Donut or below, use
+            // Contacts.Phones.CONTENT_FILTER_URL and
+            // Contacts.Phones.DISPLAY_NAME
+            uri = Uri.withAppendedPath(
+                    ContactsContract.PhoneLookup.CONTENT_FILTER_URI,
+                    Uri.encode(fromAddress));
+            projection = new String[] { ContactsContract.PhoneLookup.DISPLAY_NAME };
+
+            // Query the filter URI
+            Cursor cursor = context.getContentResolver().query(uri, projection, null, null, null);
+            if (cursor != null) {
+                if (cursor.moveToFirst())
+                    fromDisplayName = cursor.getString(0);
+
+                cursor.close();
+            }
+
+            // Trigger the main activity to fire up a dialog that shows/reads the received messages
+            Intent di = new Intent();
+            di.setClass(context, SmsReceivedDialog.class);
+            di.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
+            di.putExtra(SmsReceivedDialog.SMS_FROM_ADDRESS_EXTRA, fromAddress);
+            di.putExtra(SmsReceivedDialog.SMS_FROM_DISPLAY_NAME_EXTRA, fromDisplayName);
+            di.putExtra(SmsReceivedDialog.SMS_MESSAGE_EXTRA, message.getMessageBody().toString());
+            context.startActivity(di);
+
+            // For the purposes of this demo, we'll only handle the first received message.
+            break;
+        }
+    }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/os/SmsMessagingDemo.java b/samples/ApiDemos/src/com/example/android/apis/os/SmsMessagingDemo.java
new file mode 100644
index 0000000..94f4e5a
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/os/SmsMessagingDemo.java
@@ -0,0 +1,159 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.os;
+
+import java.util.List;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.pm.PackageManager;
+import android.graphics.Color;
+import android.os.Bundle;
+import android.telephony.SmsManager;
+import android.text.TextUtils;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.CompoundButton;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.Toast;
+import android.widget.CompoundButton.OnCheckedChangeListener;
+
+import com.example.android.apis.R;
+
+public class SmsMessagingDemo extends Activity {
+    /** Tag string for our debug logs */
+    private static final String TAG = "SmsMessagingDemo";
+
+    public static final String SMS_RECIPIENT_EXTRA = "com.example.android.apis.os.SMS_RECIPIENT";
+
+    public static final String ACTION_SMS_SENT = "com.example.android.apis.os.SMS_SENT_ACTION";
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        setContentView(R.layout.sms_demo);
+
+        if (getIntent().hasExtra(SMS_RECIPIENT_EXTRA)) {
+            ((TextView) findViewById(R.id.sms_recipient)).setText(getIntent().getExtras()
+                    .getString(SMS_RECIPIENT_EXTRA));
+            ((TextView) findViewById(R.id.sms_content)).requestFocus();
+        }
+
+        // Enable or disable the broadcast receiver depending on the checked
+        // state of the checkbox.
+        CheckBox enableCheckBox = (CheckBox) findViewById(R.id.sms_enable_receiver);
+
+        final PackageManager pm = this.getPackageManager();
+        final ComponentName componentName = new ComponentName("com.example.android.apis",
+                "com.example.android.apis.os.SmsMessageReceiver");
+
+        enableCheckBox.setChecked(pm.getComponentEnabledSetting(componentName) ==
+                                  PackageManager.COMPONENT_ENABLED_STATE_ENABLED);
+
+        enableCheckBox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
+            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
+                Log.d(TAG, (isChecked ? "Enabling" : "Disabling") + " SMS receiver");
+
+                pm.setComponentEnabledSetting(componentName,
+                        isChecked ? PackageManager.COMPONENT_ENABLED_STATE_ENABLED
+                                : PackageManager.COMPONENT_ENABLED_STATE_DISABLED,
+                        PackageManager.DONT_KILL_APP);
+            }
+        });
+
+        final EditText recipientTextEdit = (EditText) SmsMessagingDemo.this
+                .findViewById(R.id.sms_recipient);
+        final EditText contentTextEdit = (EditText) SmsMessagingDemo.this
+                .findViewById(R.id.sms_content);
+        final TextView statusView = (TextView) SmsMessagingDemo.this.findViewById(R.id.sms_status);
+
+        // Watch for send button clicks and send text messages.
+        Button sendButton = (Button) findViewById(R.id.sms_send_message);
+        sendButton.setOnClickListener(new OnClickListener() {
+            public void onClick(View v) {
+                if (TextUtils.isEmpty(recipientTextEdit.getText())) {
+                    Toast.makeText(SmsMessagingDemo.this, "Please enter a message recipient.",
+                            Toast.LENGTH_SHORT).show();
+                    return;
+                }
+
+                if (TextUtils.isEmpty(contentTextEdit.getText())) {
+                    Toast.makeText(SmsMessagingDemo.this, "Please enter a message body.",
+                            Toast.LENGTH_SHORT).show();
+                    return;
+                }
+
+                recipientTextEdit.setEnabled(false);
+                contentTextEdit.setEnabled(false);
+
+                SmsManager sms = SmsManager.getDefault();
+
+                List<String> messages = sms.divideMessage(contentTextEdit.getText().toString());
+
+                String recipient = recipientTextEdit.getText().toString();
+                for (String message : messages) {
+                    sms.sendTextMessage(recipient, null, message, PendingIntent.getBroadcast(
+                            SmsMessagingDemo.this, 0, new Intent(ACTION_SMS_SENT), 0), null);
+                }
+            }
+        });
+
+        // Register broadcast receivers for SMS sent and delivered intents
+        registerReceiver(new BroadcastReceiver() {
+            @Override
+            public void onReceive(Context context, Intent intent) {
+                String message = null;
+                boolean error = true;
+                switch (getResultCode()) {
+                case Activity.RESULT_OK:
+                    message = "Message sent!";
+                    error = false;
+                    break;
+                case SmsManager.RESULT_ERROR_GENERIC_FAILURE:
+                    message = "Error.";
+                    break;
+                case SmsManager.RESULT_ERROR_NO_SERVICE:
+                    message = "Error: No service.";
+                    break;
+                case SmsManager.RESULT_ERROR_NULL_PDU:
+                    message = "Error: Null PDU.";
+                    break;
+                case SmsManager.RESULT_ERROR_RADIO_OFF:
+                    message = "Error: Radio off.";
+                    break;
+                }
+
+                recipientTextEdit.setEnabled(true);
+                contentTextEdit.setEnabled(true);
+                contentTextEdit.setText("");
+
+                statusView.setText(message);
+                statusView.setTextColor(error ? Color.RED : Color.GREEN);
+            }
+        }, new IntentFilter(ACTION_SMS_SENT));
+    }
+}
diff --git a/samples/ApiDemos/src/com/example/android/apis/os/SmsReceivedDialog.java b/samples/ApiDemos/src/com/example/android/apis/os/SmsReceivedDialog.java
new file mode 100644
index 0000000..0df93a7
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/os/SmsReceivedDialog.java
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.os;
+
+import java.util.Locale;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.os.Bundle;
+import android.speech.tts.TextToSpeech;
+import android.speech.tts.TextToSpeech.OnInitListener;
+import android.util.Log;
+
+import com.example.android.apis.R;
+
+public class SmsReceivedDialog extends Activity implements OnInitListener {
+    private static final String TAG = "SmsReceivedDialog";
+
+    private static final int DIALOG_SHOW_MESSAGE = 1;
+
+    public static final String SMS_FROM_ADDRESS_EXTRA = "com.example.android.apis.os.SMS_FROM_ADDRESS";
+    public static final String SMS_FROM_DISPLAY_NAME_EXTRA = "com.example.android.apis.os.SMS_FROM_DISPLAY_NAME";
+    public static final String SMS_MESSAGE_EXTRA = "com.example.android.apis.os.SMS_MESSAGE";
+
+    private TextToSpeech mTts;
+
+    private String mFromDisplayName;
+    private String mFromAddress;
+    private String mMessage;
+    private String mFullBodyString;
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        mFromAddress = getIntent().getExtras().getString(SMS_FROM_ADDRESS_EXTRA);
+        mFromDisplayName = getIntent().getExtras().getString(SMS_FROM_DISPLAY_NAME_EXTRA);
+        mMessage = getIntent().getExtras().getString(SMS_MESSAGE_EXTRA);
+
+        mFullBodyString = String.format(
+                getResources().getString(R.string.sms_speak_string_format),
+                mFromDisplayName,
+                mMessage);
+
+        showDialog(DIALOG_SHOW_MESSAGE);
+        mTts = new TextToSpeech(this, this);
+    }
+
+    public void onInit(int status) {
+        if (status == TextToSpeech.SUCCESS) {
+            int result = mTts.setLanguage(Locale.US);
+            if (result == TextToSpeech.LANG_MISSING_DATA
+                    || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+                Log.e(TAG, "TTS language is not available.");
+            } else {
+                mTts.speak(mFullBodyString, TextToSpeech.QUEUE_ADD, null);
+            }
+        } else {
+            // Initialization failed.
+            Log.e(TAG, "Could not initialize TTS.");
+        }
+    }
+
+    @Override
+    protected Dialog onCreateDialog(int id) {
+        switch (id) {
+        case DIALOG_SHOW_MESSAGE:
+            return new AlertDialog.Builder(this)
+                    .setIcon(android.R.drawable.ic_dialog_email)
+                    .setTitle("Message Received")
+                    .setMessage(mFullBodyString)
+                    .setPositiveButton(R.string.reply, new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            // Begin creating the reply with the SmsMessagingDemo activity
+                            Intent i = new Intent();
+                            i.setClass(SmsReceivedDialog.this, SmsMessagingDemo.class);
+                            i.putExtra(SmsMessagingDemo.SMS_RECIPIENT_EXTRA, mFromAddress);
+                            startActivity(i);
+
+                            dialog.dismiss();
+                            finish();
+                        }
+                    })
+                    .setNegativeButton(R.string.dismiss, new DialogInterface.OnClickListener() {
+                        public void onClick(DialogInterface dialog, int whichButton) {
+                            dialog.dismiss();
+                            finish();
+                        }
+                    })
+                    .setOnCancelListener(new DialogInterface.OnCancelListener() {
+                        public void onCancel(DialogInterface dialog) {
+                            finish();
+                        }
+                    }).create();
+        }
+        return null;
+    }
+}