+ This sample is a revised version of the AlarmService functionality included in the
+ ApiDemos sample application. It is used as the application under test
+ for the Alarm Service Test
+ sample test application.
+
+
+ This application demonstrates a simple Android service that is started when needed by
+ Context.startService(Intent) and stops itself when its work is done. You can
+ use this type of service to move long-running or periodic tasks into the background. For
+ example, you could use this type of service to perform data synchronization.
+
+
+ In the sample, the service simply runs for 15 seconds and then stops itself. The wait is
+ implemented in a separate thread that uses a thread-safe object. This illustrates how to
+ set up a service that runs multiple threads that depend on one or more objects that must be
+ made thread-safe.
+
+
+ The application also contains the AlarmActivity activity that is a client of the
+ service. You use the activity to control when the service starts and stops. By default, the
+ activity fires off the service every thirty seconds. In effect, the service starts after
+ thirty seconds, runs for 15 seconds, stops, and then runs again automatically in another
+ 15 seconds. You also use the client to stop this cycle.
+
+
+ The test application AlarmServiceTest
+ shows you how to set up a test of this service.
+
diff --git a/samples/Alarm/res/drawable/icon.png b/samples/Alarm/res/drawable/icon.png
new file mode 100644
index 000000000..5ae7701a8
Binary files /dev/null and b/samples/Alarm/res/drawable/icon.png differ
diff --git a/samples/Alarm/res/drawable/stat_sample.png b/samples/Alarm/res/drawable/stat_sample.png
new file mode 100755
index 000000000..6c9ba0a97
Binary files /dev/null and b/samples/Alarm/res/drawable/stat_sample.png differ
diff --git a/samples/Alarm/res/layout/main.xml b/samples/Alarm/res/layout/main.xml
new file mode 100644
index 000000000..3d79e2ba5
--- /dev/null
+++ b/samples/Alarm/res/layout/main.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Alarm/res/values/strings.xml b/samples/Alarm/res/values/strings.xml
new file mode 100644
index 000000000..18a34cc39
--- /dev/null
+++ b/samples/Alarm/res/values/strings.xml
@@ -0,0 +1,29 @@
+
+
+
+ Alarm
+
+ This shows how to schedule a repeating alarm that starts a service.
+ Start Alarm Service
+ Stop Alarm Service
+
+ Repeating timer started. Starts AlarmService every 30 seconds.
+ The sample service is running.
+ Sample Alarm Service
+ The sample service is no longer running.
+
+ Repeating timer stopped. AlarmService will no longer be started.
+
diff --git a/samples/Alarm/src/com/example/android/newalarm/AlarmActivity.java b/samples/Alarm/src/com/example/android/newalarm/AlarmActivity.java
new file mode 100644
index 000000000..d26507d05
--- /dev/null
+++ b/samples/Alarm/src/com/example/android/newalarm/AlarmActivity.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.newalarm;
+
+import android.app.Activity;
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.os.SystemClock;
+import android.os.Bundle;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.Toast;
+
+/**
+ * This is the activity that controls AlarmService.
+ *
+ * When the user clicks the "Start Alarm Service" button, it triggers a repeating countdown
+ * timer. Every thirty seconds, the timer starts AlarmService, which then runs for 15 seconds
+ * and shuts itself down.
+ *
+ *
+ * When the user clicks the "Stop Alarm Service" button, it stops the countdown timer.
+ *
+ */
+
+public class AlarmActivity extends Activity {
+ // 30 seconds in milliseconds
+ private static final long THIRTY_SECONDS_MILLIS = 30 * 1000;
+
+ // An intent for AlarmService, to trigger it as if the Activity called startService().
+ private PendingIntent mAlarmSender;
+
+ // Contains a handle to the system alarm service
+ private AlarmManager mAlarmManager;
+
+ /**
+ * This method is called when Android starts the activity. It initializes the UI.
+ *
+ * This method is automatically called when Android starts the Activity
+ *
+ */
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ // Create a PendingIntent to trigger a startService() for AlarmService
+ mAlarmSender = PendingIntent.getService( // set up an intent for a call to a service
+ AlarmActivity.this, // the current context
+ 0, // request code (not used)
+ new Intent(AlarmActivity.this, AlarmService.class), // A new Service intent
+ 0 // flags (none are required for a service)
+ );
+
+ // Creates the main view
+ setContentView(R.layout.main);
+
+ // Finds the button that starts the repeating countdown timer
+ Button button = (Button)findViewById(R.id.start_alarm);
+
+ // Sets the listener for the start button
+ button.setOnClickListener(mStartAlarmListener);
+
+ // Finds the button that stops countdown timer
+ button = (Button)findViewById(R.id.stop_alarm);
+
+ // Sets the listener for the stop button
+ button.setOnClickListener(mStopAlarmListener);
+
+ // Gets the handle to the system alarm service
+ mAlarmManager = (AlarmManager)getSystemService(ALARM_SERVICE);
+ }
+
+ // Creates a new anonymous click listener for the start button. It starts the repeating
+ // countdown timer.
+ private OnClickListener mStartAlarmListener = new OnClickListener() {
+ // Sets the callback for when the button is clicked
+ public void onClick(View v) {
+
+ // Sets the time when the alarm will first go off
+ // The Android AlarmManager uses this form of the current time.
+ long firstAlarmTime = SystemClock.elapsedRealtime();
+
+ // Sets a repeating countdown timer that triggers AlarmService
+ mAlarmManager.setRepeating(
+ AlarmManager.ELAPSED_REALTIME_WAKEUP, // based on time since last wake up
+ firstAlarmTime, // sends the first alarm immediately
+ THIRTY_SECONDS_MILLIS, // repeats every thirty seconds
+ mAlarmSender // when the alarm goes off, sends this Intent
+ );
+
+ // Notifies the user that the repeating countdown timer has been started
+ Toast.makeText(
+ AlarmActivity.this, // the current context
+ R.string.repeating_started, // the message to display
+ Toast.LENGTH_LONG // how long to display the message
+ ).show(); // show the message on the screen
+ }
+ };
+
+ // Creates a new anonymous click listener for the stop button. It shuts off the repeating
+ // countdown timer.
+ private OnClickListener mStopAlarmListener = new OnClickListener() {
+ // Sets the callback for when the button is clicked
+ public void onClick(View v) {
+
+ // Cancels the repeating countdown timer
+ mAlarmManager.cancel(mAlarmSender);
+
+ // Notifies the user that the repeating countdown timer has been stopped
+ Toast.makeText(
+ AlarmActivity.this, // the current context
+ R.string.repeating_stopped, // the message to display
+ Toast.LENGTH_LONG // how long to display the message
+ ).show(); // display the message
+ }
+ };
+}
diff --git a/samples/Alarm/src/com/example/android/newalarm/AlarmService.java b/samples/Alarm/src/com/example/android/newalarm/AlarmService.java
new file mode 100644
index 000000000..1f882069d
--- /dev/null
+++ b/samples/Alarm/src/com/example/android/newalarm/AlarmService.java
@@ -0,0 +1,216 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.newalarm;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.Intent;
+import android.os.Binder;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.RemoteException;
+import android.widget.Toast;
+
+/**
+ *
+ * This class implements a service. The service is started by AlarmActivity, which contains a
+ * repeating countdown timer that sends a PendingIntent. The user starts and stops the timer with
+ * buttons in the UI.
+ *
+ *
+ * When this service is started, it creates a Runnable and starts it in a new Thread. The
+ * Runnable does a synchronized lock on the service's Binder object for 15 seconds, then issues
+ * a stopSelf(). The net effect is a new worker thread that takes 15 seconds to run and then
+ * shuts down the entire service. The activity restarts the service after 15 more seconds, when the
+ * countdown timer triggers again.
+ *
+ *
+ * This service is provided as the service under test for the sample test application
+ * AlarmServiceTest.
+ *
+ *
+ * Note: Since this sample is based on the Android 1.5 platform, it does not implement
+ * onStartCommand. See the Javadoc for android.app.Service for more details.
+ *
+ */
+public class AlarmService extends Service {
+ // Defines a label for the thread that this service starts
+ private static final String ALARM_SERVICE_THREAD = "AlarmService";
+
+ // Defines 15 seconds
+ public static final long WAIT_TIME_SECONDS = 15;
+
+ // Define the number of milliseconds in one second
+ public static final long MILLISECS_PER_SEC = 1000;
+
+ /*
+ * For testing purposes, the following variables are defined as fields and set to
+ * package visibility.
+ */
+
+ // The NotificationManager used to send notifications to the status bar.
+ NotificationManager mNotificationManager;
+
+ // An Intent that displays the client if the user clicks the notification.
+ PendingIntent mContentIntent;
+
+ // A Notification to send to the Notification Manager when the service is started.
+ Notification mNotification;
+
+ // A Binder, used as the lock object for the worker thread.
+ IBinder mBinder = new AlarmBinder();
+
+ // A Thread object that will run the background task
+ Thread mWorkThread;
+
+ // The Runnable that is the service's "task". This illustrates how a service is used to
+ // offload work from a client.
+ Runnable mWorkTask = new Runnable() {
+ public void run() {
+ // Sets the wait time to 15 seconds, simulating a 15-second background task.
+ long waitTime = System.currentTimeMillis() + WAIT_TIME_SECONDS * MILLISECS_PER_SEC;
+
+ // Puts the wait in a while loop to ensure that it actually waited 15 seconds.
+ // This covers the situation where an interrupt might have overridden the wait.
+ while (System.currentTimeMillis() < waitTime) {
+ // Waits for 15 seconds or interruption
+ synchronized (mBinder) {
+ try {
+ // Waits for 15 seconds or until an interrupt triggers an exception.
+ // If an interrupt occurs, the wait is recalculated to ensure a net
+ // wait of 15 seconds.
+ mBinder.wait(waitTime - System.currentTimeMillis());
+ } catch (InterruptedException e) {
+ }
+ }
+ }
+ // Stops the current service. In response, Android calls onDestroy().
+ stopSelf();
+ }
+ };
+
+ /**
+ * Makes a full concrete subclass of Binder, rather than doing it in line, for readability.
+ */
+ public class AlarmBinder extends Binder {
+ // Constructor. Calls the super constructor to set up the instance.
+ public AlarmBinder() {
+ super();
+ }
+
+ @Override
+ protected boolean onTransact(int code, Parcel data, Parcel reply, int flags)
+ throws RemoteException {
+
+ // Call the parent method with the arguments passed in
+ return super.onTransact(code, data, reply, flags);
+ }
+ }
+
+ /**
+ * Initializes the service when it is first started by a call to startService() or
+ * bindService().
+ */
+ @Override
+ public void onCreate() {
+ // Gets a handle to the system mNotification service.
+ mNotificationManager = (NotificationManager)getSystemService(NOTIFICATION_SERVICE);
+
+ // Updates the status bar to indicate that this service is running.
+ showNotification();
+
+ // Creates a new thread. A new thread is used so that the service's work doesn't block
+ // anything on the calling client's thread. By default, a service runs in the same
+ // process and thread as the client that starts it.
+ mWorkThread = new Thread(
+ null, // threadgroup (in this case, null)
+ mWorkTask, // the Runnable that will run in this thread
+ ALARM_SERVICE_THREAD
+ );
+ // Starts the thread
+ mWorkThread.start();
+ }
+
+ /**
+ * Stops the service in response to the stopSelf() issued when the wait is over. Other
+ * clients that use this service could stop it by issuing a stopService() or a stopSelf() on
+ * the service object.
+ */
+ @Override
+ public void onDestroy() {
+ // Cancels the status bar mNotification based on its ID, which is set in showNotification().
+ mNotificationManager.cancel(R.string.alarm_service_started);
+
+ // Sends a notification to the screen.
+ Toast.makeText(
+ this, // the current context
+ R.string.alarm_service_finished, // the message to show
+ Toast.LENGTH_LONG // how long to keep the message on the screen
+ ).show(); // show the text
+ }
+
+ // Returns the service's binder object to clients that issue onBind().
+ @Override
+ public IBinder onBind(Intent intent) {
+ return mBinder;
+ }
+
+ /**
+ * Displays a notification in the status bar that this service is running. This method
+ * also creates an Intent for the AlarmActivity client and attaches it to the notification
+ * line. If the user clicks the line in the expanded status window, the Intent triggers
+ * AlarmActivity.
+ */
+ private void showNotification() {
+ // Sets the text to use for the status bar and status list views.
+ CharSequence notificationText = getText(R.string.alarm_service_started);
+
+ // Sets the icon, status bar text, and display time for the mNotification.
+ mNotification = new Notification(
+ R.drawable.stat_sample, // the status icon
+ notificationText, // the status text
+ System.currentTimeMillis() // the time stamp
+ );
+
+ // Sets up the Intent that starts AlarmActivity
+ mContentIntent = PendingIntent.getActivity(
+ this, // Start the Activity in the current context
+ 0, // not used
+ new Intent(this, AlarmActivity.class), // A new Intent for AlarmActivity
+ 0 // Use an existing activity instance if available
+ );
+
+ // Creates a new content view for the mNotification. The view appears when the user
+ // shows the expanded status window.
+ mNotification.setLatestEventInfo(
+ this, // Put the content view in the current context
+ getText(R.string.alarm_service_label), // The text to use as the label of the entry
+ notificationText, // The text to use as the contents of the entry
+ mContentIntent // The intent to send when the entry is clicked
+ );
+
+ // Sets a unique ID for the notification and sends it to NotificationManager to be
+ // displayed. The ID is the integer marker for the notification string, which is
+ // guaranteed to be unique within the entire application.
+ mNotificationManager.notify(
+ R.string.alarm_service_started, // unique id for the mNotification
+ mNotification // the mNotification object
+ );
+ }
+}
diff --git a/samples/Alarm/tests/AndroidManifest.xml b/samples/Alarm/tests/AndroidManifest.xml
new file mode 100644
index 000000000..868892000
--- /dev/null
+++ b/samples/Alarm/tests/AndroidManifest.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/Alarm/tests/_index.html b/samples/Alarm/tests/_index.html
new file mode 100644
index 000000000..614125dd3
--- /dev/null
+++ b/samples/Alarm/tests/_index.html
@@ -0,0 +1,50 @@
+
+ This sample is the test application for the Alarm
+ sample application. It tests the application's AlarmService service.
+
+ The application shows how to set up a test application project,
+ how to create the AndroidManifest.xml
+ file for a test application, and how to set up a test case class for a service. The
+ test case class,
+ AlarmServiceTest, contains tests that demonstrate the following
+ Android test patterns:
+
+
+
+ Test setup: The setUp() method re-initializes the state of the
+ service under test before each test is run.
+
+
+ Service start: The Service.testServiceCreate() test confirms that the
+ service starts correctly and initializes the variables it needs to provide its
+ services.
+
+
+
+ The manifest declares an <instrumentation>
+ element that links the test application with the application under test. Specifically, the
+ element's android:name attribute specifies InstrumentationTestRunner
+ as the instrumentation to use. The android:targetPackage attribute specifies
+ com.android.example.newalarm as the name of the Android package that contains the
+ service under test.
+
+
+ Note:AlarmServiceTest.java uses the Java package name
+ com.example.android.newalarm, which is the same package used by service under
+ test, AlarmService.java. This allows the test class to access members in the
+ service under test that are defined with package visibility. To prevent conflicts, though,
+ the generated java file R.java for AlarmServiceTest uses the
+ Java package name com.example.android.newalarm.test. For the same reason, the
+ Android package name for the test application (specified in the manifest file), is
+ com.example.android.newalarm.test.
+
diff --git a/samples/Alarm/tests/src/com/example/android/newalarm/AlarmServiceTest.java b/samples/Alarm/tests/src/com/example/android/newalarm/AlarmServiceTest.java
new file mode 100644
index 000000000..4d6c7aded
--- /dev/null
+++ b/samples/Alarm/tests/src/com/example/android/newalarm/AlarmServiceTest.java
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.newalarm;
+
+import android.content.Intent;
+import android.test.ServiceTestCase;
+import com.example.android.newalarm.AlarmService;
+
+/**
+ * Test class for the Alarm sample test package. This test class tests the AlarmService
+ * service component.
+ */
+public class AlarmServiceTest extends ServiceTestCase {
+ // Contains an Intent used to start the service
+ Intent mStartServiceIntent;
+
+ // Contains a handle to the system alarm service
+ AlarmService mService;
+
+ /**
+ * Constructor for the test class. Test classes that are run by InstrumentationTestRunner
+ * must provide a constructor with no arguments that calls the base class constructor as its
+ * first statement.
+ */
+ public AlarmServiceTest() {
+ super(AlarmService.class);
+ }
+
+ /*
+ * Sets up the test fixture. This method is called before each test
+ */
+ @Override
+ protected void setUp() throws Exception {
+
+ super.setUp();
+
+ // Sets up an intent to start the service under test
+ mStartServiceIntent = new Intent(this.getSystemContext(),AlarmService.class);
+ }
+
+ /**
+ * Cleans up the test fixture
+ * Called after each test method. If you override the method, call super.tearDown() as the
+ * last statement in your override.
+ */
+ @Override
+ protected void tearDown() throws Exception {
+ // Always call the super constructor when overriding tearDown()
+ super.tearDown();
+ }
+
+ /**
+ * Tests the service's onCreate() method. Starts the service using startService(Intent)
+ */
+ public void testServiceCreate() {
+ // Starts the service under test
+ this.startService(mStartServiceIntent);
+
+ // Gets a handle to the service under test.
+ mService = this.getService();
+
+ // Asserts that the Notification Manager was created in the service under test.
+ assertNotNull(mService.mNotificationManager);
+
+ // Asserts that the PendingIntent for the expanded status window was created
+ assertNotNull(mService.mContentIntent);
+
+ // Asserts that the notification was created
+ assertNotNull(mService.mNotification);
+ }
+
+}
diff --git a/samples/ApiDemos/AndroidManifest.xml b/samples/ApiDemos/AndroidManifest.xml
index 816326591..6e39538fa 100644
--- a/samples/ApiDemos/AndroidManifest.xml
+++ b/samples/ApiDemos/AndroidManifest.xml
@@ -32,6 +32,9 @@
+
+
+
@@ -1884,6 +1887,13 @@
+
+
+
+
+
+
+
diff --git a/samples/ApiDemos/src/com/example/android/apis/media/AudioFxDemo.java b/samples/ApiDemos/src/com/example/android/apis/media/AudioFxDemo.java
new file mode 100644
index 000000000..7b4db1ff5
--- /dev/null
+++ b/samples/ApiDemos/src/com/example/android/apis/media/AudioFxDemo.java
@@ -0,0 +1,252 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.apis.media;
+
+import com.example.android.apis.R;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.AssetFileDescriptor;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.audiofx.Equalizer;
+import android.media.audiofx.Visualizer;
+import android.net.Uri;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.LinearLayout;
+import android.widget.SeekBar;
+import android.widget.TextView;
+
+import java.io.IOException;
+
+public class AudioFxDemo extends Activity {
+ private static final String TAG = "AudioFxDemo";
+
+ private static final float VISUALIZER_HEIGHT_DIP = 50f;
+
+ private MediaPlayer mMediaPlayer;
+ private Visualizer mVisualizer;
+ private Equalizer mEqualizer;
+
+ private LinearLayout mLinearLayout;
+ private VisualizerView mVisualizerView;
+ private TextView mStatusTextView;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ setVolumeControlStream(AudioManager.STREAM_MUSIC);
+
+ mStatusTextView = new TextView(this);
+
+ mLinearLayout = new LinearLayout(this);
+ mLinearLayout.setOrientation(LinearLayout.VERTICAL);
+ mLinearLayout.addView(mStatusTextView);
+
+ setContentView(mLinearLayout);
+
+ // Create the MediaPlayer
+ mMediaPlayer = MediaPlayer.create(this, R.raw.test_cbr);
+ Log.d(TAG, "MediaPlayer audio session ID: " + mMediaPlayer.getAudioSessionId());
+
+ setupVisualizerFxAndUI();
+ setupEqualizerFxAndUI();
+
+ // Make sure the visualizer is enabled only when you actually want to receive data, and
+ // when it makes sense to receive data.
+ mVisualizer.setEnabled(true);
+
+ // When the stream ends, we don't need to collect any more data. We don't do this in
+ // setupVisualizerFxAndUI because we likely want to have more, non-Visualizer related code
+ // in this callback.
+ mMediaPlayer.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
+ public void onCompletion(MediaPlayer mediaPlayer) {
+ mVisualizer.setEnabled(false);
+ }
+ });
+
+ mMediaPlayer.start();
+ mStatusTextView.setText("Playing audio...");
+ }
+
+ private void setupEqualizerFxAndUI() {
+ // Create the Equalizer object (an AudioEffect subclass) and attach it to our media player,
+ // with a default priority (0).
+ mEqualizer = new Equalizer(0, mMediaPlayer.getAudioSessionId());
+ mEqualizer.setEnabled(true);
+
+ TextView eqTextView = new TextView(this);
+ eqTextView.setText("Equalizer:");
+ mLinearLayout.addView(eqTextView);
+
+ short bands = mEqualizer.getNumberOfBands();
+
+ final short minEQLevel = mEqualizer.getBandLevelRange()[0];
+ final short maxEQLevel = mEqualizer.getBandLevelRange()[1];
+
+ for (short i = 0; i < bands; i++) {
+ final short band = i;
+
+ TextView freqTextView = new TextView(this);
+ freqTextView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ freqTextView.setGravity(Gravity.CENTER_HORIZONTAL);
+ freqTextView.setText((mEqualizer.getCenterFreq(band) / 1000) + " Hz");
+ mLinearLayout.addView(freqTextView);
+
+ LinearLayout row = new LinearLayout(this);
+ row.setOrientation(LinearLayout.HORIZONTAL);
+
+ TextView minDbTextView = new TextView(this);
+ minDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ minDbTextView.setText((minEQLevel / 100) + " dB");
+
+ TextView maxDbTextView = new TextView(this);
+ maxDbTextView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT));
+ maxDbTextView.setText((maxEQLevel / 100) + " dB");
+
+ LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ layoutParams.weight = 1;
+ SeekBar bar = new SeekBar(this);
+ bar.setLayoutParams(layoutParams);
+ bar.setMax(maxEQLevel - minEQLevel);
+ bar.setProgress(mEqualizer.getBandLevel(band));
+
+ bar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
+ public void onProgressChanged(SeekBar seekBar, int progress,
+ boolean fromUser) {
+ mEqualizer.setBandLevel(band, (short) (progress + minEQLevel));
+ }
+
+ public void onStartTrackingTouch(SeekBar seekBar) {}
+ public void onStopTrackingTouch(SeekBar seekBar) {}
+ });
+
+ row.addView(minDbTextView);
+ row.addView(bar);
+ row.addView(maxDbTextView);
+
+ mLinearLayout.addView(row);
+ }
+ }
+
+ private void setupVisualizerFxAndUI() {
+ // Create a VisualizerView (defined below), which will render the simplified audio
+ // wave form to a Canvas.
+ mVisualizerView = new VisualizerView(this);
+ mVisualizerView.setLayoutParams(new ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.FILL_PARENT,
+ (int)(VISUALIZER_HEIGHT_DIP * getResources().getDisplayMetrics().density)));
+ mLinearLayout.addView(mVisualizerView);
+
+ // Create the Visualizer object and attach it to our media player.
+ mVisualizer = new Visualizer(mMediaPlayer.getAudioSessionId());
+ mVisualizer.setCaptureSize(Visualizer.getCaptureSizeRange()[1]);
+ mVisualizer.setDataCaptureListener(new Visualizer.OnDataCaptureListener() {
+ public void onWaveFormDataCapture(Visualizer visualizer, byte[] bytes,
+ int samplingRate) {
+ mVisualizerView.updateVisualizer(bytes);
+ }
+
+ public void onFftDataCapture(Visualizer visualizer, byte[] bytes, int samplingRate) {}
+ }, Visualizer.getMaxCaptureRate() / 2, true, false);
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ if (isFinishing() && mMediaPlayer != null) {
+ mVisualizer.release();
+ mEqualizer.release();
+ mMediaPlayer.release();
+ mMediaPlayer = null;
+ }
+ }
+}
+
+/**
+ * A simple class that draws waveform data received from a
+ * {@link Visualizer.OnDataCaptureListener#onWaveFormDataCapture }
+ */
+class VisualizerView extends View {
+ private byte[] mBytes;
+ private float[] mPoints;
+ private Rect mRect = new Rect();
+
+ private Paint mForePaint = new Paint();
+
+ public VisualizerView(Context context) {
+ super(context);
+ init();
+ }
+
+ private void init() {
+ mBytes = null;
+
+ mForePaint.setStrokeWidth(1f);
+ mForePaint.setAntiAlias(true);
+ mForePaint.setColor(Color.rgb(0, 128, 255));
+ }
+
+ public void updateVisualizer(byte[] bytes) {
+ mBytes = bytes;
+ invalidate();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ super.onDraw(canvas);
+
+ if (mBytes == null) {
+ return;
+ }
+
+ if (mPoints == null || mPoints.length < mBytes.length * 4) {
+ mPoints = new float[mBytes.length * 4];
+ }
+
+ mRect.set(0, 0, getWidth(), getHeight());
+
+ for (int i = 0; i < mBytes.length - 1; i++) {
+ mPoints[i * 4] = mRect.width() * i / (mBytes.length - 1);
+ mPoints[i * 4 + 1] = mRect.height() / 2
+ + ((byte) (mBytes[i] + 128)) * (mRect.height() / 2) / 128;
+ mPoints[i * 4 + 2] = mRect.width() * (i + 1) / (mBytes.length - 1);
+ mPoints[i * 4 + 3] = mRect.height() / 2
+ + ((byte) (mBytes[i + 1] + 128)) * (mRect.height() / 2) / 128;
+ }
+
+ canvas.drawLines(mPoints, mForePaint);
+ }
+}
diff --git a/samples/SipDemo/Android.mk b/samples/SipDemo/Android.mk
new file mode 100644
index 000000000..6363727ce
--- /dev/null
+++ b/samples/SipDemo/Android.mk
@@ -0,0 +1,16 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := samples
+
+# Only compile source java files in this apk.
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := SipDemo
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_PACKAGE)
+
+# Use the following include to make our test apk.
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/samples/SipDemo/AndroidManifest.xml b/samples/SipDemo/AndroidManifest.xml
new file mode 100644
index 000000000..8fb767569
--- /dev/null
+++ b/samples/SipDemo/AndroidManifest.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SipDemo/_index.html b/samples/SipDemo/_index.html
new file mode 100644
index 000000000..dbc90373f
--- /dev/null
+++ b/samples/SipDemo/_index.html
@@ -0,0 +1,52 @@
+
This is a demo application highlighting how to make internet-based calls with
+the SIP API. The application uses a walkie-talkie style interface, allowing you
+to only be heard when the button is pushed down.
+
+
The source code for this demo app shows how to accomplish three key things
+with SIP: Make a call, receive a call, and signal to the Android platform that
+your app wants to receive incoming SIP calls, so that they can be handled from
+within the application.
+
+
+
The application includes:
+
SipSettings
+ — a PreferenceActivity that supplies basic settings for SIP
+ authentication.
+ IncomingCallReceiver — a BroadcastReceiver
+ that listens for incoming SIP calls and passes them to
+ WalkieTalkieActivity for handling.
WalkieTalkieActivity
+ — a activity that login to SIP provider and registers the device to
+ receive incoming SIP, handles incoming calls and makes outgoing calls, managing
+ UI during the call.
+
If you are developing an application that uses the SIP API, remember that the
+feature is supported only on Android 2.3 (API level 9) and higher versions of
+the platform. Also, among devices running Android 2.3 (API level 9) or higher,
+not all devices will offer SIP support. To ensure that your application can only
+be installed on devices that are capable of supporting SIP, remember to add the
+following to the application's manifest before publishing to Android Market:
+
<uses-sdk android:minSdkVersion="9" />, which
+ indicates to Android Market and the platform that your application requires
+ Android 2.3 or higher. For more information, see API Levels and the
+ documentation for the <uses-sdk>
+ element.
To control how Android Market filters your application
+from devices that do not support SIP, remember to add the following to the
+application's manifest
<uses-feature
+ android:name="android.hardware.sip.voip" />, which tells Android
+ Market that your application uses the SIP API. The declaration should include
+ an android:required attribute that indicates whether you want
+ Android Market to filter the application from devices that do not offer SIP
+ support. Other <uses-feature> declarations may also be
+ needed, depending on your implementation. For more information, see the
+ documentation for the <uses-feature>
+ element.
+
For more information about using the SIP API, see the android.net.sip
+documentation.
+
+
diff --git a/samples/SipDemo/res/drawable/btn_record.xml b/samples/SipDemo/res/drawable/btn_record.xml
new file mode 100644
index 000000000..a803da11c
--- /dev/null
+++ b/samples/SipDemo/res/drawable/btn_record.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
+
+
+
diff --git a/samples/SipDemo/res/drawable/btn_speak_normal.png b/samples/SipDemo/res/drawable/btn_speak_normal.png
new file mode 100644
index 000000000..fb86ceba1
Binary files /dev/null and b/samples/SipDemo/res/drawable/btn_speak_normal.png differ
diff --git a/samples/SipDemo/res/drawable/btn_speak_pressed.png b/samples/SipDemo/res/drawable/btn_speak_pressed.png
new file mode 100644
index 000000000..cffdf918c
Binary files /dev/null and b/samples/SipDemo/res/drawable/btn_speak_pressed.png differ
diff --git a/samples/SipDemo/res/drawable/btn_speak_selected.png b/samples/SipDemo/res/drawable/btn_speak_selected.png
new file mode 100644
index 000000000..99a1a6f57
Binary files /dev/null and b/samples/SipDemo/res/drawable/btn_speak_selected.png differ
diff --git a/samples/SipDemo/res/drawable/icon.png b/samples/SipDemo/res/drawable/icon.png
new file mode 100644
index 000000000..64e3601c2
Binary files /dev/null and b/samples/SipDemo/res/drawable/icon.png differ
diff --git a/samples/SipDemo/res/layout/call_address_dialog.xml b/samples/SipDemo/res/layout/call_address_dialog.xml
new file mode 100644
index 000000000..2bd75565e
--- /dev/null
+++ b/samples/SipDemo/res/layout/call_address_dialog.xml
@@ -0,0 +1,45 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SipDemo/res/layout/walkietalkie.xml b/samples/SipDemo/res/layout/walkietalkie.xml
new file mode 100644
index 000000000..bdf634c84
--- /dev/null
+++ b/samples/SipDemo/res/layout/walkietalkie.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/samples/SipDemo/res/values/strings.xml b/samples/SipDemo/res/values/strings.xml
new file mode 100644
index 000000000..a0d49bc0a
--- /dev/null
+++ b/samples/SipDemo/res/values/strings.xml
@@ -0,0 +1,22 @@
+
+
+
+
+ SIP Demo
+ SIP Address to contact
+ Talk
+
diff --git a/samples/SipDemo/res/xml/preferences.xml b/samples/SipDemo/res/xml/preferences.xml
new file mode 100644
index 000000000..590e0924c
--- /dev/null
+++ b/samples/SipDemo/res/xml/preferences.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
+
+
diff --git a/samples/SipDemo/src/com/example/android/sip/IncomingCallReceiver.java b/samples/SipDemo/src/com/example/android/sip/IncomingCallReceiver.java
new file mode 100644
index 000000000..ea27a0a85
--- /dev/null
+++ b/samples/SipDemo/src/com/example/android/sip/IncomingCallReceiver.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.sip;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.net.sip.*;
+import android.util.Log;
+
+/**
+ * Listens for incoming SIP calls, intercepts and hands them off to WalkieTalkieActivity.
+ */
+public class IncomingCallReceiver extends BroadcastReceiver {
+ /**
+ * Processes the incoming call, answers it, and hands it over to the
+ * WalkieTalkieActivity.
+ * @param context The context under which the receiver is running.
+ * @param intent The intent being received.
+ */
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ SipAudioCall incomingCall = null;
+ try {
+
+ SipAudioCall.Listener listener = new SipAudioCall.Listener() {
+ @Override
+ public void onRinging(SipAudioCall call, SipProfile caller) {
+ try {
+ call.answerCall(30);
+ } catch (Exception e) {
+ e.printStackTrace();
+ }
+ }
+ };
+
+ WalkieTalkieActivity wtActivity = (WalkieTalkieActivity) context;
+
+ incomingCall = wtActivity.manager.takeAudioCall(intent, listener);
+ incomingCall.answerCall(30);
+ incomingCall.startAudio();
+ incomingCall.setSpeakerMode(true);
+ if(incomingCall.isMuted()) {
+ incomingCall.toggleMute();
+ }
+
+ wtActivity.call = incomingCall;
+
+ wtActivity.updateStatus(incomingCall);
+
+ } catch (Exception e) {
+ if (incomingCall != null) {
+ incomingCall.close();
+ }
+ }
+ }
+
+}
diff --git a/samples/SipDemo/src/com/example/android/sip/SipSettings.java b/samples/SipDemo/src/com/example/android/sip/SipSettings.java
new file mode 100644
index 000000000..2f0624bbf
--- /dev/null
+++ b/samples/SipDemo/src/com/example/android/sip/SipSettings.java
@@ -0,0 +1,34 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.sip;
+
+import android.os.Bundle;
+import android.preference.PreferenceActivity;
+
+/**
+ * Handles SIP authentication settings for the Walkie Talkie app.
+ */
+public class SipSettings extends PreferenceActivity {
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ // Note that none of the preferences are actually defined here.
+ // They're all in the XML file res/xml/preferences.xml.
+ super.onCreate(savedInstanceState);
+ addPreferencesFromResource(R.xml.preferences);
+ }
+}
diff --git a/samples/SipDemo/src/com/example/android/sip/WalkieTalkieActivity.java b/samples/SipDemo/src/com/example/android/sip/WalkieTalkieActivity.java
new file mode 100644
index 000000000..4c187eddc
--- /dev/null
+++ b/samples/SipDemo/src/com/example/android/sip/WalkieTalkieActivity.java
@@ -0,0 +1,358 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.example.android.sip;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.PendingIntent;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.SharedPreferences;
+import android.os.Bundle;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.*;
+import android.net.sip.*;
+import android.widget.EditText;
+import android.widget.TextView;
+import android.widget.ToggleButton;
+
+import java.text.ParseException;
+
+/**
+ * Handles all calling, receiving calls, and UI interaction in the WalkieTalkie app.
+ */
+public class WalkieTalkieActivity extends Activity implements View.OnTouchListener {
+
+ public String sipAddress = null;
+
+ public SipManager manager = null;
+ public SipProfile me = null;
+ public SipAudioCall call = null;
+ public IncomingCallReceiver callReceiver;
+
+ private static final int CALL_ADDRESS = 1;
+ private static final int SET_AUTH_INFO = 2;
+ private static final int UPDATE_SETTINGS_DIALOG = 3;
+ private static final int HANG_UP = 4;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.walkietalkie);
+
+ ToggleButton pushToTalkButton = (ToggleButton) findViewById(R.id.pushToTalk);
+ pushToTalkButton.setOnTouchListener(this);
+
+ // Set up the intent filter. This will be used to fire an
+ // IncomingCallReceiver when someone calls the SIP address used by this
+ // application.
+ IntentFilter filter = new IntentFilter();
+ filter.addAction("android.SipDemo.INCOMING_CALL");
+ callReceiver = new IncomingCallReceiver();
+ this.registerReceiver(callReceiver, filter);
+
+ // "Push to talk" can be a serious pain when the screen keeps turning off.
+ // Let's prevent that.
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
+
+ initializeManager();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+ // When we get back from the preference setting Activity, assume
+ // settings have changed, and re-login with new auth info.
+ initializeManager();
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ if (call != null) {
+ call.close();
+ }
+
+ closeLocalProfile();
+
+ if (callReceiver != null) {
+ this.unregisterReceiver(callReceiver);
+ }
+ }
+
+ public void initializeManager() {
+ if(manager == null) {
+ manager = SipManager.newInstance(this);
+ }
+
+ initializeLocalProfile();
+ }
+
+ /**
+ * Logs you into your SIP provider, registering this device as the location to
+ * send SIP calls to for your SIP address.
+ */
+ public void initializeLocalProfile() {
+ if (manager == null) {
+ return;
+ }
+
+ if (me != null) {
+ closeLocalProfile();
+ }
+
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getBaseContext());
+ String username = prefs.getString("namePref", "");
+ String domain = prefs.getString("domainPref", "");
+ String password = prefs.getString("passPref", "");
+
+ if (username.length() == 0 || domain.length() == 0 || password.length() == 0) {
+ showDialog(UPDATE_SETTINGS_DIALOG);
+ return;
+ }
+
+ try {
+ SipProfile.Builder builder = new SipProfile.Builder(username, domain);
+ builder.setPassword(password);
+ me = builder.build();
+
+ Intent i = new Intent();
+ i.setAction("android.SipDemo.INCOMING_CALL");
+ PendingIntent pi = PendingIntent.getBroadcast(this, 0, i, Intent.FILL_IN_DATA);
+ manager.open(me, pi, null);
+
+
+ // This listener must be added AFTER manager.open is called,
+ // Otherwise the methods aren't guaranteed to fire.
+
+ manager.setRegistrationListener(me.getUriString(), new SipRegistrationListener() {
+ public void onRegistering(String localProfileUri) {
+ updateStatus("Registering with SIP Server...");
+ }
+
+ public void onRegistrationDone(String localProfileUri, long expiryTime) {
+ updateStatus("Ready");
+ }
+
+ public void onRegistrationFailed(String localProfileUri, int errorCode,
+ String errorMessage) {
+ updateStatus("Registration failed. Please check settings.");
+ }
+ });
+ } catch (ParseException pe) {
+ updateStatus("Connection Error.");
+ } catch (SipException se) {
+ updateStatus("Connection error.");
+ }
+ }
+
+ /**
+ * Closes out your local profile, freeing associated objects into memory
+ * and unregistering your device from the server.
+ */
+ public void closeLocalProfile() {
+ if (manager == null) {
+ return;
+ }
+ try {
+ if (me != null) {
+ manager.close(me.getUriString());
+ }
+ } catch (Exception ee) {
+ Log.d("WalkieTalkieActivity/onDestroy", "Failed to close local profile.", ee);
+ }
+ }
+
+ /**
+ * Make an outgoing call.
+ */
+ public void initiateCall() {
+
+ updateStatus(sipAddress);
+
+ try {
+ SipAudioCall.Listener listener = new SipAudioCall.Listener() {
+ // Much of the client's interaction with the SIP Stack will
+ // happen via listeners. Even making an outgoing call, don't
+ // forget to set up a listener to set things up once the call is established.
+ @Override
+ public void onCallEstablished(SipAudioCall call) {
+ call.startAudio();
+ call.setSpeakerMode(true);
+ call.toggleMute();
+ updateStatus(call);
+ }
+
+ @Override
+ public void onCallEnded(SipAudioCall call) {
+ updateStatus("Ready.");
+ }
+ };
+
+ call = manager.makeAudioCall(me.getUriString(), sipAddress, listener, 30);
+
+ }
+ catch (Exception e) {
+ Log.i("WalkieTalkieActivity/InitiateCall", "Error when trying to close manager.", e);
+ if (me != null) {
+ try {
+ manager.close(me.getUriString());
+ } catch (Exception ee) {
+ Log.i("WalkieTalkieActivity/InitiateCall",
+ "Error when trying to close manager.", ee);
+ ee.printStackTrace();
+ }
+ }
+ if (call != null) {
+ call.close();
+ }
+ }
+ }
+
+ /**
+ * Updates the status box at the top of the UI with a messege of your choice.
+ * @param status The String to display in the status box.
+ */
+ public void updateStatus(final String status) {
+ // Be a good citizen. Make sure UI changes fire on the UI thread.
+ this.runOnUiThread(new Runnable() {
+ public void run() {
+ TextView labelView = (TextView) findViewById(R.id.sipLabel);
+ labelView.setText(status);
+ }
+ });
+ }
+
+ /**
+ * Updates the status box with the SIP address of the current call.
+ * @param call The current, active call.
+ */
+ public void updateStatus(SipAudioCall call) {
+ String useName = call.getPeerProfile().getDisplayName();
+ if(useName == null) {
+ useName = call.getPeerProfile().getUserName();
+ }
+ updateStatus(useName + "@" + call.getPeerProfile().getSipDomain());
+ }
+
+ /**
+ * Updates whether or not the user's voice is muted, depending on whether the button is pressed.
+ * @param v The View where the touch event is being fired.
+ * @param event The motion to act on.
+ * @return boolean Returns false to indicate that the parent view should handle the touch event
+ * as it normally would.
+ */
+ public boolean onTouch(View v, MotionEvent event) {
+ if (call == null) {
+ return false;
+ } else if (event.getAction() == MotionEvent.ACTION_DOWN && call != null && call.isMuted()) {
+ call.toggleMute();
+ } else if (event.getAction() == MotionEvent.ACTION_UP && !call.isMuted()) {
+ call.toggleMute();
+ }
+ return false;
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu) {
+ menu.add(0, CALL_ADDRESS, 0, "Call someone");
+ menu.add(0, SET_AUTH_INFO, 0, "Edit your SIP Info.");
+ menu.add(0, HANG_UP, 0, "End Current Call.");
+
+ return true;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case CALL_ADDRESS:
+ showDialog(CALL_ADDRESS);
+ break;
+ case SET_AUTH_INFO:
+ updatePreferences();
+ break;
+ case HANG_UP:
+ if(call != null) {
+ try {
+ call.endCall();
+ } catch (SipException se) {
+ Log.d("WalkieTalkieActivity/onOptionsItemSelected",
+ "Error ending call.", se);
+ }
+ call.close();
+ }
+ break;
+ }
+ return true;
+ }
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ switch (id) {
+ case CALL_ADDRESS:
+
+ LayoutInflater factory = LayoutInflater.from(this);
+ final View textBoxView = factory.inflate(R.layout.call_address_dialog, null);
+ return new AlertDialog.Builder(this)
+ .setTitle("Call Someone.")
+ .setView(textBoxView)
+ .setPositiveButton(
+ android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ EditText textField = (EditText)
+ (textBoxView.findViewById(R.id.calladdress_edit));
+ sipAddress = textField.getText().toString();
+ initiateCall();
+
+ }
+ })
+ .setNegativeButton(
+ android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ // Noop.
+ }
+ })
+ .create();
+
+ case UPDATE_SETTINGS_DIALOG:
+ return new AlertDialog.Builder(this)
+ .setMessage("Please update your SIP Account Settings.")
+ .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ updatePreferences();
+ }
+ })
+ .setNegativeButton(
+ android.R.string.cancel, new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int whichButton) {
+ // Noop.
+ }
+ })
+ .create();
+ }
+ return null;
+ }
+
+ public void updatePreferences() {
+ Intent settingsActivity = new Intent(getBaseContext(),
+ SipSettings.class);
+ startActivity(settingsActivity);
+ }
+}