diff --git a/samples/Alarm/AndroidManifest.xml b/samples/Alarm/AndroidManifest.xml
new file mode 100644
index 000000000..6a7a831a8
--- /dev/null
+++ b/samples/Alarm/AndroidManifest.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+ 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 @@ + + + + + ++ * 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 @@ + + + + +
+ This sample is the test application for the Alarm
+ sample application. It tests the application's AlarmService service.
+
+ The test application uses the
+
+ ServiceTestCase test case class,
+ which extends the JUnit
+ TestCase class. The test runner is
+
+ InstrumentationTestRunner.
+
+ 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:
+
setUp() method re-initializes the state of the
+ service under test before each test is run.
+ 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.
+