Notice: This is a static archive of the Mobile Processing website, as the project is no longer significantly active. All forms and the Discourse boards are no longer functional, and the mobile website at http://wapmp.at/ is no longer available. Please visit the Mobile Processing Google Group for any remaining discussions and the Processing website for the latest news about Processing.
Index

Bluescan




Code:
bluescan.pde

Download:
bluescan.jad
bluescan.jar

Obfuscated:
bluescan.jad
bluescan.jar
    
// Bluescan
// by Francis Li
// http://www.francisli.com/
//
// inspired by
// 
// Jabberwocky
// by Intel Research Berkeley
// http://www.urban-atmospheres.net/Jabberwocky/index.htm
//
// Posted January 2, 2006
//
// Bluescan discovers nearby bluetooth devices and represents them on the screen
// as a cellphone icon (even though they may be PCs or other devices).  As they are discovered,
// they appear at the top of the screen, then slowly drift down until they disappear at the bottom.
// Newly discovered devices are red, devices you've encountered before are green.  The more times
// you encounter a device, the bigger the icon will be.
//
import processing.bluetooth.*;

final String SOFTKEY_DISCOVER = "Discover";
final String SOFTKEY_CANCEL   = "Cancel";

PFont font;
Bluetooth bt;
boolean discovering;
CellphoneIcon[] icons;
int numIcons;
int selected;

Record[] records;
int numRecords;

int prev;

void setup() {
  //// get and set the default system font
  font = loadFont();
  textFont(font);

  //// instantiate a new bluetooth object
  bt = new Bluetooth(this);
  
  //// initialize the records array to a default size
  records = new Record[8];
  numRecords = 0;
  
  //// initialize the icons array to a default size
  icons = new CellphoneIcon[8];
  numIcons = 0;
  
  //// set up a command softkey to start discovery process
  softkey(SOFTKEY_DISCOVER);
  
  //// cap framerate
  framerate(20);
  
  //// align everything center
  textAlign(CENTER);
  
  //// load previously saved records
  String[] savedRecords = loadStrings("records.txt");
  int numSaved = savedRecords.length;
  Record r;
  for (int i = 0; i < numSaved; i++) {
    r = new Record(savedRecords[i]);
    addRecord(r);
  }
}

void destroy() {
  //// before the midlet is destroyed, save records
  String[] savedRecords = new String[numRecords];
  for (int i = 0; i < numRecords; i++) {
    savedRecords[i] = records[i].toString();
  }
  saveStrings("records.txt", savedRecords);
}

void draw() {
  //// set a nice blue sea background
  background(0xff0099ff);
  
  //// update frame timing
  int current = millis();
  int elapsed = current - prev;
  prev = current;
  
  //// move icons based on elapsed time
  for (int i = 0; i < numIcons; i++) {
    icons[i].move(elapsed);
    if (icons[i].ty >= (height - icons[i].height() / 2)) {
      icons[i].tscale_fp = 0;
    }
  }
  
  //// if icons have reached the bottom, remove them from the array
  for (int i = numIcons - 1; i >= 0; i--) {
    if (icons[i].scale_fp == 0) {
      icons[i] = null;
      arraycopy(icons, i + 1, icons, i, numIcons - i - 1);
      numIcons--;
      selected--;
    }
  }
  if (selected < 0) {
    selected = 0;
  }
  
  //// draw the icons
  for (int i = 0; i < numIcons; i++) {
    icons[i].draw();    
  }
  
  //// draw name of current selection
  if (selected < numIcons) {
    Record r = icons[selected].r;
    //// draw the name under the icon
    if (r.name == Device.UNKNOWN) {
      //// if the record name is unknown, check the device to see if it has
      //// been updated in the background discovery process
      r.name = r.device.name;
    }
    int width = textWidth(r.name);
    stroke(0);
    fill(255);
    rect(icons[selected].x - width / 2 - 2, icons[selected].y + icons[selected].height() / 2, width + 4, font.height + 4);
    fill(0);
    text(r.name, icons[selected].x, icons[selected].y + icons[selected].height() / 2 + font.baseline + 2);
  }  
  
  //// draw the current number of visible devices
  text(str(numIcons), width / 2, height);
}

void keyPressed() {
  if (key == '5') {
    //// testing animation on the emulator, just puts a dummy device/icon on the screen
    Record r = new Record();
    r.name = "Test";
    r.count = random(5);
    addIcon(r);
  } else if (keyCode == LEFT) {
    //// update selection
    if (numIcons > 0) {
      selected = (selected - 1 + numIcons) % numIcons;
    }
  } else if (keyCode == RIGHT) {
    //// update selection
    if (numIcons > 0) {
      selected = (selected + 1) % numIcons;
    }
  }
}

void softkeyPressed(String label) {
  if (label.equals(SOFTKEY_DISCOVER)) {
    //// start discovery process
    discovering = true;  
    bt.discover();
    softkey(SOFTKEY_CANCEL);
  } else if (label.equals(SOFTKEY_CANCEL)) {
    //// cancel discovery process
    discovering = false;
    bt.cancel();
    softkey(SOFTKEY_DISCOVER);
  }
}

void libraryEvent(Object library, int event, Object data) {
  if (library == bt) {
    switch (event) {
      case Bluetooth.EVENT_DISCOVER_DEVICE:
        //// new device discovered!
        Device d = (Device) data;
        Record r = null;
        String address = d.address;
        boolean found = false;
        //// first, look for it in existing records list
        for (int i = 0; i < numRecords; i++) {
          if (records[i].address.equals(address)) {
            /// found! update count and last encounter time
            r = records[i];
            r.count++;
            //// a bit of a hack to get an absolute system time (unlike millis() which is relative to app start)
            r.last = (int) (System.currentTimeMillis() / 1000);
            r.device = d;
            found = true;
            break;
          }
        }
        if (found) {
          found = false;
          //// see if it is currently on screen
          for (int i = 0; i < numIcons; i++) {
            if (icons[i].r == r) {
              icons[i].tx = icons[i].height() / 2;
              found = true;
              break;
            }
          }
          if (!found) {
            //// put it on screen!
            addIcon(r);
          }
        } else {
          //// not found, so create a new record
          r = new Record();
          r.name = d.name;
          r.address = d.address;
          r.count = 1;
          r.last = (int) (System.currentTimeMillis() / 1000);
          r.device = d;
          addRecord(r);
          addIcon(r);
        }
        break;
      case Bluetooth.EVENT_DISCOVER_DEVICE_COMPLETED:
        //// done, reset command key and state
        discovering = false;
        softkey(SOFTKEY_DISCOVER);
        break;
    }
  }
}

//// adds a record to the array, expanding it if necessary
void addRecord(Record r) {
  records[numRecords] = r;
  numRecords++;
  
  if (numRecords == records.length) {
    Record[] oldRecords = records;
    records = new Record[2 * numRecords];
    arraycopy(oldRecords, 0, records, 0, numRecords);
  }
}

//// puts an icon on the screen
void addIcon(Record r) {
  CellphoneIcon i = new CellphoneIcon();
  i.r = r;
  i.tscale_fp = (3 + r.count) * ONE / 4;
  i.x = i.tx = 10 + random(width - 20);
  i.y = i.ty = i.height() / 2;
  
  icons[numIcons] = i;
  numIcons++;
  
  if (numIcons == icons.length) {
    CellphoneIcon[] oldIcons = icons;
    icons = new CellphoneIcon[2 * numIcons];
    arraycopy(oldIcons, 0, icons, 0, numIcons);
  }
}

//// a data structure to store device info, and to read/write it from strings
class Record {
  String name;
  String address;
  int count;
  int last;
  Device device;
  
  public Record() {
  }
  
  public Record(String savedRecord) {
    String[] parts = split(savedRecord, ",");
    name = parts[0];
    address = parts[1];
    count = int(parts[2]);
    last = int(parts[3]);
  }
  
  String toString() {
    return name + "," + address + "," + count + "," + last;
  }
}

//// a data structure to store icon info, as well as render it to screen
class CellphoneIcon {
  /** Reference to device history record object */
  Record r;
  /** the actual x, y location on the screen */
  int x, y;
  /** the actual scale of the icon on the screen */
  int scale_fp;
  /** accumulated distance */
  int dx_fp, dy_fp;
  /** the target x, y, and scale */
  int tx, ty, tscale_fp;
  /** accumulated distance of target motion */  
  int dty_fp;
  /** x, y velocities */
  int vx_fp, vy_fp;
  
  int width() {
    return fptoi(tscale_fp * 12);
  }
  
  int height() {
    return fptoi(tscale_fp * 30);
  }
  
  void move(int elapsed) {
    //// quickly reach target scale
    int dscale_fp = (tscale_fp - scale_fp) / 2;
    if (dscale_fp == 0) {
      scale_fp = tscale_fp;
    } else {
      scale_fp += dscale_fp;
    }    
    
    //// target y moves downward at constant velocity
    dty_fp += elapsed * itofp(8) / 1000;
    if (dty_fp > ONE) {
      ty += fptoi(floor(dty_fp));
      dty_fp = dty_fp - floor(dty_fp);
    }    
    //// target x randomly shifts
    tx += random(-2, 2);
    if (tx < 10) {
      tx = 10;
    } else if (tx > (width - 10)) {
      tx = width - 10;
    }
    
    //// icon accelerates towards target
    vy_fp += (ty - y) * itofp(2) * elapsed / 1000;      
    if (vy_fp > itofp(16)) {
      vy_fp = itofp(16);
    } else if (vy_fp < -itofp(16)) {
      vy_fp = -itofp(16);
    }
    dy_fp += elapsed * vy_fp / 1000;
    if ((dy_fp > ONE) || (dy_fp < -ONE)) {
      y += fptoi(floor(dy_fp));
      dy_fp = dy_fp - floor(dy_fp);
    }
    
    vx_fp += (tx - x) * itofp(2) * elapsed / 1000;
    if (vx_fp > itofp(16)) {
      vx_fp = itofp(16);
    } else if (vx_fp < -itofp(16)) {
      vx_fp = -itofp(16);
    }
    dx_fp += elapsed * vx_fp / 1000;
    if ((dx_fp > ONE) || (dx_fp < -ONE)) {
      x += fptoi(floor(dx_fp));
      dx_fp = dx_fp - floor(dx_fp);
    }
  }
  
  void draw() {
    noStroke();
    if (r.count > 1) {
      fill(0xff00ff00);
    } else {
      fill(0xffff0000);
    }
    //// antenna
    rect(x - fptoi(scale_fp * 12) / 2 + fptoi(scale_fp * 12) - fptoi(scale_fp * 3), y - fptoi(scale_fp * 25) / 2 - fptoi(scale_fp * 5), fptoi(scale_fp * 3), fptoi(scale_fp * 5));    
    //// body
    rect(x - fptoi(scale_fp * 12) / 2, y - fptoi(scale_fp * 25) / 2, fptoi(scale_fp * 12), fptoi(scale_fp * 25));
    //// screen
    fill(230);
    rect(x - fptoi(scale_fp * 12) / 2 + fptoi(scale_fp * 2), y - fptoi(scale_fp * 25) / 2 + fptoi(scale_fp * 2), fptoi(scale_fp * 12) - 2 * fptoi(scale_fp * 2), fptoi(scale_fp * 8));
  }
}