Taste of Android :: Part-10


Bhaskar S 07/19/2013


Android Application Development - IX

Until now we have been exploring various aspects of the Android component Activity. The next important Android component to explore is Service.

A Service is an Android component that is used to perform some long-running task in the background without the need for any user interface.

The Android Alarm Clock application is an example of a Service component.

To implement a custom Service, one needs to sub-class the Android framework class android.app.Service.

In the custom Service class will override the following callback methods:

Let us create a new Android application named DroidAlarmService to illustrate the concept of using Android Service component to build our own custom alarm clock application.

This application will display a screen with a Time picker and two Buttons - one to set the alarm and other to cancel the alarm. We will not go step-by-step to show the various screens since we already did that in Part-2 for the DroidTipCalculator application.

Modify the contents of the dimens.xml file to look like the one shown in the listing 10.1 below:

Listing-10.1
<?xml version="1.0" encoding="utf-8"?>
<resources>

<dimen name="title_size">26sp</dimen>
<dimen name="margin_size">26dp</dimen>

</resources>

Next, modify the contents of the strings.xml file to look like the one shown in the listing 10.2 below:

Listing-10.2
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!-- id references -->

<string name="textview">textview</string>
<string name="timepicker">timepicker</string>
<string name="button1">button1</string>
<string name="button2">button2</string>

<!-- Text references -->

<string name="app_name">DroidAlarmService</string>
<string name="simple_alarm">Simple Alarm</string>
<string name="set_alarm">Set Alarm</string>
<string name="cancel_alarm">Cancel Alarm</string>

</resources>

The contents of the activity_time_pick.xml layout file will look like the one shown in the listing 10.3 below:

Listing-10.3
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >

<TextView
android:id="@+id/textview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/margin_size"
android:paddingBottom="@dimen/margin_size"
android:text="@string/simple_alarm"
android:textColor="#cc3300"
android:textSize="@dimen/title_size"
android:textStyle="bold"
android:typeface="sans" />

<TimePicker
android:id="@+id/timepicker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_size" />

<Button
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/set_alarm" />

<Button
android:id="@+id/button2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/cancel_alarm" />

</LinearLayout>

Previewing the activity_time_pick.xml layout file in the Graphical Mode in Eclipse will look like the one shown in the figure 10.1 below:

Activity Time Picker Image
Figure-10.1

The contents of the java source file TimePickActivity.java will look like the one shown in the listing 10.4 below:

Listing-10.4
package com.polarsparc.android.droidalarmservice;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.Messenger;
import android.view.View;
import android.widget.Button;
import android.widget.TimePicker;
import android.widget.Toast;


public class TimePickActivity extends Activity {

private int pickedHour;
private int pickedMinute;

private Handler handler;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);

setContentView(R.layout.activity_time_pick);

handler = new Handler() {
@Override
public void handleMessage(Message msg) {
Button set = (Button) findViewById(R.id.button1);
set.setEnabled(true);

Button cancel = (Button) findViewById(R.id.button2);
cancel.setEnabled(false);

Toast.makeText(getBaseContext(),
String.format("Wake Up Alarm for %02d:%02d !!!",
pickedHour, pickedMinute), Toast.LENGTH_LONG).show();
}
};

TimePicker picker = (TimePicker) findViewById(R.id.timepicker);
pickedHour = picker.getCurrentHour();
pickedMinute = picker.getCurrentMinute();
picker.setOnTimeChangedListener(new TimePicker.OnTimeChangedListener() {
@Override
public void onTimeChanged(TimePicker view, int hour, int minute) {
pickedHour = hour;
pickedMinute = minute;
}
});

final Button set = (Button) findViewById(R.id.button1);
set.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
set.setEnabled(false);

Button cancel = (Button) findViewById(R.id.button2);
cancel.setEnabled(true);

Intent intent = new Intent(getBaseContext(), MyAlarmService.class);
intent.putExtra("pickedHour", pickedHour);
intent.putExtra("pickedMinute", pickedMinute);
intent.putExtra("Messenger", new Messenger(handler));

startService(intent);
}
});

final Button cancel = (Button) findViewById(R.id.button2);
cancel.setEnabled(false);
cancel.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
cancel.setEnabled(false);

Button set = (Button) findViewById(R.id.button1);
set.setEnabled(true);

stopService(new Intent(getBaseContext(), MyAlarmService.class));
}
});
}

}

The contents of the java source file MyAlarmService.java will look like the one shown in the listing 10.5 below:

Listing-10.5
package com.polarsparc.android.droidalarmservice;

import android.app.Service;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.util.Log;

import java.util.Calendar;

public class MyAlarmService extends Service {

private static String TAG = "MyAlarmService";

private AlarmTask task = null;

private Messenger messenger = null;

@Override
public void onCreate() {
// Not used
}

@Override
public IBinder onBind(Intent intent) {
// Not used
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
int pickedHour = intent.getIntExtra("pickedHour", -1);
int pickedMinute = intent.getIntExtra("pickedMinute", -1);

Bundle extras = intent.getExtras();

messenger =(Messenger) extras.get("Messenger");

Log.i(TAG, "MyAlarmService - onStartCommand() :: pickedHour = " +
pickedHour + ", pickedMinute = " + pickedMinute);

if (pickedHour >= 0 && pickedMinute >= 0 && messenger != null) {
task = new AlarmTask();
task.execute(new Integer[] {pickedHour, pickedMinute});
}

return START_STICKY;
}

@Override
public void onDestroy() {
if (task != null) {
task.cancel(true);
}
}

private class AlarmTask extends AsyncTask<Integer, Void, Boolean> {
private int pickedHour;
private int pickedMinute;

@Override
protected Boolean doInBackground(Integer... params) {
pickedHour = params[0];
pickedMinute = params[1];

Log.i(TAG, "AlarmTask - doInBackground() :: pickedHour = " +
pickedHour + ", pickedMinute = " + pickedMinute);

for (;;) {
try {
// Sleep for 1 second
Thread.sleep(1000);

if (isCancelled()) {
break;
}

Calendar cal = Calendar.getInstance();

int curHour = cal.get(Calendar.HOUR_OF_DAY);
int curMinute = cal.get(Calendar.MINUTE);

if (curHour == pickedHour && curMinute == pickedMinute) {
Log.i(TAG, "AlarmTask - doInBackground() :: Alarm triggered !!!");

return true;
}
}
catch (Throwable t) {
if (isCancelled()) {
break;
}
Log.e(TAG, "AlarmTask <" + Thread.currentThread().getName() +
"> - doInBackground() exception !!!", t);
}
}

return false;
}

@Override
protected void onProgressUpdate(Void... progress) {
// Not used
}

@Override
protected void onPostExecute(Boolean result) {
if (result) {
Message msg = Message.obtain();

try {
messenger.send(msg);
}
catch (Throwable t) {
Log.e(TAG, "AlarmTask <" + Thread.currentThread().getName() +
"> - onPostExecute() exception !!!", t);
}
}
}
}

}

Here is how our custom alarm clock will work:

Finally, modify the contents of the AndroidManifest.xml file to look like the one shown in the listing 10.6 below:

Listing-10.6
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.polarsparc.android.droidalarmservice"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk
android:minSdkVersion="14"
android:targetSdkVersion="14" />

<application
android:allowBackup="true"
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >

<activity
android:name="com.polarsparc.android.droidalarmservice.TimePickActivity"
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=".MyAlarmService" />

</application>

</manifest>

The important change to notice is the additional tag named <Service>. We use this tag to define our service MyAlarmService.

We are now ready to test our DroidAlarmService application on the virtual Android device we created in Part-1. We will create a Run Configuration for DroidAlarmService as we did in Part-2 for DroidTipCalculator.

Once the run configuration for DroidAlarmService is ready, we will Run the application and the application will come to life as shown in the following figure 10.2 below:

Alarm Time Picker Image
Figure-10.2

Pick the desired alarm time (hour and minute) and click on the Set Alarm button. This will launch the background service MyAlarmService resulting in the following figure 10.3 below:

Alarm Service Image
Figure-10.3

When the alarm goes off at the desired time (hour and minute), it will display a Toast message as shown in the following figure 10.4 below:

Alarm Toast Image
Figure-10.4

References

Taste of Android :: Part-1

Taste of Android :: Part-2

Taste of Android :: Part-3

Taste of Android :: Part-4

Taste of Android :: Part-5

Taste of Android :: Part-6

Taste of Android :: Part-7

Taste of Android :: Part-8

Taste of Android :: Part-9