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
Conflicts:
build/sdk.atree
diff --git a/build/sdk.atree b/build/sdk.atree
index 8e45665..79ed597 100644
--- a/build/sdk.atree
+++ b/build/sdk.atree
@@ -204,6 +204,8 @@
development/samples/WiktionarySimple samples/${PLATFORM_NAME}/WiktionarySimple
development/samples/XmlAdapters samples/${PLATFORM_NAME}/XmlAdapters
development/samples/RenderScript/HelloCompute samples/${PLATFORM_NAME}/RenderScript/HelloCompute
+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);
+ }
+}