In this Taste of Android series so far we
have
covered quite
a bit of ground involving Activity, Intent,
Handler, AsyncTask,
Service, IntentService,
Notification, etc.
Wouldn't it be nice to tie most of these concepts into an
application ???
We will do exactly that by creating a simple Stock
Alert application. The user will be able to set a Low price
and/or a High price alert for
one or more stock symbols and get notified when the condition occurs.
Let us create a new Android application named
DroidStockAlert that will put to test many of the
concepts we have touched upon so far.
We will not go step-by-step to show the various
screens since we already did that in
Part-2
for the DroidTipCalculator
application.
First we will need to download four icons of size 16x16
and save it in the
directory res/drawable-mdpi. There are
tons of open
source friendly icons downloadable from the site
openwebgraphics.
Modify the contents of the dimens.xml
file to look like the one shown in the
listing 12.1 below:
Next, modify the contents of the strings.xml
file to look like the one shown in the
listing 12.2 below:
We will have three layout definitions files - the first is for the
main stock
ticker screen, the second is for each row of the stock ticker list, and
the third
is for setting or cancelling the low or high alert conditions. The
layout file for
the main stock ticker screen is named activity_stock_alert.xml,
the layout file for the list row is named list_item_row.xml,
and the layout file for setting or cancelling alerts is named
activity_apply_cancel.xml.
The contents of the activity_stock_alert.xml
layout file will look like the one shown in the listing 12.3 below:
The contents of the list_item_row.xml
layout file will look like the one shown in the listing 12.4 below:
The contents of the activity_apply_cancel.xml
layout file will look like the one shown in the listing 12.5 below:
The contents of the java source file TickSignal.java
will look like the one shown in the listing 12.6 below:
We will need an object to encapsulate a stock ticker information
such
as the stock symbol, the current price, the price change, the low price
alert value, the high price alert value, etc. This information is
encapsulated in the class StockTicker.
The contents of the java source file StockTicker.java
will look like the one shown in the listing 12.7 below:
In this DroidStockAlert application, the
stock prices
will be updated in the background in an IntentService
and accessed by the main stock ticker screen Activity.
This means we need a way to share across multiple Android components
(Activity, Service,
etc).
So how can we do that ??? Enter the Android framework class
android.app.Application. In order to
share
state across multiple Android components, one needs to create a custom
implementation of Application by extending
it
and wrapping application state object(s). In this application, we
create
a custom class named StockAlertApplication.
The contents of the java source file StockAlertApplication.java
will look like the one shown in the listing 12.8 below:
The class StockAlertApplication allows us
to
share both the Handler object instance and a
list
of StockTicker object instances.
The contents of the java source file StockTickerListAdapter.java
will look like the one shown in the listing 12.9 below:
The StockTickerListAdapter class is a
custom
ArrayAdapter that is used to draw each row of
the ListView of the activity
activity_stock_alert.xml.
Note that the rows are displayed in alternating background colors.
Each row displays three text columns (symbol, price, and change) and
one image icon column. Clicking on the image of a row launches the
activity
activity_apply_cancel.xml. Since an activity
can
only be launched from the main UI Thread, we
need
reference to the Handler object of the main
UI Thread to send it a
Message.
Once the activity activity_apply_cancel.xml
is
launched, one can either set or cancel (previously set) Low and High
Price
alerts. Note that the image icon changes when there is alert set.
The contents of the java source file StockAlertActivity.java
will look like the one shown in the listing 12.10 below:
In the listing 12.10 above, notice we have created a static inner
class
for the Handler implementation. Why did we
do this
??? If we dont do this (as was the case in Part-7, we will see
the
following Android warning in Eclipse:
This Handler class should be static or leaks
might occur
In Java, inner class(es) implicitly hold references to their outer
class.
So if we had followed what we had done in Part-7, the inner
Handler class would hold reference to the
outer
activity class. An Handler class internally
uses a message queue to queue incoming Message
objects for processing. If we change the orientation of the screen,
then the activity is killed and if there are still pending
Message objects in the message queue of the
Handler, then the Java garbage collector will
not clean the references. The screen orientation change will create a
new activity and the corresponding Handler.
Imagine doing this a few times - we have a memory leak !!!
Static inner class(es) will never hold an implicit reference to
their
outer class(es).
This is the recommended practice in Android programming.
When the Handler gets a message with
the LAUNCH_MSG command, it starts the
activity_apply_cancel.xml activity.
Similarly, when the Handler gets a
message
with the UPDATE_MSG command, it starts
refreshes the stock ticker list on the screen.
In the onStart() method of the class
StockAlertActivity, we start an
IntentService in the background to
update the stock prices. The IntentService
is defined in the
class StockUpdaterService.
The contents of the java source file StockUpdaterService.java
will look like the one shown in the listing 12.11 below:
From the code Listing 12.11 above, the new and interesting parts are
in
the methods StockUpdaterService(),
onHandleIntent(), and
onDestroy().
From the previous topic on Service in
Part-10,
we know that a
Service can run in the background forever.
What happens when the battery becomes low ??? Do we want to still
continue running, draining the battery further or as a good citizen
shut off the background Service and conserve
battery power ???
How would an Android application know that the battery power is low
??? Enter the Android framework class android.content.BroadcastReceiver.
One can register an implementation of
BroadcastReceiver with the Android system
to receive system events. The Android system broadcasts system
Intent for various events such as
BATTERY_LOW or
CONNECTIVITY_CHANGE, etc. In our case, we
will register a custom implementation of
BroadcastReceiver to listen for
BATTERY_CHANGED system event.
When the battery capacity reaches 25%,
we will shutdown the background service
StockUpdaterService that updates the
stock ticker prices.
From the code Listing 12.11 above, we perform the following steps
at the various methods:
In the constructor StockUpdaterService(),
we create a custom anonymous class that extends BroadcastReceiver
and overrides the
method onReceive(). It is in this
method, we
check the current battery capacity. If it is less than or equal to 25%, we shutdown the service by calling
the method stopSelf()
In the method onHandleIntent(), we
register our custom BroadcastReceiver
by invoking the method registerReceiver()
In the method onDestroy(), we
un-register our custom BroadcastReceiver
by invoking the method unregisterReceiver()
Note that nothing prevents us from boardcasting our own application
events using Intent.
The code to handle the user setting or cancelling price alerts is
defined
in the class StockAlertActivity2. The
contents
of the java source file StockAlertActivity2.java
will look like the one shown in the listing 12.12 below:
Finally, modify the contents of the AndroidManifest.xml
file to look like the one shown in the listing 12.13 below:
The first change to notice is the additional attribute named
android:name for the tag
<application>. This
attribute
has the value of our custom Application
class
StockAlertApplication. When our Android
application starts up, the Android system will create an instance
of this class and is made available to the application via the
method call getApplicationContext() of
the
Context.
The second change to notice will be around permissions. By default,
an Android application is granted no permissions. In order to listen
to and receive the system events related to the device battery, we need
to grant
permissions for BATTERY_STATS. We do
that by
listing the required permissions between the tag
<uses-permission>.
We are now ready to test our DroidStockAlert
application on the virtual Android device we created in
Part-1.
We will create a Run Configuration for DroidStockAlert
as we did in Part-2
for DroidTipCalculator.
Once the run configuration for DroidStockAlert
is ready, we will Run
the application and the application will come to life as shown in the
following figure 12.1 below:
Figure-12.1
Clicking on the Green Plus image on the
stock
symbol IBM will launch the activity
activity_apply_cancel.xml as shown
in the following figure 12.2 below:
Figure-12.2
Key in the desired Low Price numeric value in the input
field at which you want the alert. For this test, let us enter
a value of 200.00 and click on the
Set Alert button as shown in the
following figure 12.3 below:
Figure-12.3
When any type of alert (Low or High) is set, notice the change
in the image to a Red Minus from a
Green Plus as shown in the following
figure 12.4 below:
Figure-12.4
When the price for the stock symbol IBM
reaches 200.00 or below, we are notified
with a notification as shown in the following figure 12.5 below:
Figure-12.5
Drag the notification icon downwards
with the computer mouse (to simulate the swipe down on the device)
and it will reveal the Low Price alert as shown in the following
figure 12.6 below:
Figure-12.6
Now, click on the Red Minus to reset the
Low Price alert and set the High Price alert. Key in the desired High
Price numeric value in the input field at which you want the alert.
For this test, let us enter a value of 216.00
and click on the Set Alert button as shown
in
the following figure 12.7 below:
Figure-12.7
When the price for the stock symbol IBM
reaches 216.00 or above, we are notified
with a notification as shown in the following figure 12.8 below:
Figure-12.8
Drag the notification icon downwards
with the computer mouse (to simulate the swipe down on the device)
and it will reveal the High Price alert as shown in the following
figure 12.9 below:
Figure-12.9
The last part of functionality that needs to be demostrated is the
handling of BATTERY_CHANGED system event.
So how do we control and simulate the battery power capacity of the
Virtual Android Device ???
Well it turns out that we can connect to the Virtual Device at the
network port at which the emulator is running using
telnet and issue commands.
So how do we find the emulator network port to connect to ???
We point out the network port and the fact that the Virtual
Device emulator is running on AC power in
the following figure 12.10 below:
Figure-12.10
From the figure 12.10 above, we determine the emulator port to
connect to be 5554.
Open a terminal and using telnet, issue
the following command:
$ telnet localhost 5554
The terminal window will appear as shown in the following
figure 12.11 below:
Figure-12.11
To turn the AC power off, issue the
power ac off command in the terminal
running the telnet command and then press
the ENTER key.
The terminal window will appear as shown in the following
figure 12.12 below:
Figure-12.12
Now notice the change in the battery status in the Virtual Device
as shown in the following figure 12.13 below:
Figure-12.13
To control the battery charge capacity, issue the
power capacity 40 command in the
terminal
running the telnet command and then press
the ENTER key. This will change the battery
charge to 40%. However, this will not shut
the service StockUpdaterService and the
stock prices will continue to change. Next, issue the
power capacity 25 command in the
terminal
running the telnet command and then press
the ENTER key. Now the service
StockUpdaterService will be shut down
as the battery charge has dropped to 25%.
The terminal window will appear as shown in the following
figure 12.14 below: