FM Radio: Add support for FM Radio in Android

Two sample applications for the proposed FM Radio API.

Change-Id: I8a0b0099f6ea20d9f53822127498d5e603eb3255

Signed-off-by: Christian Bejram <christian.bejram@stericsson.com>

Conflicts:
	build/sdk.atree
diff --git a/build/sdk.atree b/build/sdk.atree
index 038dafc..b6c42eb 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -205,6 +205,8 @@
 development/samples/RenderScript/HelloCompute  samples/${PLATFORM_NAME}/RenderScript/HelloCompute
 development/samples/RenderScript/HelloWorld    samples/${PLATFORM_NAME}/RenderScript/HelloWorld
 development/samples/RenderScript/MiscSamples   samples/${PLATFORM_NAME}/RenderScript/MiscSamples
+development/samples/FmRadioReceiver          samples/${PLATFORM_NAME}/FmRadioReceiver
+development/samples/FmRadioTransmitter       samples/${PLATFORM_NAME}/FmRadioTransmitter
 
 # NOTICE files are copied by build/core/Makefile from sdk.git
 sdk/files/sdk_files_NOTICE.txt samples/${PLATFORM_NAME}/NOTICE.txt
diff --git a/samples/FmRadioReceiver/Android.mk b/samples/FmRadioReceiver/Android.mk
new file mode 100755
index 0000000..177ce93
--- /dev/null
+++ b/samples/FmRadioReceiver/Android.mk
@@ -0,0 +1,10 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := FmRadioReceiver
+
+include $(BUILD_PACKAGE)
diff --git a/samples/FmRadioReceiver/AndroidManifest.xml b/samples/FmRadioReceiver/AndroidManifest.xml
new file mode 100755
index 0000000..f1aae65
--- /dev/null
+++ b/samples/FmRadioReceiver/AndroidManifest.xml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+	package="com.example.android.fmradioreceiver" android:versionCode="1"
+	android:versionName="1.0">
+	<application android:icon="@drawable/icon" android:label="@string/app_name">
+		<activity android:name=".FmRadioReceiver" android:label="@string/app_name">
+			<intent-filter>
+				<action android:name="android.intent.action.MAIN" />
+				<category android:name="android.intent.category.LAUNCHER" />
+			</intent-filter>
+		</activity>
+	</application>
+	<uses-permission android:name="com.stericsson.permission.FM_RADIO_TRANSMITTER"></uses-permission>
+	<uses-permission android:name="com.stericsson.permission.FM_RADIO_RECEIVER"></uses-permission>
+</manifest>
diff --git a/samples/FmRadioReceiver/_index.html b/samples/FmRadioReceiver/_index.html
new file mode 100755
index 0000000..080591e
--- /dev/null
+++ b/samples/FmRadioReceiver/_index.html
@@ -0,0 +1,15 @@
+<p>FmRadioReceiver is a sample application that demonstrates the use of the
+<a href="../../../reference/android/fm/FmReceiver.html">android.fm.FmReceiver</a>
+class to implement a FM Radio receiver in an application. The application allows the
+user to tune to any local radio stations, switch between them, scan for new stations and
+mute the radio. It also allows the user to change the radio band to their suit their location.
+The use of listeners is shown to display the tuned station name if RDS data is present.</p>
+
+<p>The FmRadioReceiver.java file in FmRadioReceiver also illustrates the interaction with
+the MediaPlayer. </p>
+
+<p><strong>See also:</strong><br/>
+<a href="../../../reference/android/fm/FmBand.html">FmBand</a></p>
+<a href="../../../reference/android/fm/FmTransmitter.html">FmTransmitter</a></p>
+
+<img alt="" src="../images/FmRadioReceiver.png"  />
diff --git a/samples/FmRadioReceiver/res/drawable/backward.png b/samples/FmRadioReceiver/res/drawable/backward.png
new file mode 100755
index 0000000..c53408f
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/backward.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/backwardbutton.xml b/samples/FmRadioReceiver/res/drawable/backwardbutton.xml
new file mode 100755
index 0000000..22ff201
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/backwardbutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/backwardpress" />
+	<item android:state_focused="true" android:drawable="@drawable/backwardpress" />
+	<item android:drawable="@drawable/backward" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioReceiver/res/drawable/backwardpress.png b/samples/FmRadioReceiver/res/drawable/backwardpress.png
new file mode 100755
index 0000000..3fcfada
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/backwardpress.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/forward.png b/samples/FmRadioReceiver/res/drawable/forward.png
new file mode 100755
index 0000000..a3f6a75
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/forward.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/forwardbutton.xml b/samples/FmRadioReceiver/res/drawable/forwardbutton.xml
new file mode 100755
index 0000000..bbefffc
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/forwardbutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/forwardpress" />
+	<item android:state_focused="true" android:drawable="@drawable/forwardpress" />
+	<item android:drawable="@drawable/forward" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioReceiver/res/drawable/forwardpress.png b/samples/FmRadioReceiver/res/drawable/forwardpress.png
new file mode 100755
index 0000000..417b99d
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/forwardpress.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/icon.png b/samples/FmRadioReceiver/res/drawable/icon.png
new file mode 100755
index 0000000..a07c69f
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/icon.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/pause.png b/samples/FmRadioReceiver/res/drawable/pause.png
new file mode 100755
index 0000000..ff5ffd7
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/pause.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/pausebutton.xml b/samples/FmRadioReceiver/res/drawable/pausebutton.xml
new file mode 100755
index 0000000..92b7c89
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/pausebutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/pausepress" />
+	<item android:state_focused="true" android:drawable="@drawable/pausepress" />
+	<item android:drawable="@drawable/pause" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioReceiver/res/drawable/pausepress.png b/samples/FmRadioReceiver/res/drawable/pausepress.png
new file mode 100755
index 0000000..e4d978c
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/pausepress.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/play.png b/samples/FmRadioReceiver/res/drawable/play.png
new file mode 100755
index 0000000..5b77d3b
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/play.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/playbutton.xml b/samples/FmRadioReceiver/res/drawable/playbutton.xml
new file mode 100755
index 0000000..12ba91e
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/playbutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/playpress" />
+	<item android:state_focused="true" android:drawable="@drawable/playpress" />
+	<item android:drawable="@drawable/play" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioReceiver/res/drawable/playpress.png b/samples/FmRadioReceiver/res/drawable/playpress.png
new file mode 100755
index 0000000..c0063c7
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/playpress.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/rescan.png b/samples/FmRadioReceiver/res/drawable/rescan.png
new file mode 100755
index 0000000..2d2cc2a
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/rescan.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/drawable/rescanbutton.xml b/samples/FmRadioReceiver/res/drawable/rescanbutton.xml
new file mode 100755
index 0000000..9585ff5
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/rescanbutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/rescanpress" />
+	<item android:state_focused="true" android:drawable="@drawable/rescanpress" />
+	<item android:drawable="@drawable/rescan" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioReceiver/res/drawable/rescanpress.png b/samples/FmRadioReceiver/res/drawable/rescanpress.png
new file mode 100755
index 0000000..a03ce7a
--- /dev/null
+++ b/samples/FmRadioReceiver/res/drawable/rescanpress.png
Binary files differ
diff --git a/samples/FmRadioReceiver/res/layout/main.xml b/samples/FmRadioReceiver/res/layout/main.xml
new file mode 100755
index 0000000..355823d
--- /dev/null
+++ b/samples/FmRadioReceiver/res/layout/main.xml
@@ -0,0 +1,49 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout android:layout_width="fill_parent"
+	android:layout_height="fill_parent" android:orientation="vertical"
+	xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/MainWindow"
+	android:gravity="center">
+	<TextView android:layout_width="fill_parent"
+		android:layout_height="wrap_content" android:text="@string/fmradio"
+		android:textSize="50px" android:id="@+id/PSNTextView" android:gravity="center"></TextView>
+	<TextView android:layout_width="fill_parent"
+		android:layout_height="wrap_content" android:id="@+id/FrequencyTextView"
+		android:lines="1" android:textSize="90px" android:gravity="center"
+		android:text="@string/dashes"></TextView>
+	<LinearLayout android:id="@+id/TopRow"
+		android:layout_height="wrap_content" android:layout_width="fill_parent"
+		android:gravity="center">
+		<LinearLayout android:layout_width="fill_parent"
+			android:layout_height="wrap_content" android:layout_weight="1"
+			android:gravity="right" android:paddingRight="10px">
+			<Button android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:id="@+id/ScanDown"
+				android:background="@drawable/backwardbutton"/>
+		</LinearLayout>
+		<LinearLayout android:layout_width="fill_parent"
+			android:layout_height="wrap_content" android:layout_weight="1"
+			android:gravity="left" android:paddingLeft="10px">
+			<Button android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:id="@+id/ScanUp"
+				android:background="@drawable/forwardbutton" />
+		</LinearLayout>
+	</LinearLayout>
+	<LinearLayout android:id="@+id/BottonRow"
+		android:layout_height="wrap_content" android:layout_width="fill_parent"
+		android:gravity="center">
+		<LinearLayout android:layout_width="fill_parent"
+			android:layout_height="wrap_content" android:layout_weight="1"
+			android:gravity="right" android:paddingRight="10px">
+			<Button android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:id="@+id/FullScan"
+				android:background="@drawable/rescanbutton"/>
+		</LinearLayout>
+		<LinearLayout android:layout_width="fill_parent"
+			android:layout_height="wrap_content" android:layout_weight="1"
+			android:gravity="left" android:paddingLeft="10px">
+			<Button android:layout_height="wrap_content"
+				android:layout_width="wrap_content" android:id="@+id/Pause"
+				android:background="@drawable/pausebutton" />
+		</LinearLayout>
+	</LinearLayout>
+</LinearLayout> 
\ No newline at end of file
diff --git a/samples/FmRadioReceiver/res/values/strings.xml b/samples/FmRadioReceiver/res/values/strings.xml
new file mode 100755
index 0000000..ec714c2
--- /dev/null
+++ b/samples/FmRadioReceiver/res/values/strings.xml
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+	<string name="app_name">FmRadioReceiver</string>
+	<string name="band_select">Select Band</string>
+	<string name="band_us">U.S</string>
+	<string name="band_eu">Europe</string>
+	<string name="band_ja">Japan</string>
+	<string name="band_ch">China</string>
+	<string name="station_select">Select Station</string>
+	<string name="fmradio">FM RADIO</string>
+	<string name="dashes">- - - -</string>
+</resources>
diff --git a/samples/FmRadioReceiver/src/com/example/android/fmradioreceiver/FmRadioReceiver.java b/samples/FmRadioReceiver/src/com/example/android/fmradioreceiver/FmRadioReceiver.java
new file mode 100755
index 0000000..269dd84
--- /dev/null
+++ b/samples/FmRadioReceiver/src/com/example/android/fmradioreceiver/FmRadioReceiver.java
@@ -0,0 +1,518 @@
+/*
+ * Copyright (C) 2011 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.fmradioreceiver;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.media.MediaPlayer;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.ArrayAdapter;
+import android.widget.Button;
+import android.widget.TextView;
+import android.widget.Toast;
+import com.stericsson.hardware.fm.FmBand;
+import com.stericsson.hardware.fm.FmReceiver;
+
+import java.io.IOException;
+
+
+public class FmRadioReceiver extends Activity {
+
+    // The string to find in android logs
+    private static final String LOG_TAG = "FM Radio Demo App";
+
+    // The string to show that the station list is empty
+    private static final String EMPTY_STATION_LIST = "No stations available";
+
+    // The 50kHz channel offset
+    private static final int CHANNEL_OFFSET_50KHZ = 50;
+
+    // The base menu identifier
+    private static final int BASE_OPTION_MENU = 0;
+
+    // The band menu identifier
+    private static final int BAND_SELECTION_MENU = 1;
+
+    // The station menu identifier
+    private static final int STATION_SELECTION_MENU = 2;
+
+    // Handle to the Media Player that plays the audio from the selected station
+    private MediaPlayer mMediaPlayer;
+
+    // The scan listener that receives the return values from the scans
+    private FmReceiver.OnScanListener mReceiverScanListener;
+
+    // The listener that receives the RDS data from the current channel
+    private FmReceiver.OnRDSDataFoundListener mReceiverRdsDataFoundListener;
+
+    // The started listener is activated when the radio has started
+    private FmReceiver.OnStartedListener mReceiverStartedListener;
+
+    // Displays the currently tuned frequency
+    private TextView mFrequencyTextView;
+
+    // Displays the current station name if there is adequate RDS data
+    private TextView mStationNameTextView;
+
+    // Handle to the FM radio Band object
+    private FmBand mFmBand;
+
+    // Handle to the FM radio receiver object
+    private FmReceiver mFmReceiver;
+
+    // Indicates if we are in the initialization sequence
+    private boolean mInit = true;
+
+    // Indicates that we are restarting the app
+    private boolean mRestart = false;
+
+    // Protects the MediaPlayer and FmReceiver against rapid muting causing
+    // errors
+    private boolean mPauseMutex = false;
+
+    // Array of the available stations in MHz
+    private ArrayAdapter<CharSequence> mMenuAdapter;
+
+    // The name of the storage string
+    public static final String PREFS_NAME = "FMRadioPrefsFile";
+
+    // The menu items
+    public static final int FM_BAND = Menu.FIRST;
+
+    public static final int BAND_US = Menu.FIRST + 1;
+
+    public static final int BAND_EU = Menu.FIRST + 2;
+
+    public static final int BAND_JAPAN = Menu.FIRST + 3;
+
+    public static final int BAND_CHINA = Menu.FIRST + 4;
+
+    public static final int STATION_SELECT = Menu.FIRST + 5;
+
+    public static final int STATION_SELECT_MENU_ITEMS = STATION_SELECT + 1;
+
+    // The currently selected FM Radio band
+    private int mSelectedBand;
+
+    /**
+     * Required method from parent class
+     *
+     * @param icicle - The previous instance of this app
+     */
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.main);
+        mFmReceiver = (FmReceiver) getSystemService("fm_receiver");
+        SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
+        mSelectedBand = settings.getInt("selectedBand", 1);
+        mFmBand = new FmBand(mSelectedBand);
+        setupButtons();
+    }
+
+    /**
+     * Starts up the listeners and the FM radio if it isn't already active
+     */
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mReceiverScanListener = new com.stericsson.hardware.fm.FmReceiver.OnScanListener() {
+
+            // FullScan results
+            public void onFullScan(int[] frequency, int[] signalStrength, boolean aborted) {
+                ((Button) findViewById(R.id.FullScan)).setEnabled(true);
+                showToast("Fullscan complete", Toast.LENGTH_LONG);
+                mMenuAdapter.clear();
+                if (frequency.length == 0) {
+                    mMenuAdapter.add(EMPTY_STATION_LIST);
+                    return;
+                }
+                for (int i = 0; i < frequency.length; i++) {
+                    String a = Double.toString((double) frequency[i] / 1000);
+                    if (mFmBand.getChannelOffset() == CHANNEL_OFFSET_50KHZ) {
+                        a = String.format(a, "%.2f");
+                    } else {
+                        a = String.format(a, "%.1f");
+                    }
+                    mMenuAdapter.add(a);
+                }
+                if (mInit) {
+                    mInit = false;
+                    try {
+                        mFmReceiver.setFrequency(frequency[0]);
+                        mFrequencyTextView.setText(mMenuAdapter.getItem(0).toString());
+                    } catch (IOException e) {
+                        showToast("Unable to set the receiver's frequency", Toast.LENGTH_LONG);
+                    } catch (IllegalArgumentException e) {
+                        showToast("Unable to set the receiver's frequency", Toast.LENGTH_LONG);
+                    }
+                }
+            }
+
+            // Returns the new frequency.
+            public void onScan(int tunedFrequency, int signalStrength, int scanDirection, boolean aborted) {
+                String a = Double.toString((double) tunedFrequency / 1000);
+                if (mFmBand.getChannelOffset() == CHANNEL_OFFSET_50KHZ) {
+                    mFrequencyTextView.setText(String.format(a, "%.2f"));
+                } else {
+                    mFrequencyTextView.setText(String.format(a, "%.1f"));
+                }
+                ((Button) findViewById(R.id.ScanUp)).setEnabled(true);
+                ((Button) findViewById(R.id.ScanDown)).setEnabled(true);
+            }
+        };
+        mReceiverRdsDataFoundListener = new com.stericsson.hardware.fm.FmReceiver.OnRDSDataFoundListener() {
+
+            // Receives the current frequency's RDS Data
+            public void onRDSDataFound(Bundle rdsData, int frequency) {
+                if (rdsData.containsKey("PSN")) {
+                    mStationNameTextView.setText(rdsData.getString("PSN"));
+                }
+            }
+        };
+
+        mReceiverStartedListener = new com.stericsson.hardware.fm.FmReceiver.OnStartedListener() {
+
+            public void onStarted() {
+                // Activate all the buttons
+                ((Button) findViewById(R.id.ScanUp)).setEnabled(true);
+                ((Button) findViewById(R.id.ScanDown)).setEnabled(true);
+                ((Button) findViewById(R.id.Pause)).setEnabled(true);
+                ((Button) findViewById(R.id.FullScan)).setEnabled(true);
+                initialBandscan();
+                startAudio();
+            }
+        };
+
+        mFmReceiver.addOnScanListener(mReceiverScanListener);
+        mFmReceiver.addOnRDSDataFoundListener(mReceiverRdsDataFoundListener);
+        mFmReceiver.addOnStartedListener(mReceiverStartedListener);
+
+        if (!mRestart) {
+            turnRadioOn();
+        }
+        mRestart = false;
+    }
+
+    /**
+     * Stops the FM Radio listeners
+     */
+    @Override
+    protected void onRestart() {
+        super.onRestart();
+        mRestart = true;
+    }
+
+    /**
+     * Stops the FM Radio listeners
+     */
+    @Override
+    protected void onStop() {
+        super.onStop();
+
+        if (mFmReceiver != null) {
+            mFmReceiver.removeOnScanListener(mReceiverScanListener);
+            mFmReceiver.removeOnRDSDataFoundListener(mReceiverRdsDataFoundListener);
+            mFmReceiver.removeOnStartedListener(mReceiverStartedListener);
+        }
+    }
+
+    /**
+     * Saves the FmBand for next time the program is used and closes the radio
+     * and media player.
+     */
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.putInt("selectedBand", mSelectedBand);
+        editor.commit();
+        try {
+            mFmReceiver.reset();
+        } catch (IOException e) {
+            Log.e(LOG_TAG, "Unable to reset correctly", e);
+        }
+        if (mMediaPlayer != null) {
+            mMediaPlayer.release();
+            mMediaPlayer = null;
+        }
+    }
+
+    /**
+     * Starts the initial bandscan in it's own thread
+     */
+    private void initialBandscan() {
+        Thread bandscanThread = new Thread() {
+            public void run() {
+                try {
+                    mFmReceiver.startFullScan();
+                } catch (IllegalStateException e) {
+                    showToast("Unable to start the scan", Toast.LENGTH_LONG);
+                    return;
+                }
+            }
+        };
+        bandscanThread.start();
+    }
+
+    /**
+     * Helper method to display toast
+     */
+    private void showToast(final String text, final int duration) {
+        runOnUiThread(new Runnable() {
+            public void run() {
+                Toast.makeText(getApplicationContext(), text, duration).show();
+            }
+        });
+    }
+
+    /**
+     * Starts the FM receiver and makes the buttons appear inactive
+     */
+    private void turnRadioOn() {
+
+        try {
+            mFmReceiver.startAsync(mFmBand);
+            // Darken the the buttons
+            ((Button) findViewById(R.id.ScanUp)).setEnabled(false);
+            ((Button) findViewById(R.id.ScanDown)).setEnabled(false);
+            ((Button) findViewById(R.id.Pause)).setEnabled(false);
+            ((Button) findViewById(R.id.FullScan)).setEnabled(false);
+            showToast("Scanning initial stations", Toast.LENGTH_LONG);
+        } catch (IOException e) {
+            showToast("Unable to start the radio receiver.", Toast.LENGTH_LONG);
+        } catch (IllegalStateException e) {
+            showToast("Unable to start the radio receiver.", Toast.LENGTH_LONG);
+        }
+    }
+
+    /**
+     * Starts the FM receiver and makes the buttons appear inactive
+     */
+    private void startAudio() {
+
+        mMediaPlayer = new MediaPlayer();
+        try {
+            mMediaPlayer.setDataSource("fmradio://rx");
+            mMediaPlayer.prepare();
+            mMediaPlayer.start();
+        } catch (IOException e) {
+            showToast("Unable to start the media player", Toast.LENGTH_LONG);
+        }
+    }
+
+    /**
+     * Sets up the buttons and their listeners
+     */
+    private void setupButtons() {
+
+        mMenuAdapter = new ArrayAdapter<CharSequence>(this, android.R.layout.simple_spinner_item);
+        mMenuAdapter.setDropDownViewResource(android.R.layout.simple_list_item_single_choice);
+        mMenuAdapter.add("No stations available");
+        mFrequencyTextView = (TextView) findViewById(R.id.FrequencyTextView);
+        mStationNameTextView = (TextView) findViewById(R.id.PSNTextView);
+
+        final Button scanUp = (Button) findViewById(R.id.ScanUp);
+        scanUp.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                try {
+                    mFmReceiver.scanUp();
+                } catch (IllegalStateException e) {
+                    showToast("Unable to ScanUp", Toast.LENGTH_LONG);
+                    return;
+                }
+                scanUp.setEnabled(false);
+            }
+        });
+        final Button scanDown = (Button) findViewById(R.id.ScanDown);
+        scanDown.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                try {
+                    mFmReceiver.scanDown();
+                } catch (IllegalStateException e) {
+                    showToast("Unable to ScanDown", Toast.LENGTH_LONG);
+                    return;
+                }
+                scanDown.setEnabled(false);
+            }
+        });
+        final Button pause = (Button) findViewById(R.id.Pause);
+        pause.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                if (mFmReceiver.getState() == FmReceiver.STATE_PAUSED && mPauseMutex != true) {
+                    try {
+                        mPauseMutex = true;
+                        mFmReceiver.resume();
+                        mMediaPlayer.start();
+                        pause.setBackgroundResource(R.drawable.pausebutton);
+                    } catch (IOException e) {
+                        showToast("Unable to resume", Toast.LENGTH_LONG);
+                    } catch (IllegalStateException e) {
+                        showToast("Unable to resume", Toast.LENGTH_LONG);
+                    }
+                    mPauseMutex = false;
+                } else if (mFmReceiver.getState() == FmReceiver.STATE_STARTED
+                        && mPauseMutex != true) {
+                    try {
+                        mPauseMutex = true;
+                        mMediaPlayer.pause();
+                        mFmReceiver.pause();
+                        pause.setBackgroundResource(R.drawable.playbutton);
+                    } catch (IOException e) {
+                        showToast("Unable to pause", Toast.LENGTH_LONG);
+                    } catch (IllegalStateException e) {
+                        showToast("Unable to pause", Toast.LENGTH_LONG);
+                    }
+                    mPauseMutex = false;
+                } else if (mPauseMutex) {
+                    showToast("MediaPlayer busy. Please wait and try again.", Toast.LENGTH_LONG);
+                } else {
+                    Log.i(LOG_TAG, "No action: incorrect state - " + mFmReceiver.getState());
+                }
+            }
+        });
+        final Button fullScan = (Button) findViewById(R.id.FullScan);
+        fullScan.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                try {
+                    fullScan.setEnabled(false);
+                    showToast("Scanning for stations", Toast.LENGTH_LONG);
+                    mFmReceiver.startFullScan();
+                } catch (IllegalStateException e) {
+                    showToast("Unable to start the scan", Toast.LENGTH_LONG);
+                }
+            }
+        });
+    }
+
+    /**
+     * Sets up the options menu when the menu button is pushed, dynamic
+     * population of the station select menu
+     */
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        menu.clear();
+        boolean result = super.onCreateOptionsMenu(menu);
+        SubMenu subMenu = menu.addSubMenu(BASE_OPTION_MENU, FM_BAND, Menu.NONE,
+                R.string.band_select);
+        subMenu.setIcon(android.R.drawable.ic_menu_mapmode);
+        // Populate the band selection menu
+        subMenu.add(BAND_SELECTION_MENU, BAND_US, Menu.NONE, R.string.band_us);
+        subMenu.add(BAND_SELECTION_MENU, BAND_EU, Menu.NONE, R.string.band_eu);
+        subMenu.add(BAND_SELECTION_MENU, BAND_JAPAN, Menu.NONE, R.string.band_ja);
+        subMenu.add(BAND_SELECTION_MENU, BAND_CHINA, Menu.NONE, R.string.band_ch);
+        subMenu.setGroupCheckable(BAND_SELECTION_MENU, true, true);
+        subMenu.getItem(mSelectedBand).setChecked(true);
+
+        subMenu = menu.addSubMenu(BASE_OPTION_MENU, STATION_SELECT, Menu.NONE,
+                R.string.station_select);
+        subMenu.setIcon(android.R.drawable.ic_menu_agenda);
+
+        // Dynamically populate the station select menu each time the option
+        // button is pushed
+        if (mMenuAdapter.isEmpty()) {
+            subMenu.setGroupEnabled(STATION_SELECTION_MENU, false);
+        } else {
+            subMenu.setGroupEnabled(STATION_SELECTION_MENU, true);
+            for (int i = 0; i < mMenuAdapter.getCount(); i++) {
+                subMenu.add(STATION_SELECTION_MENU, STATION_SELECT_MENU_ITEMS + i, Menu.NONE,
+                        mMenuAdapter.getItem(i));
+            }
+            subMenu.setGroupCheckable(STATION_SELECTION_MENU, true, true);
+        }
+        return result;
+    }
+
+    public int getSelectStationMenuItem(MenuItem item) {
+        return item.getItemId() - STATION_SELECT_MENU_ITEMS;
+    }
+
+    /**
+     * React to a selection in the option menu
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+
+        switch (item.getGroupId()) {
+            case BAND_SELECTION_MENU:
+                switch (item.getItemId()) {
+                    case BAND_US:
+                        mSelectedBand = FmBand.BAND_US;
+                        item.setChecked(true);
+                        break;
+                    case BAND_EU:
+                        mSelectedBand = FmBand.BAND_EU;
+                        item.setChecked(true);
+                        break;
+                    case BAND_JAPAN:
+                        mSelectedBand = FmBand.BAND_JAPAN;
+                        item.setChecked(true);
+                        break;
+                    case BAND_CHINA:
+                        mSelectedBand = FmBand.BAND_CHINA;
+                        item.setChecked(true);
+                        break;
+                    default:
+                        break;
+                }
+                mFmBand = new FmBand(mSelectedBand);
+                try {
+                    mFmReceiver.reset();
+                } catch (IOException e) {
+                    showToast("Unable to restart the FM Radio", Toast.LENGTH_LONG);
+                }
+                if (mMediaPlayer != null) {
+                    mMediaPlayer.release();
+                    mMediaPlayer = null;
+                }
+                turnRadioOn();
+                break;
+            case STATION_SELECTION_MENU:
+                try {
+                    if (!mMenuAdapter.getItem(getSelectStationMenuItem(item)).toString().matches(
+                            EMPTY_STATION_LIST)) {
+                        mFmReceiver.setFrequency((int) (Double.valueOf(mMenuAdapter.getItem(
+                                getSelectStationMenuItem(item)).toString()) * 1000));
+                        mFrequencyTextView.setText(mMenuAdapter.getItem(
+                                getSelectStationMenuItem(item)).toString());
+                    }
+                } catch (IOException e) {
+                    showToast("Unable to set the frequency", Toast.LENGTH_LONG);
+                } catch (IllegalStateException e) {
+                    showToast("Unable to set the frequency", Toast.LENGTH_LONG);
+                } catch (IllegalArgumentException e) {
+                    showToast("Unable to set the frequency", Toast.LENGTH_LONG);
+                }
+
+                break;
+            default:
+                break;
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}
diff --git a/samples/FmRadioTransmitter/Android.mk b/samples/FmRadioTransmitter/Android.mk
new file mode 100755
index 0000000..0c25b35
--- /dev/null
+++ b/samples/FmRadioTransmitter/Android.mk
@@ -0,0 +1,12 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+LOCAL_SRC_FILES := $(call all-subdir-java-files)
+
+LOCAL_PACKAGE_NAME := FmRadioTransmitter
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
diff --git a/samples/FmRadioTransmitter/AndroidManifest.xml b/samples/FmRadioTransmitter/AndroidManifest.xml
new file mode 100755
index 0000000..ec0d336
--- /dev/null
+++ b/samples/FmRadioTransmitter/AndroidManifest.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+	package="com.example.android.fmradiotransmitter" android:versionCode="1"
+	android:versionName="1.0">
+	<application android:icon="@drawable/icon" android:label="@string/app_name">
+		<activity android:name=".FmRadioTransmitter" android:label="@string/app_name">
+			<intent-filter>
+				<action android:name="android.intent.action.MAIN" />
+				<category android:name="android.intent.category.LAUNCHER" />
+			</intent-filter>
+		</activity>
+
+	</application>
+	<uses-permission android:name="android.permission.FM_RADIO_TRANSMITTER"></uses-permission>
+	<uses-permission android:name="android.permission.FM_RADIO_RECEIVER"></uses-permission>
+</manifest> 
\ No newline at end of file
diff --git a/samples/FmRadioTransmitter/_index.html b/samples/FmRadioTransmitter/_index.html
new file mode 100755
index 0000000..0a286e2
--- /dev/null
+++ b/samples/FmRadioTransmitter/_index.html
@@ -0,0 +1,14 @@
+<p>FmRadioTransmitter is a sample application that demonstrates the use of the
+<a href="../../../reference/android/fm/FmTransmitter.html">android.fm.FmTransmitter</a>
+class to implement a FM Radio transmitter in an application. The application allows the
+user to scan the 88MHz-90MHz frequency region for the channel with the least signal
+interference. The application then transmits any audio using the FM Radio chip.</p>
+
+<p>The suggested use is to start a long media file in the MediaPlayer application
+then turn on the FmRadioTransmitter sample application.</p>
+
+<p><strong>See also:</strong><br/>
+<a href="../../../reference/android/fm/FmBand.html">FmBand</a></p>
+<a href="../../../reference/android/fm/FmReceiver.html">FmReceiver</a></p>
+
+<img alt="" src="../images/FmRadioTransmitter.png"  />
diff --git a/samples/FmRadioTransmitter/res/drawable/anim.xml b/samples/FmRadioTransmitter/res/drawable/anim.xml
new file mode 100755
index 0000000..4a06a4f
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/anim.xml
@@ -0,0 +1,7 @@
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
+    android:oneshot="false">
+    <item android:drawable="@drawable/transmit1" android:duration="200" />
+    <item android:drawable="@drawable/transmit2" android:duration="200" />
+    <item android:drawable="@drawable/transmit3" android:duration="200" />
+    <item android:drawable="@drawable/transmit4" android:duration="200" />
+</animation-list>
\ No newline at end of file
diff --git a/samples/FmRadioTransmitter/res/drawable/backward.png b/samples/FmRadioTransmitter/res/drawable/backward.png
new file mode 100755
index 0000000..c53408f
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/backward.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/backwardbutton.xml b/samples/FmRadioTransmitter/res/drawable/backwardbutton.xml
new file mode 100755
index 0000000..22ff201
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/backwardbutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/backwardpress" />
+	<item android:state_focused="true" android:drawable="@drawable/backwardpress" />
+	<item android:drawable="@drawable/backward" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioTransmitter/res/drawable/backwardpress.png b/samples/FmRadioTransmitter/res/drawable/backwardpress.png
new file mode 100755
index 0000000..3fcfada
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/backwardpress.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/forward.png b/samples/FmRadioTransmitter/res/drawable/forward.png
new file mode 100755
index 0000000..a3f6a75
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/forward.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/forwardbutton.xml b/samples/FmRadioTransmitter/res/drawable/forwardbutton.xml
new file mode 100755
index 0000000..bbefffc
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/forwardbutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/forwardpress" />
+	<item android:state_focused="true" android:drawable="@drawable/forwardpress" />
+	<item android:drawable="@drawable/forward" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioTransmitter/res/drawable/forwardpress.png b/samples/FmRadioTransmitter/res/drawable/forwardpress.png
new file mode 100755
index 0000000..417b99d
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/forwardpress.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/icon.png b/samples/FmRadioTransmitter/res/drawable/icon.png
new file mode 100755
index 0000000..a07c69f
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/icon.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/rescan.png b/samples/FmRadioTransmitter/res/drawable/rescan.png
new file mode 100755
index 0000000..2d2cc2a
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/rescan.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/rescanbutton.xml b/samples/FmRadioTransmitter/res/drawable/rescanbutton.xml
new file mode 100755
index 0000000..9585ff5
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/rescanbutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/rescanpress" />
+	<item android:state_focused="true" android:drawable="@drawable/rescanpress" />
+	<item android:drawable="@drawable/rescan" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioTransmitter/res/drawable/rescanpress.png b/samples/FmRadioTransmitter/res/drawable/rescanpress.png
new file mode 100755
index 0000000..a03ce7a
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/rescanpress.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/transmit1.png b/samples/FmRadioTransmitter/res/drawable/transmit1.png
new file mode 100755
index 0000000..ecf4803
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmit1.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/transmit2.png b/samples/FmRadioTransmitter/res/drawable/transmit2.png
new file mode 100755
index 0000000..19aaeda
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmit2.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/transmit3.png b/samples/FmRadioTransmitter/res/drawable/transmit3.png
new file mode 100755
index 0000000..5303824
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmit3.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/transmit4.png b/samples/FmRadioTransmitter/res/drawable/transmit4.png
new file mode 100755
index 0000000..39e4190
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmit4.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/transmitgo.png b/samples/FmRadioTransmitter/res/drawable/transmitgo.png
new file mode 100755
index 0000000..af8639c
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmitgo.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/transmitgobutton.xml b/samples/FmRadioTransmitter/res/drawable/transmitgobutton.xml
new file mode 100755
index 0000000..b486bbe
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmitgobutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/transmitgopress" />
+	<item android:state_focused="true" android:drawable="@drawable/transmitgopress" />
+	<item android:drawable="@drawable/transmitgo" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioTransmitter/res/drawable/transmitgopress.png b/samples/FmRadioTransmitter/res/drawable/transmitgopress.png
new file mode 100755
index 0000000..b9319c9
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmitgopress.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/transmitstop.png b/samples/FmRadioTransmitter/res/drawable/transmitstop.png
new file mode 100755
index 0000000..a1871ba
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmitstop.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/drawable/transmitstopbutton.xml b/samples/FmRadioTransmitter/res/drawable/transmitstopbutton.xml
new file mode 100755
index 0000000..79c2db9
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmitstopbutton.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+	<item android:state_pressed="true" android:drawable="@drawable/transmitstoppress" />
+	<item android:state_focused="true" android:drawable="@drawable/transmitstoppress" />
+	<item android:drawable="@drawable/transmitstop" />
+</selector>
\ No newline at end of file
diff --git a/samples/FmRadioTransmitter/res/drawable/transmitstoppress.png b/samples/FmRadioTransmitter/res/drawable/transmitstoppress.png
new file mode 100755
index 0000000..eee7306
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/drawable/transmitstoppress.png
Binary files differ
diff --git a/samples/FmRadioTransmitter/res/layout/main.xml b/samples/FmRadioTransmitter/res/layout/main.xml
new file mode 100755
index 0000000..1669bb3
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/layout/main.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout android:layout_width="fill_parent"
+	android:layout_height="fill_parent" android:orientation="vertical"
+	android:gravity="center" xmlns:android="http://schemas.android.com/apk/res/android"
+	android:id="@+id/MainWindow">
+	<ImageView android:layout_y="40px" android:layout_height="80px"
+		android:layout_x="120px" android:id="@+id/TransmitIcon"
+		android:layout_width="160px"></ImageView>
+	<TextView android:layout_width="wrap_content"
+		android:layout_height="wrap_content" android:layout_gravity="center_horizontal"
+		android:text="@string/dashes" android:padding="40px" android:id="@+id/FrequencyTextView"
+		android:lines="1" android:textSize="60px"></TextView>
+	<LinearLayout android:layout_width="fill_parent"
+		android:orientation="horizontal" android:layout_height="wrap_content"
+		android:layout_gravity="center_horizontal" android:id="@+id/ButtonRow"
+		android:paddingBottom="40px">
+		<LinearLayout android:layout_width="fill_parent"
+			android:layout_height="wrap_content" android:layout_weight="1"
+			android:gravity="center" android:id="@+id/Left">
+			<Button android:layout_width="wrap_content"
+				android:layout_height="wrap_content" android:padding="10px"
+				android:id="@+id/BlockScan" android:background="@drawable/rescanbutton" />
+		</LinearLayout>
+		<LinearLayout android:layout_width="fill_parent"
+			android:layout_height="wrap_content" android:layout_weight="1"
+			android:gravity="center" android:id="@+id/Centre">
+			<Button android:layout_width="wrap_content"
+				android:layout_height="wrap_content" android:padding="10px"
+				android:id="@+id/ScanDown" android:background="@drawable/backwardbutton" />
+		</LinearLayout>
+		<LinearLayout android:layout_width="fill_parent"
+			android:layout_height="wrap_content" android:layout_weight="1"
+			android:gravity="center" android:id="@+id/Right">
+			<Button android:layout_width="wrap_content"
+				android:layout_height="wrap_content" android:padding="10px"
+				android:id="@+id/ScanUp" android:background="@drawable/forwardbutton" />
+
+		</LinearLayout>
+	</LinearLayout>
+	<Button android:id="@+id/Transmit" android:layout_x="40px"
+		android:layout_y="300px" android:layout_height="60px"
+		android:layout_width="200px" android:background="@drawable/transmitgobutton" />
+</LinearLayout>
\ No newline at end of file
diff --git a/samples/FmRadioTransmitter/res/values/strings.xml b/samples/FmRadioTransmitter/res/values/strings.xml
new file mode 100755
index 0000000..32d8048
--- /dev/null
+++ b/samples/FmRadioTransmitter/res/values/strings.xml
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<resources>
+    <string name="app_name">FM Radio Transmitter</string>
+    <string name="band_select">Select Band</string>
+    <string name="band_us">U.S</string>
+    <string name="band_eu">Europe</string>
+    <string name="band_ja">Japan</string>
+    <string name="band_ch">China</string>
+    <string name="dashes">----</string>
+</resources>
diff --git a/samples/FmRadioTransmitter/src/com/example/android/fmradiotransmitter/FmRadioTransmitter.java b/samples/FmRadioTransmitter/src/com/example/android/fmradiotransmitter/FmRadioTransmitter.java
new file mode 100755
index 0000000..e06095e
--- /dev/null
+++ b/samples/FmRadioTransmitter/src/com/example/android/fmradiotransmitter/FmRadioTransmitter.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2011 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,
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.fmradiotransmitter;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.hardware.fm.FmBand;
+import android.hardware.fm.FmTransmitter;
+import android.graphics.drawable.AnimationDrawable;
+import android.os.Bundle;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class FmRadioTransmitter extends Activity {
+
+    // Scan range that works for all bands
+    private static final int MINSCANRANGE = 88000;
+
+    private static final int MAXSCANRANGE = 90000;
+
+    // The 50kHz channel offset
+    private static final int CHANNEL_OFFSET_50KHZ = 50;
+
+    // The scan listener that receives the return values from the scans
+    private FmTransmitter.OnScanListener mTransmitterScanListener;
+
+    // The started listener is activated when the radio has started
+    private FmTransmitter.OnStartedListener mTransmitterStartedListener;
+
+    // Displays the currently tuned frequency
+    private TextView mFrequencyTextView;
+
+    // Handle to the FM radio Band object
+    private FmBand mFmBand;
+
+    // Handle to the FM radio transmitter object
+    private FmTransmitter mFmTransmitter;
+
+    // The frequency that the radio is currently tuned to.
+    private int mCurrentfrequency;
+
+    // The current band's channel offset.
+    private int mFrequencyIncrement;
+
+    // Handle to the Transmitter's Animation.
+    private AnimationDrawable mTransmitAnimation;
+
+    // The name of the storage string
+    public static final String PREFS_NAME = "FMRadioPrefsFile";
+
+    // The currently selected FM Radio band
+    private int mSelectedBand;
+
+    // The base menu identifier
+    private static final int BASE_OPTION_MENU = 0;
+
+    // The band menu identifier
+    private static final int BAND_SELECTION_MENU = 1;
+
+    // The menu items
+    public static final int FM_BAND = Menu.FIRST;
+
+    public static final int BAND_US = Menu.FIRST + 1;
+
+    public static final int BAND_EU = Menu.FIRST + 2;
+
+    public static final int BAND_JAPAN = Menu.FIRST + 3;
+
+    public static final int BAND_CHINA = Menu.FIRST + 4;
+
+    /**
+     * Required method from parent class
+     *
+     * @param icicle - The previous instance of this app
+     */
+    @Override
+    public void onCreate(Bundle icicle) {
+        super.onCreate(icicle);
+        setContentView(R.layout.main);
+        mFmTransmitter = (FmTransmitter) getSystemService(Context.RADIO_FM_TRANSMITTER_SERVICE);
+        SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
+        mSelectedBand = settings.getInt("selectedBand", 1);
+        mFmBand = new FmBand(mSelectedBand);
+        mFrequencyIncrement = mFmBand.getChannelOffset();
+        setupButtons();
+        setupAnimation();
+    }
+
+    /**
+     * Starts up the listeners and the FM radio if it isn't already active
+     */
+    @Override
+    protected void onStart() {
+        super.onStart();
+        mTransmitterStartedListener = new android.hardware.fm.FmTransmitter.OnStartedListener() {
+
+            public void onStarted() {
+                mTransmitAnimation.start();
+                ((Button) findViewById(R.id.Transmit))
+                        .setBackgroundResource(R.drawable.transmitstopbutton);
+                mCurrentfrequency = mFmBand.getDefaultFrequency();
+                mFrequencyIncrement = mFmBand.getChannelOffset();
+                blockscan();
+            }
+        };
+
+        mTransmitterScanListener = new android.hardware.fm.FmTransmitter.OnScanListener() {
+
+            public void onBlockScan(int[] frequency, int[] signalStrength, boolean aborted) {
+                ((Button) findViewById(R.id.BlockScan)).setEnabled(true);
+                int minFreq = 0;
+                int minSigStr = Integer.MAX_VALUE;
+                for (int i = 0; i < frequency.length; i++) {
+                    if (signalStrength[i] < minSigStr) {
+                        minSigStr = signalStrength[i];
+                        minFreq = frequency[i];
+                    }
+                }
+                try {
+                    mFmTransmitter.setFrequency(minFreq);
+                } catch (Exception e) {
+                    showToast("Unable to set the frequency after scanning", Toast.LENGTH_LONG);
+                    return;
+                }
+                mCurrentfrequency = minFreq;
+                displayFreq();
+            }
+        };
+        mFmTransmitter.addOnScanListener(mTransmitterScanListener);
+        mFmTransmitter.addOnStartedListener(mTransmitterStartedListener);
+    }
+
+    /**
+     * Stops the FM Radio listeners
+     */
+    @Override
+    protected void onStop() {
+        super.onStop();
+        if (mFmTransmitter != null) {
+            mFmTransmitter.removeOnScanListener(mTransmitterScanListener);
+            mFmTransmitter.removeOnStartedListener(mTransmitterStartedListener);
+        }
+    }
+
+    /**
+     * Saves the FmBand for next time the program is used and closes the radio
+     * and media player.
+     */
+    @Override
+    protected void onDestroy() {
+        super.onDestroy();
+        SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
+        SharedPreferences.Editor editor = settings.edit();
+        editor.putInt("selectedBand", mSelectedBand);
+        editor.commit();
+        try {
+            mFmTransmitter.reset();
+        } catch (Exception e) {
+            showToast("Unable to reset the radio hardware.", Toast.LENGTH_LONG);
+        }
+    }
+
+    /**
+     * Starts a blockscan in a seperate thread.
+     */
+    protected void blockscan() {
+        showToast("Performining blockscan", Toast.LENGTH_LONG);
+        Thread blockscanThread = new Thread() {
+            public void run() {
+                try {
+                    mFmTransmitter.startBlockScan(MINSCANRANGE, MAXSCANRANGE);
+                } catch (Exception e) {
+                    showToast("Unable to start BlockScan between " + String.valueOf(MINSCANRANGE)
+                            + "-" + String.valueOf(MAXSCANRANGE), Toast.LENGTH_LONG);
+                    return;
+                }
+            }
+        };
+        blockscanThread.start();
+    }
+
+    /**
+     * Helper function that formats and displays the current frequency.
+     */
+    private void displayFreq() {
+        String a = Double.toString((double) mCurrentfrequency / 1000);
+        if (mFmBand.getChannelOffset() == CHANNEL_OFFSET_50KHZ) {
+            mFrequencyTextView.setText(String.format(a, "%.2f"));
+        } else {
+            mFrequencyTextView.setText(String.format(a, "%.1f"));
+        }
+    }
+
+    /**
+     * Sets up the button animation
+     */
+    private void setupAnimation() {
+        ImageView transmitImage = (ImageView) findViewById(R.id.TransmitIcon);
+        transmitImage.setBackgroundResource(R.drawable.anim);
+        mTransmitAnimation = (AnimationDrawable) transmitImage.getBackground();
+    }
+
+    /**
+     * Helper method to display toast
+     */
+    private void showToast(final String text, final int duration) {
+        runOnUiThread(new Runnable() {
+            public void run() {
+                Toast.makeText(getApplicationContext(), text, duration).show();
+            }
+        });
+    }
+
+    /**
+     * Sets up the button's onclick listeners
+     */
+    private void setupButtons() {
+        mFrequencyTextView = (TextView) findViewById(R.id.FrequencyTextView);
+        final Button tuneUp = (Button) findViewById(R.id.ScanUp);
+        tuneUp.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                try {
+                    ((Button) findViewById(R.id.ScanUp)).setEnabled(false);
+                    mFmTransmitter.setFrequency(mCurrentfrequency + mFrequencyIncrement);
+                } catch (Exception e) {
+                    showToast("Unable to scan up", Toast.LENGTH_LONG);
+                    return;
+                }
+                mCurrentfrequency += mFrequencyIncrement;
+                displayFreq();
+                ((Button) findViewById(R.id.ScanUp)).setEnabled(true);
+            }
+        });
+        final Button tuneDown = (Button) findViewById(R.id.ScanDown);
+        tuneDown.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                try {
+                    ((Button) findViewById(R.id.ScanDown)).setEnabled(false);
+                    mFmTransmitter.setFrequency(mCurrentfrequency - mFrequencyIncrement);
+                } catch (Exception e) {
+                    showToast("Unable to scan down", Toast.LENGTH_LONG);
+                    return;
+                }
+                mCurrentfrequency -= mFrequencyIncrement;
+                displayFreq();
+                ((Button) findViewById(R.id.ScanDown)).setEnabled(true);
+            }
+        });
+        final Button transmit = (Button) findViewById(R.id.Transmit);
+        transmit.setBackgroundResource(R.drawable.transmitgobutton);
+        transmit.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                switch (mFmTransmitter.getState()) {
+
+                    case FmTransmitter.STATE_IDLE:
+                        try {
+                            mFmTransmitter.startAsync(mFmBand);
+                            showToast("Preparing radio", Toast.LENGTH_LONG);
+                        } catch (Exception e) {
+                            showToast("Unable to start radio", Toast.LENGTH_LONG);
+                            return;
+                        }
+
+                        break;
+                    case FmTransmitter.STATE_PAUSED:
+                        try {
+                            mFmTransmitter.resume();
+                        } catch (Exception e) {
+                            showToast("Unable to resume radio", Toast.LENGTH_LONG);
+                            return;
+                        }
+                        mTransmitAnimation.start();
+                        transmit.setBackgroundResource(R.drawable.transmitstopbutton);
+
+                        break;
+                    case FmTransmitter.STATE_STARTED:
+                        try {
+                            mFmTransmitter.pause();
+                        } catch (Exception e) {
+                            showToast("Unable to pause radio", Toast.LENGTH_LONG);
+                            return;
+                        }
+                        mTransmitAnimation.stop();
+                        transmit.setBackgroundResource(R.drawable.transmitgobutton);
+                        break;
+                    default:
+                }
+            }
+        });
+        final Button blockscan = (Button) findViewById(R.id.BlockScan);
+        blockscan.setOnClickListener(new OnClickListener() {
+
+            public void onClick(View v) {
+                blockscan();
+            }
+        });
+    }
+
+    /**
+     * Sets up the options menu when the menu button is push, dynamic population
+     * of the station select menu
+     */
+    public boolean onPrepareOptionsMenu(Menu menu) {
+        menu.clear();
+        boolean result = super.onCreateOptionsMenu(menu);
+        SubMenu subMenu = menu.addSubMenu(BASE_OPTION_MENU, FM_BAND, Menu.NONE,
+                R.string.band_select);
+        subMenu.setIcon(android.R.drawable.ic_menu_mapmode);
+        // Populate the band selection menu
+        subMenu.add(BAND_SELECTION_MENU, BAND_US, Menu.NONE, R.string.band_us);
+        subMenu.add(BAND_SELECTION_MENU, BAND_EU, Menu.NONE, R.string.band_eu);
+        subMenu.add(BAND_SELECTION_MENU, BAND_CHINA, Menu.NONE, R.string.band_ch);
+        subMenu.add(BAND_SELECTION_MENU, BAND_JAPAN, Menu.NONE, R.string.band_ja);
+        subMenu.setGroupCheckable(BAND_SELECTION_MENU, true, true);
+        subMenu.getItem(mSelectedBand).setChecked(true);
+        return result;
+    }
+
+    /**
+     * React to a selection in the option menu
+     */
+    @Override
+    public boolean onOptionsItemSelected(MenuItem item) {
+
+        if (item.getGroupId() == BAND_SELECTION_MENU) {
+            switch (item.getItemId()) {
+                case BAND_US:
+                    mSelectedBand = FmBand.BAND_US;
+                    item.setChecked(true);
+                    break;
+                case BAND_EU:
+                    mSelectedBand = FmBand.BAND_EU;
+                    item.setChecked(true);
+                    break;
+                case BAND_JAPAN:
+                    mSelectedBand = FmBand.BAND_JAPAN;
+                    item.setChecked(true);
+                    break;
+                case BAND_CHINA:
+                    mSelectedBand = FmBand.BAND_CHINA;
+                    item.setChecked(true);
+                    break;
+                default:
+                    break;
+            }
+            mFmBand = new FmBand(mSelectedBand);
+            try {
+                mFmTransmitter.reset();
+                ((Button) findViewById(R.id.Transmit))
+                        .setBackgroundResource(R.drawable.transmitgobutton);
+                ((TextView) findViewById(R.id.FrequencyTextView)).setText("----");
+                mFrequencyIncrement = mFmBand.getChannelOffset() / 1000;
+                mTransmitAnimation.stop();
+            } catch (Exception e) {
+                showToast("Unable to restart the FM Radio", Toast.LENGTH_LONG);
+            }
+        }
+        return super.onOptionsItemSelected(item);
+    }
+}