StockMIDletpublic class StockMIDlet extends MIDlet The MIDlet application class that we'll run for the Stock Demo. |
Fields Summary |
---|
private static int | OFFSETSince there is no support in MIDP/CLDC for floating point numbers,
and all of the stock data comes in the form ##.### (usually) then we
have to find a way to store the decimals as well as the integer part
of the quote data. Multiplying by the OFFSET will shift the decimal
place far enough over to make the number an integer which we can
store. Every time we retrieve quote data from our
RecordStore we
must make sure that we divide by the OFFSET to correctly display and
use the value we actually want. | Display | displayThe MIDlet 's display object | private Ticker | stockTickerThe Ticker that scrolls along the top of the screen | private List | chooseA List of stocks | private List | viewThe Stock Tracker menu | private List | menuThe Main menu | private List | alertListThe Alert menu | private List | settingsListThe Settings menu | private Form | whatifThe 'What If?' Form upon which we enter our query data | private Form | updatesFormThe Form to display the different update intervals settings | private TextBox | stockSymbolBoxUsed to input a stock symbol | private TextBox | alertPriceBoxUsed to enter the price at which the user wishes to be alerted to the
stocks's value | private TextField | origPurchPriceFieldThe original price the user purchased the stock at, used on the What If?
Form | private TextField | numSharesFieldThe number of shares the users wishes to sell, used on the What If?
Form | private static final Command | BACK_COMMANDBack Command | private static final Command | MAIN_MENU_COMMANDMain Menu Command | private static final Command | DONE_COMMANDDone Command | private static final Command | SET_COMMANDSet Command | private static final Command | EXIT_COMMANDExit Command | private static final Command | CALC_COMMANDCalc Command | private static final Command | ABOUT_COMMANDAbout Command | private ChoiceGroup | updatesChoicesThe radio buttons for the update interval time | private String | currentMenuA textual reference to the current menu that is displayed
onscreen to
allow the StockCommandListener to decide what
action to perform upon
execution of a command | private String | stockSymbolA reference to the stock that has been chosen from a list of stocks
onscreen. Using this we can extract the correct stock's data from
the StockDatabase | private StockDatabase | stocksThe reference to the StockDatabase that stores
the stock data | private AlertDatabase | alertsThe reference to the AlertDatabase that stores the alerts | private String | quoteServerURLThe server from which the quotes are downloaded
NOTE: Currently, only the quote.yahoo.com server is supported | private String | quoteFormatThe format parameter for the quote server to retrieve stock data
NOTE: Currently, only this format is supported | private String | proxyURLThe proxy server that must be negotiated
NOTE: Proxy server is optional and a blank string indicates that
no proxy should be used | private Timer | stockRefreshThe Timer object that refreshes the stock
quotes periodically | private StockRefreshTask | stockRefreshTaskThe TimerTask that the Timer
performs periodically.
It refreshes the stock quotes | private int | refresh_intervalHow often are the stocks' data updated off of the server? |
Constructors Summary |
---|
public StockMIDlet()Default constructor that is called by the application
manager to create a new instance of this MIDlet after
which the MIDlet enters the Paused state. // 1000 = 1 second
|
Methods Summary |
---|
private void | about()Show the about box with copyright info and image
Runtime runtime = Runtime.getRuntime();
/*
StringBuffer props = new StringBuffer();
props.append("\nSystem Properties\n");
props.append("Used Memory = "
+ (runtime.totalMemory() - runtime.freeMemory()) + "\n");
props.append("Garbage collecting . . .\n");
runtime.gc();
long free = runtime.freeMemory();
long total = runtime.totalMemory();
props.append("Used Memory = " + (total - free) + "\n");
props.append("Free Memory = " + free + "\n");
props.append("Total Memory = " + total + "\n");
System.out.println(props.toString());
*/
example.About.showAbout(display);
| private boolean | addNewStock(java.lang.String tkrSymbol)Add the stock to the database
first contact the quote server to get the stock quote
if the stock doesn't not exist, alert the user and return to
the add screen
if the stock exists then add it to our list
if the addition of the stock was successful, return true
otherwise, return false
This is the format returned by the quote.yahoo.com server in
the format
specified in the instance variable:
NAME TIME PRICE CHANGE LOW HIGH OPEN PREV
"SUNW","11:24AM - 79.0625",-3.0625,"26.9375 - 106.75",80.5,82.125
This is what is returned if the stock is not found:
"NAME" ,"N/A - 0.00" ,N/A ,"N/A - N/A" ,N/A ,N/A
try {
// When stocks.search() returns null, the stock doesn't yet exist
if (stocks.search(tkrSymbol) == null) {
try {
stocks.add(getStockQuote(tkrSymbol));
stockTicker.setString(makeTickerString());
} catch (RecordStoreFullException rsf) {
error("Database is full.", 2000);
return false;
} catch (RecordStoreException rs) {
error("Failed to add " + tkrSymbol, 2000);
return false;
} catch (IOException ioe) {
error("Failed to download stock quote for \"" +
tkrSymbol + "\"", 2000);
return false;
} catch (NumberFormatException nfe) {
error("\"" + tkrSymbol +
"\" not found on server, or invalid data "
+ "received from server",
2000);
return false;
}
// The stock already exists so we'll just update it
} else {
try {
stocks.update(tkrSymbol,
getStockQuote(tkrSymbol).getBytes());
stockTicker.setString(makeTickerString());
} catch (RecordStoreFullException rsf) {
error("Database is full.", 2000);
return false;
} catch (RecordStoreException rs) {
error("Failed to update " + tkrSymbol, 2000);
return false;
} catch (IOException ioe) {
error("Failed to download stock quote for "
+ tkrSymbol, 2000);
return false;
}
}
} catch (RecordStoreException rs) {
error("Error accessing database.", 2000);
return false;
}
return true;
| private void | addStock()Show the screen to add a stock
stockSymbolBox.setString("");
display.setCurrent(stockSymbolBox);
currentMenu = "AddStock";
| private void | alertForm(java.lang.String tkrSymbol)Show the form to add an alert
display.setCurrent(alertPriceBox);
currentMenu = "AlertForm";
stockSymbol = tkrSymbol;
| private void | alertMenu(boolean showAlert)Show the alert management menu
display.setCurrent(alertList);
currentMenu = "AlertMenu";
if (showAlert) {
Alert a = new Alert("", "\n\n\n Saved!", null, null);
a.setTimeout(2000);
display.setCurrent(a, alertList);
}
| private void | calc()Calculate the profits in a What If? scenario by the formula:
Profit = (CurrentPrice - OriginalPurchasePrice)
* NumberOfShares
First we retrieve the current price of the stock. Then parse the
original purchase price that the user enters to format it to an
integer. Next, retrieve the number of shares from the form that
the user filled in and then calculate the profits and display a nice
message (with the result) to the user onscreen.
try {
String s = stocks.search(stockSymbol);
int currPrice = Stock.getPrice(s);
int opp = Stock.makeInt(origPurchPriceField.getString());
int numShares =
Integer.valueOf(numSharesField.getString()).intValue();
int profit = ((currPrice - opp) * numShares);
Form answerForm = new Form(Stock.getName(s) +
" " + Stock.getStringPrice(s));
StringBuffer sb =
new StringBuffer().append("Net profit (loss) is ")
.append((profit >= 0) ? "$" : "($")
.append((profit >= 0) ? Stock.convert(profit) :
"-" + Stock.convert(profit))
.append((profit >= 0) ? "" : ")")
.append(" when selling ")
.append(String.valueOf(numShares))
.append(" shares at $")
.append(Stock.convert(currPrice))
.append(" per share.");
answerForm.append(sb.toString());
answerForm.addCommand(BACK_COMMAND);
answerForm.addCommand(MAIN_MENU_COMMAND);
answerForm.setCommandListener(new StockCommandListener());
display.setCurrent(answerForm);
currentMenu = "AnswerForm";
}
catch (Exception e) {
error("Calculation Failed", 2000);
}
| private void | checkAlerts(java.lang.String tkrSymbol)Check the alerts to see if any are registered for tkrSymbol at the
tkrSymbol's current price
try {
int current_price = Stock.getPrice(stocks.search(tkrSymbol));
RecordEnumeration re = alerts.enumerateRecords(tkrSymbol,
current_price);
while (re.hasNextElement()) {
String alert_string = new String(re.nextRecord());
int my_price =
Integer.valueOf(alert_string
.substring(alert_string.indexOf(';")+1,
alert_string.length()))
.intValue();
Alert a = new Alert(tkrSymbol, "", null, AlertType.ALARM);
StringBuffer sb = new StringBuffer()
.append(tkrSymbol)
.append(" has reached your price point of $")
.append(Stock.convert(my_price))
.append(" and currently is trading at $")
.append(Stock.getStringPrice(stocks
.search(tkrSymbol)));
a.setString(sb.toString());
a.setTimeout(Alert.FOREVER);
display.setCurrent(a);
alerts.delete(alert_string);
}
} catch (RecordStoreNotOpenException rsno) {
} catch (RecordStoreException rs) {
}
| private void | chooseStock(boolean reload, java.lang.String menuType, int type, boolean prices)Show the list of stocks to pick from and set the menu type to indicate
to the Listener what to do with the list choice
if (reload) {
choose = new List("Choose Stocks", type);
choose.setTicker(stockTicker);
choose.addCommand(BACK_COMMAND);
if (menuType.equals("RemoveStock")) {
choose.addCommand(DONE_COMMAND);
} else if (menuType.equals("WhatChoose") ||
menuType.equals("AddAlert")) {
choose.addCommand(MAIN_MENU_COMMAND);
}
choose.setCommandListener(new StockCommandListener());
try {
RecordEnumeration re = stocks.enumerateRecords();
while (re.hasNextElement()) {
String theStock = new String(re.nextRecord());
if (prices) {
choose.append(Stock.getName(theStock)
+ " @ "
+ Stock.getStringPrice(theStock), null);
} else {
choose.append(Stock.getName(theStock), null);
}
}
} catch (RecordStoreNotOpenException rsno) {
} catch (RecordStoreException rs) {
}
}
display.setCurrent(choose);
currentMenu = menuType;
| private void | deleteStock(java.lang.String tkrSymbol)Remove the stock from the StockDatabase
try {
stocks.delete(tkrSymbol);
alerts.removeUselessAlerts(tkrSymbol);
stockTicker.setString(makeTickerString());
} catch (RecordStoreException rs) {
error("Failed to delete " + tkrSymbol, 2000);
}
| public void | destroyApp(boolean unconditional)When the application management software has determined that the
MIDlet is no longer needed, or perhaps needs to make room
for a higher priority application in memory, is signals
the MIDlet
that it is a candidate to be destroyed by invoking the
destroyApp(boolean) method. In this case, we need to destroy
the RecordEnumeration s so that we don't waste their memory
and close the open RecordStore s. If the
RecordStore s
are empty, then we do not need them to be stored as
they will be recreated
on the next invokation of the MIDlet so
we should delete them.
At the end of our clean up, we must call notifyDestroyed
which will inform the application management software that we are done
cleaning up and have finished our execution and can
now be safely terminated and
enters the Destroyed state.
// If there is no criteria that will keep us from terminating
if (unconditional) {
synchronized (this) {
if (display == null) {
// If display == null, we are not initialized and
// we have nothing to destroy
return;
}
stockRefresh.cancel();
try {
stocks.close();
alerts.close();
RecordStore settings =
RecordStore.openRecordStore("Settings", true);
try {
settings.setRecord(1,
String.valueOf(refresh_interval)
.getBytes(),
0, String.valueOf(refresh_interval)
.length());
// First time writing to the settings file
} catch (RecordStoreException rse) {
settings.addRecord(String.valueOf(refresh_interval)
.getBytes(),
0, String.valueOf(refresh_interval)
.length());
}
settings.closeRecordStore();
} catch (Exception e) {
// Ignore exception there is no place to report it
}
notifyDestroyed();
// Something might make us not want to exit so check it
// here before terminating
} // synchronized
} else {
}
| private void | displayStock(java.lang.String tkrSymbol)Display the stock selected on the View menu with all its
attributes shown (ie. Name, Price, etc.)
try {
String theStock = stocks.search(tkrSymbol);
Form stockInfo = new Form(Stock.getName(theStock));
stockInfo.setTicker(stockTicker);
StringBuffer sb = new StringBuffer()
.append("Last Trade:\n ")
.append(Stock.getTime(theStock))
.append("\n ")
.append(Stock.getStringPrice(theStock))
.append("\nChange: ")
.append(Stock.getStringChange(theStock))
.append("\nHigh: ")
.append(Stock.getStringHigh(theStock))
.append("\nLow: ")
.append(Stock.getStringLow(theStock))
.append("\nOpen: ")
.append(Stock.getStringOpen(theStock))
.append("\nPrev: ")
.append(Stock.getStringPrevious(theStock));
stockInfo.append(sb.toString());
stockInfo.addCommand(BACK_COMMAND);
stockInfo.addCommand(MAIN_MENU_COMMAND);
stockInfo.setCommandListener(new StockCommandListener());
display.setCurrent(stockInfo);
currentMenu = "stockInfo";
} catch (RecordStoreNotOpenException rsno) {
error("Could not display stock. ", 2000);
} catch (RecordStoreException rs) {
error("Could not display stock. ", 2000);
} catch (NullPointerException npe) {
error("Could not display stock. ", 2000);
}
| private void | error(java.lang.String message, int time)Display a message onscreen for a specified period of time
if (!(display.getCurrent() instanceof Alert)) {
Alert a = new Alert("Error", message, null, AlertType.ERROR);
a.setTimeout(time);
display.setCurrent(a, display.getCurrent());
}
| private java.lang.String | getStockQuote(java.lang.String tkrSymbol)This method actually contacts the server, downloads and returns
the stock quote.
NOTE: If PROXY support is added to HttpConnection that switch the code
over to that as it will be pure MIDP instead of this hack
String quoteURL = quoteServerURL + tkrSymbol + quoteFormat;
StreamConnection c = (StreamConnection)
Connector.open(quoteURL, Connector.READ_WRITE);
InputStream is = c.openInputStream();
int ch;
StringBuffer sb = new StringBuffer();
while ((ch = is.read()) != -1) {
sb.append((char)ch);
}
Stock.parse(sb.toString());
is.close();
c.close();
return sb.toString();
| private void | mainMenu()Display the main menu of the program
display.setCurrent(menu);
currentMenu = "Main";
| private java.lang.String | makeTickerString()Generate a string (which concatenates all of the stock names and
prices) which will be used for the Ticker .
// the ticker tape string
StringBuffer tickerTape = new StringBuffer();
try {
RecordEnumeration re = stocks.enumerateRecords();
while (re.hasNextElement()) {
String theStock = new String(re.nextRecord());
tickerTape.append(Stock.getName(theStock))
.append(" @ ")
.append(Stock.getStringPrice(theStock))
.append(" ");
}
} catch (Exception e) {
return "Error Accessing Database";
}
return tickerTape.toString();
| public void | pauseApp()This method is invoked by the application management software when
the MIDlet no longer needs to be active. It is a stop
signal for the MIDlet upon which the MIDlet
should release any resources which can be re-acquired through the
startApp method which will be called upon re-activation of
the MIDlet . The MIDlet enters
the Paused
state upon completion of this method.
synchronized (this) {
// free memory used by these objects
display = null;
choose = null;
view = null;
menu = null;
alertList = null;
settingsList = null;
whatif = null;
updatesForm = null;
stockSymbolBox = null;
alertPriceBox = null;
origPurchPriceField = null;
numSharesField = null;
stockTicker = null;
stockRefresh.cancel();
stockRefresh = null;
stockRefreshTask = null;
try {
stocks.close();
stocks = null;
alerts.close();
alerts = null;
} catch (Exception e) {}
} // synchronized
| private void | removeAlert(java.lang.String choose_data)Remove the alert from our RecordStore referenced by index
try {
// Separate the symbol and price from the data
String symbol = choose_data.substring(0,
choose_data.indexOf('@")-1);
int sPrice = Stock.makeInt(
choose_data.substring(choose_data.indexOf('@")+3,
choose_data.length()));
System.out.println("Remove Alert: " + symbol + ";" + sPrice);
// Remove the alert
alerts.delete(symbol + ";" + sPrice);
} catch (Exception e) {
error("Failed to remove alert", 2000);
}
| private void | setAlert(java.lang.String Sprice)Set an alert for the selected stock at the specified price
try {
alerts.add((new StringBuffer()
.append(Stock.getName(stocks.search(stockSymbol)))
.append(';")
.append(Stock.makeInt(Sprice))).toString());
} catch (Exception e) {
error("Failed to add alert", 2000);
}
| private void | settings(boolean showAlert)Show the settings menu
display.setCurrent(settingsList);
currentMenu = "Settings";
if (showAlert) {
Alert a = new Alert("", "\n\n\n Saved!", null, null);
a.setTimeout(1500);
display.setCurrent(a, settingsList);
}
| public void | startApp()This method is invoked when the MIDlet
is ready to run and
starts/resumes execution after being in the Paused
state. The
MIDlet acquires any resources it needs,
enters the
Active state and begins to perform its service,
which in this
case means it displays the main menu on the screen.
The method proceeds like so:
open the StockDatabase and the
AlertDatabase
read the settings data from the settings
RecordStore
create the string to be scrolled across the Ticker on
the top of the screens and instantiate the Ticker object using that
string. That string will be constructed of the names and prices of
the stocks in our database
get and store the MIDlet 's
Display object
create and show the main menu
instantiate the TimerTask and Timer and
associate the two setting the refresh interval to the value of
the refresh_interval variable
synchronized (this) {
display = Display.getDisplay(this);
// Open the Stocks file
stocks = new StockDatabase();
try {
stocks.open("Stocks");
} catch (Exception e) {
try {
stocks.cleanUp("Stocks");
} catch (Exception e2) {}
}
// Open the Alerts file
alerts = new AlertDatabase();
try {
alerts.open("Alerts");
} catch (Exception e) {
try {
alerts.cleanUp("Alerts");
} catch (Exception e2) {}
}
// Open the Settings file
try {
RecordStore settings =
RecordStore.openRecordStore("Settings", true);
refresh_interval =
Integer.valueOf(new String(settings.getRecord(1)))
.intValue();
settings.closeRecordStore();
// No settings file existed
} catch (Exception e) {
refresh_interval = 900000;
}
// Make the ticker
stockTicker = new Ticker(makeTickerString());
// Create all the menus and forms
origPurchPriceField =
new TextField("Original Purchase Price:", "", 5,
TextField.NUMERIC);
numSharesField =
new TextField("Number Of Shares:", "", 9,
TextField.NUMERIC);
menu = new List("Stock Menu", Choice.IMPLICIT);
menu.append("Stock Tracker", null);
menu.append("What If?", null);
menu.append("Alerts", null);
menu.append("Settings", null);
menu.addCommand(EXIT_COMMAND);
menu.addCommand(ABOUT_COMMAND);
menu.setCommandListener(new StockCommandListener());
menu.setTicker(stockTicker);
whatif = new Form("What If?");
whatif.setTicker(stockTicker);
whatif.append(origPurchPriceField);
whatif.append(numSharesField);
whatif.addCommand(BACK_COMMAND);
whatif.addCommand(CALC_COMMAND);
whatif.setCommandListener(new StockCommandListener());
alertList = new List("Alert Menu", Choice.IMPLICIT);
alertList.setTicker(stockTicker);
alertList.append("Add", null);
alertList.append("Remove", null);
alertList.addCommand(BACK_COMMAND);
alertList.setCommandListener(new StockCommandListener());
settingsList = new List("Settings", Choice.IMPLICIT);
settingsList.setTicker(stockTicker);
settingsList.append("Updates", null);
settingsList.append("Add Stock", null);
settingsList.append("Remove Stock", null);
settingsList.addCommand(BACK_COMMAND);
settingsList.setCommandListener(new StockCommandListener());
alertPriceBox = new TextBox("Alert me when stock reaches:",
"", 9, TextField.NUMERIC);
alertPriceBox.setTicker(stockTicker);
alertPriceBox.addCommand(DONE_COMMAND);
alertPriceBox.addCommand(BACK_COMMAND);
alertPriceBox.setCommandListener(new StockCommandListener());
updatesForm = new Form("Updates");
updatesChoices = new ChoiceGroup("Update Interval:",
Choice.EXCLUSIVE);
updatesChoices.append("Continuous", null); // will be 30 seconds
updatesChoices.append("15 minutes", null); // default for JavaONE
updatesChoices.append("30 minutes", null);
updatesChoices.append("1 hour", null);
updatesChoices.append("3 hours", null);
switch (refresh_interval) {
case 30000: updatesChoices.setSelectedIndex(0, true);
break;
case 1800000: updatesChoices.setSelectedIndex(2, true);
break;
case 3600000: updatesChoices.setSelectedIndex(3, true);
break;
case 10800000: updatesChoices.setSelectedIndex(4, true);
break;
case 900000:
default: updatesChoices.setSelectedIndex(1, true);
break;
}
updatesForm.setTicker(stockTicker);
updatesForm.append(updatesChoices);
updatesForm.addCommand(BACK_COMMAND);
updatesForm.addCommand(DONE_COMMAND);
updatesForm.setCommandListener(new StockCommandListener());
stockSymbolBox = new TextBox("Enter a Stock Symbol:",
"", 5, TextField.ANY);
stockSymbolBox.setTicker(stockTicker);
stockSymbolBox.addCommand(DONE_COMMAND);
stockSymbolBox.addCommand(BACK_COMMAND);
stockSymbolBox.setCommandListener(new StockCommandListener());
mainMenu();
// Set up and start the timer to refresh the stock quotes
stockRefreshTask = new StockRefreshTask();
stockRefresh = new Timer();
stockRefresh.schedule(stockRefreshTask, 0, refresh_interval);
// FOR JavaONE -- ADD IN A COUPLE DEFAULT STOCKS
/*
addNewStock("MOT");
addNewStock("SUNW");
addNewStock("NOK");
addNewStock("IBM");
addNewStock("AOL");
addNewStock("MSFT");
addNewStock("GM");
addNewStock("FORD");
addNewStock("ORCL");
addNewStock("SEEK");
addNewStock("AT&T");
addNewStock("LU");
addNewStock("HON");
addNewStock("CORL");
addNewStock("NOR");
*/
} // synchronized
| private void | updates()Show the updates choices
display.setCurrent(updatesForm);
currentMenu = "Updates";
| private void | viewAlerts()Show a list of all active alerts
choose = new List("Current Alerts", Choice.MULTIPLE);
choose.setTicker(stockTicker);
try {
// Get all the Alert records
RecordEnumeration re = alerts.enumerateRecords("", 0);
while (re.hasNextElement()) {
String a = new String(re.nextRecord());
String price =
Stock.convert(Integer.valueOf(a.substring(a.indexOf(';")+1,
a.length())).intValue());
choose.append(a.substring(0, a.indexOf(';")) + " @ $"
+ price, null);
}
} catch (Exception e) {
error("Error reading alerts", 2500);
}
choose.addCommand(BACK_COMMAND);
choose.addCommand(DONE_COMMAND);
choose.setCommandListener(new StockCommandListener());
display.setCurrent(choose);
currentMenu = "RemoveAlert";
| private void | whatIfForm(java.lang.String tkrSymbol)Show the What If? form to investigate a hypothetical stock deal
display.setCurrent(whatif);
currentMenu = "WhatIfForm";
stockSymbol = tkrSymbol;
|
|