blob: ec3163cb889a911b72466bb83be04c78c53fc4c7 [file] [log] [blame]
/*
* 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 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
);
// Build the notification object.
mNotification = new Notification.Builder(this) // The builder requires the context
.setSmallIcon(R.drawable.stat_sample) // the status icon
.setTicker(notificationText) // the status text
.setWhen(System.currentTimeMillis()) // the time stamp
.setContentTitle(getText(R.string.alarm_service_label)) // the label of the entry
.setContentText(notificationText) // the contents of the entry
.setContentIntent(mContentIntent) // The intent to send when the entry is clicked
.build();
// 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
);
}
}