package com.oreilly.demo.android.pa.uidemo;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.view.ContextMenu;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
import android.widget.Button;
import android.widget.EditText;
import com.oreilly.demo.android.pa.uidemo.model.Dot;
import com.oreilly.demo.android.pa.uidemo.model.Dots;
import com.oreilly.demo.android.pa.uidemo.view.DotView;
/**
* Android UI demo program
*/
public class TouchMe extends Activity {
/** Dot diameter */
public static final int DOT_DIAMETER = 6;
/** Listen for taps. */
private static final class TrackingTouchListener
implements View.OnTouchListener
{
private final Dots mDots;
private List<Integer> tracks = new ArrayList<Integer>();
TrackingTouchListener(Dots dots) { mDots = dots; }
@Override public boolean onTouch(View v, MotionEvent evt) {
int n;
int idx;
int action = evt.getAction();
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_POINTER_DOWN:
idx = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
tracks.add(Integer.valueOf(evt.getPointerId(idx)));
break;
case MotionEvent.ACTION_POINTER_UP:
idx = (action & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
tracks.remove(Integer.valueOf(evt.getPointerId(idx)));
break;
case MotionEvent.ACTION_MOVE:
n = evt.getHistorySize();
for (Integer i: tracks) {
idx = evt.findPointerIndex(i.intValue());
for (int j = 0; j < n; j++) {
addDot(
mDots,
evt.getHistoricalX(idx, j),
evt.getHistoricalY(idx, j),
evt.getHistoricalPressure(idx, j),
evt.getHistoricalSize(idx, j));
}
}
break;
default:
return false;
}
for (Integer i: tracks) {
idx = evt.findPointerIndex(i.intValue());
addDot(
mDots,
evt.getX(idx),
evt.getY(idx),
evt.getPressure(idx),
evt.getSize(idx));
}
return true;
}
private void addDot(Dots dots, float x, float y, float p, float s) {
dots.addDot(
x,
y,
Color.CYAN,
(int) ((p + 0.5) * (s + 0.5) * DOT_DIAMETER));
}
}
/** Generate new dots, one per second. */
private final class DotGenerator implements Runnable {
final Dots dots;
final DotView view;
final int color;
private final Handler hdlr = new Handler();
private final Runnable makeDots = new Runnable() {
@Override public void run() { makeDot(dots, view, color); }
};
private volatile boolean done;
DotGenerator(Dots dots, DotView view, int color) {
this.dots = dots;
this.view = view;
this.color = color;
}
public void done() { done = true; }
@Override
public void run() {
while (!done) {
hdlr.post(makeDots);
try { Thread.sleep(5000); }
catch (InterruptedException e) { }
}
}
}
private final Random rand = new Random();
/** The application model */
final Dots dotModel = new Dots();
/** The application view */
DotView dotView;
/** The dot generator */
DotGenerator dotGenerator;
/** Called when the activity is first created. */
@Override public void onCreate(Bundle state) {
super.onCreate(state);
// install the view
setContentView(R.layout.main);
// find the dots view
dotView = (DotView) findViewById(R.id.dots);
dotView.setDots(dotModel);
dotView.setOnCreateContextMenuListener(this);
dotView.setOnTouchListener(new TrackingTouchListener(dotModel));
dotView.setOnKeyListener(new OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (KeyEvent.ACTION_DOWN != event.getAction()) {
return false;
}
int color;
switch (keyCode) {
case KeyEvent.KEYCODE_SPACE:
color = Color.MAGENTA;
break;
case KeyEvent.KEYCODE_ENTER:
color = Color.BLUE;
break;
default:
return false;
}
makeDot(dotModel, dotView, color);
return true;
} });
dotView.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus && (null != dotGenerator)) {
dotGenerator.done();
dotGenerator = null;
}
else if (hasFocus && (null == dotGenerator)) {
dotGenerator
= new DotGenerator(dotModel, dotView, Color.BLACK);
new Thread(dotGenerator).start();
}
} });
// wire up the controller
((Button) findViewById(R.id.button1)).setOnClickListener(
new Button.OnClickListener() {
@Override public void onClick(View v) {
makeDot(dotModel, dotView, Color.RED);
} });
((Button) findViewById(R.id.button2)).setOnClickListener(
new Button.OnClickListener() {
@Override public void onClick(View v) {
makeDot(dotModel, dotView, Color.GREEN);
} });
final EditText tb1 = (EditText) findViewById(R.id.text1);
final EditText tb2 = (EditText) findViewById(R.id.text2);
dotModel.setDotsChangeListener(new Dots.DotsChangeListener() {
@Override public void onDotsChange(Dots dots) {
Dot d = dots.getLastDot();
// This code makes the UI unacceptably unresponsive.
// ... investigating
//tb1.setText((null == d) ? "" : String.valueOf(d.getX()));
//tb2.setText((null == d) ? "" : String.valueOf(d.getY()));
dotView.invalidate();
} });
}
/** Install an options menu. */
@Override public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.simple_menu, menu);
return true;
}
/** Respond to an options menu selection. */
@Override public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_clear:
dotModel.clearDots();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
/** Install a context menu. */
@Override public void onCreateContextMenu(
ContextMenu menu,
View v,
ContextMenuInfo menuInfo)
{
menu.add(Menu.NONE, 1, Menu.NONE, "Clear")
.setAlphabeticShortcut('x');
}
/** Respond to a context menu selection. */
@Override public boolean onContextItemSelected(MenuItem item) {
switch (item.getItemId()) {
case 1:
dotModel.clearDots();
return true;
default: ;
}
return false;
}
/**
* @param dots the dots we're drawing
* @param view the view in which we're drawing dots
* @param color the color of the dot
*/
void makeDot(Dots dots, DotView view, int color) {
int pad = (DOT_DIAMETER + 2) * 2;
dots.addDot(
DOT_DIAMETER + (rand.nextFloat() * (view.getWidth() - pad)),
DOT_DIAMETER + (rand.nextFloat() * (view.getHeight() - pad)),
color,
DOT_DIAMETER);
}
}