Chord Dialer

Code sample

Here is the source code for the FindShapeMidlet class.
Some details have been obscured for security purposes.
package com.sc.chordbook;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;
import javax.microedition.io.*;
import java.io.*;
import java.util.Vector;
import java.util.Timer;
import java.util.TimerTask;

public class FindShapeMidlet extends MIDlet implements CommandListener
{
  private Display mDisplay;
  private Form mRootForm, mTypeForm, mProgressForm;
  private ChordViewCanvas mChordViewCanvas;
  private ChoiceGroup mRootChoice, mTypeChoice;
  private StringItem mProgressString;
  private Gauge mProgressGauge;
  private Command mNextCommand, mBackCommand;
  private Command mFindCommand, mMoreCommand;
  private Command mExitCommand;
  private String mRoot, mType;        // user's search parameters
  private Vector mChords;             // returned chord data
  private int mProgressValue = 0;

  public FindShapeMidlet()
  {
    // initialise the chords vector
    mChords = new Vector();

    // initialise forms
    // - root form
    mRootForm = new Form("Chord root");
    String[] roots = {"A", "Bb", "B", "C", "C#", "D", "Eb", "E", "F", "F#", "G", "Ab"};
    mRootChoice = new ChoiceGroup(null, Choice.EXCLUSIVE, roots, null);
    mRootForm.append(mRootChoice);

    // - type form
    mTypeForm = new Form("Chord type");
    String[] types = {"maj", "m", "7", "maj7", "m7", "6", "maj6", "m6", "9", "maj9",
                      "m9", "6/9", "sus4", "7sus4", "9sus4", "dim", "aug", "aug7",
                      "aug9", "m11", "13", "maj13", "min13", "7b5", "m7b5", "9b5"};
    mTypeChoice = new ChoiceGroup(null, Choice.EXCLUSIVE, types, null);
    mTypeForm.append(mTypeChoice);

    // - progress form
    mProgressForm = new Form("Fetching chord...");
    mProgressGauge = new Gauge(null, false, 10, 0);
    mProgressString = new StringItem(null, null);
    mProgressForm.append(mProgressGauge);
    mProgressForm.append(mProgressString);

    // initialise canvases
    // - chord view canvas (shows name results)
    mChordViewCanvas = new ChordViewCanvas();

    // - create commands
    mNextCommand = new Command("Next", Command.SCREEN, 0);
    mBackCommand = new Command("Back", Command.BACK, 0);
    mFindCommand = new Command("Find", Command.SCREEN, 0);
    mMoreCommand = new Command("More", Command.SCREEN, 0);
    mExitCommand = new Command("Exit", Command.EXIT, 0);

    // - add commands and handlers to forms
    mRootForm.addCommand(mNextCommand);
    mRootForm.addCommand(mExitCommand);
    mRootForm.setCommandListener(this);
    mTypeForm.addCommand(mBackCommand);
    mTypeForm.addCommand(mFindCommand);
    mTypeForm.setCommandListener(this);
    mProgressForm.addCommand(mBackCommand);
    mProgressForm.setCommandListener(this);

    // - add commands and handlers to canvases
    mChordViewCanvas.addCommand(mBackCommand);
    mChordViewCanvas.addCommand(mMoreCommand);
    mChordViewCanvas.setCommandListener(this);
  }

  public void startApp()
  {
    // grab the display
    mDisplay = Display.getDisplay(this);

    // tell the canvas how many colours it can use (if any)
    mChordViewCanvas.setUseColour(mDisplay.isColor());
    mChordViewCanvas.setNumColours(mDisplay.numColors());

    // show the opening screen
    Display.getDisplay(this).setCurrent(mRootForm);
  }

  public void pauseApp()
  {
  }

  public void destroyApp(boolean unconditional)
  {
  }

  public void commandAction(Command c, Displayable s)
  {
    if (c == mNextCommand)
    {
      // the user pressed Next (on the Root form)
      // grab the selected root...
      mRoot = mRootChoice.getString(mRootChoice.getSelectedIndex());

      // and move on
      mDisplay.setCurrent(mTypeForm);
    }
    else
    if (c == mBackCommand)
    {
      // the user pressed Back (on several forms)
      // - go back to the root form (new search)
      mDisplay.setCurrent(mRootForm);
    }
    else
    if (c == mFindCommand)
    {
      // the user pressed Find (on the Type form)
      // grab the selected type...
      mType = mTypeChoice.getString(mTypeChoice.getSelectedIndex());

      // display the progress form...
      mDisplay.setCurrent(mProgressForm);

      // and fetch the chord shape!
      Fetcher f = new Fetcher(getAppProperty("xxx"), mRoot, mType);
      f.start();
    }
    else
    if (c == mMoreCommand)
    {
      // the user pressed More (on the chord canvas)
      // - display the next chord inversion
      mChordViewCanvas.nextChord();
      mChordViewCanvas.repaint();
    }
    else
    if (c == mExitCommand)
    {
      // the user pressed Exit (anywhere)
      // - exit the app
      notifyDestroyed();
    }
  }

  private class Fetcher extends Thread
  {
    private StringBuffer url;

    public Fetcher(String baseURL, String rootValue, String typeValue)
    {
      // empty the chords vector
      mChords.removeAllElements();

      // encode '#' (sharp) characters as 's'
      rootValue = rootValue.replace('#', 's');

      // build the gateway server url
      url = new StringBuffer();
      url.append(baseURL);
      url.append("?root=");
      url.append(rootValue);
      url.append("&type=");
      url.append(typeValue);

      System.out.println("url = " + url.toString());
    }

    public void run()
    {
      HttpConnection c = null;
      InputStream in = null;

      // start spinning the progress gauge
      Timer timer = new Timer();
      timer.schedule(new ProgressSpinner(), 0, 100);

      // fetch and display the desired chord data
      try
      {
        // open a connection to the chordbook gate servlet
        mProgressString.setText("Sending...");
        c = (HttpConnection)Connector.open(url.toString());

        // send the request
        mProgressString.setText("Waiting...");
        in = c.openInputStream();
        if (c.getResponseCode() != HttpConnection.HTTP_OK)
        {
          System.out.println("HTTP error: " + c.getResponseCode());
        }
        else
        {
          // read the response
          // - get the number of chords returned
          int numChords = Integer.parseInt(c.getHeaderField("numChords"));

          // - read the actual chord data
          for (int i = 0; i < numChords; i++)
          {
            // - read the chord position
            byte pos = (byte)in.read();

            // - read the chord shape
            byte[] frets = new byte[Settings.NUM_STRINGS];
            in.read(frets);

            // - read the chord fingering
            byte[] fingers = new byte[Settings.NUM_STRINGS];
            in.read(fingers);

            // store the chord data in chordbox form
            ChordBox chord = new ChordBox(mRoot + mType, pos, frets, fingers);
            mChords.addElement(chord);
          }

          // display the response...
          if (mChords.size() == 0)
          {
            // no matching chords were found
            // - alert the user
            String msg = "sorry, no match for " + mRoot + mType;
            Alert results = new Alert("Search Result", msg, null, null);
            mDisplay.setCurrent(results, mRootForm);
          }
          else
          {
            // we have chords
            // - display them
            mChordViewCanvas.reset();
            mChordViewCanvas.setChords(mChords);
            mDisplay.setCurrent(mChordViewCanvas);
          }
        }
      }
      catch (IOException ioe)
      {
        String msg = "Servlet error: " + ioe;
        Alert error = new Alert("Error", msg, null, null);
        mDisplay.setCurrent(error, mRootForm);
      }
      finally
      {
        // close input stream and connection
        try
        {
          in.close();
          c.close();
        }
        catch (IOException ioe) {}
      }
    }
  }

  private class ProgressSpinner extends TimerTask
  {
    public void run()
    {
      // move the progress gauge forward by one unit
      mProgressValue++;
      mProgressValue %= 11;
      mProgressGauge.setValue(mProgressValue);
    }
  }
}