am 49fe761d: Merge "Cherry pick from master: Sample app and test package for a Service" into gingerbread

* commit '49fe761ddcf779f1913d22706a2a6153b80aa397':
  Cherry pick from master: Sample app and test package for a Service
This commit is contained in:
Dirk Dougherty
2010-11-03 13:13:33 -07:00
committed by Android Git Automerger
11 changed files with 695 additions and 0 deletions

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
Declares the contents of this Android application. The xmlns:android
attribute brings in the Android platform namespace, and the
"package" attribute provides a unique name for the application.
If you use this file as a template in your own application, you must change
the package name from "com.example.android" to one that you own or have
control over.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.newalarm"
android:versionCode="2"
android:versionName="2.0">
<!--
Declares the application, its icon, and its visible label
-->
<application
android:icon="@drawable/icon"
android:label="@string/app_name">
<!--
Declares the activity's class name and visible label. The leading "." indicates
that the name should be preceded by the application's Android package name.
-->
<activity
android:name=".AlarmActivity"
android:label="@string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<service
android:name=".AlarmService"
android:label="@string/alarm_service"
android:process="com.example.android.newalarm">
</service>
</application>
<uses-sdk android:targetSdkVersion="4" android:minSdkVersion="3"/>
</manifest>

29
samples/Alarm/_index.html Normal file
View File

@@ -0,0 +1,29 @@
<p>
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 <a href="../AlarmServiceTest/index.html">Alarm Service Test</a>
sample test application.
</p>
<p>
This application demonstrates a simple Android service that is started when needed by
<code>Context.startService(Intent)</code> 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.
</p>
<p>
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.
</p>
<p>
The application also contains the <code>AlarmActivity</code> 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.
</p>
<p>
The test application <a href="tests/index.html">AlarmServiceTest</a>
shows you how to set up a test of this service.
</p>

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 364 B

View File

@@ -0,0 +1,44 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!-- Demonstrates starting and stopping a local service.
See corresponding Java code com.android.sdk.app.LocalSerice.java. -->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_weight="0"
android:paddingBottom="4dip"
android:text="@string/alarm_service"/>
<Button android:id="@+id/start_alarm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/start_alarm_service">
<requestFocus />
</Button>
<Button android:id="@+id/stop_alarm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/stop_alarm_service" />
</LinearLayout>

View File

@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<resources>
<string name="app_name">Alarm</string>
<string name="alarm_service">
This shows how to schedule a repeating alarm that starts a service.</string>
<string name="start_alarm_service">Start Alarm Service</string>
<string name="stop_alarm_service">Stop Alarm Service</string>
<string name="repeating_started">
Repeating timer started. Starts AlarmService every 30 seconds.</string>
<string name="alarm_service_started">The sample service is running.</string>
<string name="alarm_service_label">Sample Alarm Service</string>
<string name="alarm_service_finished">The sample service is no longer running.</string>
<string name="repeating_stopped">
Repeating timer stopped. AlarmService will no longer be started.</string>
</resources>

View File

@@ -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.
* <p>
* 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.
* </p>
* <p>
* When the user clicks the "Stop Alarm Service" button, it stops the countdown timer.
* </p>
*/
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.
* <p>
* This method is automatically called when Android starts the Activity
* </p>
*/
@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
}
};
}

View File

@@ -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;
/**
* <p>
* 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.
* </p>
* <p>
* 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.
* </p>
* <p>
* This service is provided as the service under test for the sample test application
* AlarmServiceTest.
* </p>
* <p>
* 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.
* </p>
*/
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
);
}
}

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<!--
Declare the contents of this Android test package. The xmlns:android
attribute brings in the Android platform namespace, and the
"package" attribute provides a unique name for the package.
If you use this file as a template in your own test package, you must change
the package name from "com.example.android" to one that you own or have
control over.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.android.newalarm.test"
android:versionCode="1"
android:versionName="1.0">
<!--
Must use the application element to include the uses-library element.
-->
<application>
<!--
Tells Android to include this library in the test package's class loader.
The test runner is not included by default in a manifest file.
-->
<uses-library android:name="android.test.runner" />
</application>
<!--
Specifies that the test package requires API platform level 3 (Android 1.5) or above.
The installer will not install this package onto a device or emulator that is running an
older platform version.
-->
<uses-sdk android:minSdkVersion="3" />
<!--
Declares the instrumentation for this application. The instrumentation class is
specified by the "name" attribute, and must name a subclass of
android.app.Instrumentation. The application that is run by the instrumentation object is
specified by the "targetPackage" attribute.
-->
<instrumentation
android:targetPackage="com.example.android.newalarm"
android:name="android.test.InstrumentationTestRunner" />
</manifest>

View File

@@ -0,0 +1,50 @@
<p>
This sample is the test application for the <a href="../Alarm/index.html">Alarm</a>
sample application. It tests the application's <code>AlarmService</code> service.
</p>
<p>
The test application uses the
<a href="../../../reference/android/test/ServiceTestCase.html">
<code>ServiceTestCase</code></a> test case class,
which extends the JUnit <a href="../../../reference/junit/framework/TestCase.html">
<code>TestCase</code></a> class. The test runner is
<a href="../../../reference/android/test/InstrumentationTestRunner.html">
<code>InstrumentationTestRunner</code></a>.
</p>
<p>
The application shows how to set up a test application project,
how to create the <a href="AndroidManifest.html"><code>AndroidManifest.xml</code></a>
file for a test application, and how to set up a test case class for a service. The
test case class, <a href="src/com/android/example/newalarm/ServiceAlarmTest.html">
<code>AlarmServiceTest</code></a>, contains tests that demonstrate the following
Android test patterns:
</p>
<ul>
<li>
Test setup: The <code>setUp()</code> method re-initializes the state of the
service under test before each test is run.
</li>
<li>
Service start: The <code>Service.testServiceCreate()</code> test confirms that the
service starts correctly and initializes the variables it needs to provide its
services.
</li>
</ul>
<p>
The <a href="AndroidManifest.html">manifest</a> declares an <code>&lt;instrumentation&gt;</code>
element that links the test application with the application under test. Specifically, the
element's <code>android:name</code> attribute specifies <code>InstrumentationTestRunner</code>
as the instrumentation to use. The <code>android:targetPackage</code> attribute specifies
<code>com.android.example.newalarm</code> as the name of the Android package that contains the
service under test.
</p>
<p class="note">
<strong>Note:</strong> <code>AlarmServiceTest.java</code> uses the Java package name
<code>com.example.android.newalarm</code>, which is the same package used by service under
test, <code>AlarmService.java</code>. 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 <code>R.java</code> for <code>AlarmServiceTest</code> uses the
Java package name <code>com.example.android.newalarm.test</code>. For the same reason, the
Android package name for the test application (specified in the manifest file), is
<code>com.example.android.newalarm.test</code>.
</p>

View File

@@ -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<AlarmService> {
// 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);
}
}