package hk.hku.cs.srli.supermonkey; import android.os.Bundle; import android.util.Log; import android.widget.Toast; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.app.Activity; import android.app.AlertDialog; import android.app.ProgressDialog; import android.content.DialogInterface; import hk.hku.cs.srli.supermonkey.service.CalibratingController; import java.util.ArrayDeque; import java.util.Deque; public class CalibrationActivity extends Activity { private CalibrationView cview; private ProgressDialog progressDialog; private Choreographer choreographer; private CalibratingController calibCtrl; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.v("CalibrationActivity", "onCreate"); cview = new CalibrationView(this); // Create a progress dialog. progressDialog = new ProgressDialog(this); progressDialog.setIndeterminate(true); progressDialog.setCancelable(false); choreographer = new Choreographer(); cview.setListener(choreographer); setContentView(cview); calibCtrl = new CalibratingController(this, new CalibrationCallback()); // Show loading indicator. progressDialog.setMessage("Starting..."); progressDialog.show(); } @Override protected void onStart() { Log.v("CalibrationActivity", "onStart"); calibCtrl.bind(); super.onStart(); } @Override protected void onStop() { Log.v("CalibrationActivity", "onStop"); calibCtrl.abortCalibration(); calibCtrl.unbind(); super.onStop(); } private void onDanceFinished() { progressDialog.setMessage("Computing..."); progressDialog.show(); calibCtrl.computeCalibration(); } private void showInfoDialog() { // Create a information dialog. new AlertDialog.Builder(CalibrationActivity.this) .setTitle("Calibration started") .setMessage("Please follow the green dot until calibration is finished.") .setPositiveButton("OK", new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int which) { // Start animation. choreographer.startDance(); } }).setOnCancelListener(new DialogInterface.OnCancelListener() { @Override public void onCancel(DialogInterface dialog) { // Cancel and stop calibration. finish(); } }).show(); } private class CalibrationCallback implements CalibratingController.Callback { @Override public void onServiceBound() { calibCtrl.startCalibration(); } @Override public void handleDConnect(boolean connected) { if (!connected) { // Something went wrong. Toast.makeText(CalibrationActivity.this, "Connection lost", Toast.LENGTH_LONG).show(); finish(); } } @Override public void handleStarted() { progressDialog.dismiss(); showInfoDialog(); } @Override public void handleAdded() { // Stop animation from looping. choreographer.setLooping(false); } @Override public void handleDone() { progressDialog.dismiss(); Toast.makeText(CalibrationActivity.this, "Calibration finished", Toast.LENGTH_LONG).show(); } @Override public void handleStopped() { choreographer.abort(); finish(); } @Override public void handleError(String message) { Toast.makeText(CalibrationActivity.this, "Error: " + message, Toast.LENGTH_LONG).show(); choreographer.abort(); finish(); } } private class Choreographer extends AnimatorListenerAdapter{ private Deque<Movement> sequence; private boolean looping; private boolean abort; public Choreographer() { sequence = new ArrayDeque<Movement>(); } public void startDance() { prepareSequence(); looping = false; abort = false; nextMove(); } public void nextMove() { if (abort) { return; } else if (sequence.peek() != null) { sequence.poll().move(); } else { // Finish when there's no more movement in the sequence. onDanceFinished(); } } public void setLooping(boolean looping) { this.looping = looping; } public void abort() { abort = true; } @Override public void onAnimationEnd(Animator animation) { this.nextMove(); } private void prepareSequence() { sequence.clear(); addPoint(0.1f, 0.9f); addPoint(0.5f, 0.9f); addPoint(0.9f, 0.9f); addPoint(0.9f, 0.5f); addPoint(0.5f, 0.5f); addPoint(0.1f, 0.5f); addPoint(0.1f, 0.1f); addPoint(0.5f, 0.1f); addPoint(0.9f, 0.1f); // Hide the point when animation is finished. sequence.add(new Movement() { @Override public void move() { cview.hidePoint(); } }); } private void addPoint(final float x, final float y) { sequence.add(new Movement() { @Override public void move() { cview.movePointTo(x, y); } }); // Calibrate for each point. sequence.add(new ShrinkPoint()); sequence.add(new Movement() { @Override public void move() { calibCtrl.addCalibrationPoint(x, y); // Start looping while eye tracker collects data. looping = true; Choreographer.this.nextMove(); } }); sequence.add(new ExpandPoint()); sequence.add(new LoopingTrap()); } private class ShrinkPoint implements Movement { @Override public void move() { cview.shrinkPoint(); } } private class ExpandPoint implements Movement { @Override public void move() { cview.expandPoint(); } } private class LoopingTrap implements Movement { @Override public void move() { if (looping) { sequence.addFirst(new LoopingTrap()); sequence.addFirst(new ExpandPoint()); sequence.addFirst(new ShrinkPoint()); } Choreographer.this.nextMove(); } } } private interface Movement { public void move(); } }