import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
+import android.os.Handler;
import android.os.Message;
import android.os.Parcelable;
import android.util.Log;
/* * * * * * Private state * * * * * */
- private Resources res; // Android
- private int[] response; // For state saving
- private Thread calcThread;
+ private Resources res; // Android
+ private int[] response; // For state saving
+ private CalcState calcstate; // For onRetainNonConfigurationInstance
+
+ private class CalcState {
+ Handler uih; // UI thread waiting for results
+ VCPassActivity self; // The object of the UI
+
+ Thread calcThread; // The worker
+ Intent spawner; // The intent which spawned it,
+ // also used as the callback intent.
+ String e; // Error
+ };
/* * * * * * Private utility functions * * * * * */
finish();
}
- private final void
- postYieldError(final Intent s, final String e) {
- runOnUiThread(new Runnable(){
- public final void run() {
- yieldError(s,e);
- }
- });
- }
-
/** Prepare an int array for use as a response store */
private static final void
resetResponse(int[] chal) {
}
}
+ private static final void update_canvas_touch(
+ Canvas c,
+ Paint p,
+ int ix)
+ {
+ int x = ix % VCParameters.GRID_X;
+ int y = ix / VCParameters.GRID_X;
+
+ float w = c.getWidth();
+ float h = c.getHeight();
+
+ Rect sq = new Rect((int)(x*w
+ /VCParameters.GRID_X
+ ),
+ (int)(y*h
+ /VCParameters.GRID_Y
+ ),
+ (int)((x+1)*w
+ /VCParameters.GRID_X
+ ),
+ (int)((y+1)*h
+ /VCParameters.GRID_Y
+ ));
+ c.drawRect(sq, p);
+ }
+
private final class
VCPassTouchHandler
implements View.OnTouchListener {
private Canvas c;
private Paint p;
- public VCPassTouchHandler(Canvas c, int[] r) {
+ public VCPassTouchHandler(Canvas c, Paint p, int[] r) {
this.r = r;
this.c = c;
- p = new Paint();
- p.setARGB(127,0,255,0);
+ this.p = p;
}
private int expected_motion = MotionEvent.ACTION_DOWN;
label = VCParameters.VCVOC_DISTING_UP;
}
}
- r[VCParameters.GRID_X*last_down_y
- + last_down_x] = label;
+ int ix = VCParameters.GRID_X*last_down_y + last_down_x;
+ r[ix] = label;
expected_motion = MotionEvent.ACTION_DOWN;
- Rect sq = new Rect((int)(last_down_x*w
- /VCParameters.GRID_X
- ),
- (int)(last_down_y*h
- /VCParameters.GRID_Y
- ),
- (int)((last_down_x+1)*w
- /VCParameters.GRID_X
- ),
- (int)((last_down_y+1)*h
- /VCParameters.GRID_Y
- ));
- Log.d("VCPTH", encodeResponse(r).toString());
- c.drawRect(sq, p);
+ // Log.d("VCPTH", encodeResponse(r).toString());
+ update_canvas_touch(c,p,ix);
v.invalidate();
} else {
throw new RuntimeException("Unexpected action:" + action + "\n");
return;
}
- /*
- * XXX Do we have to draw blanks here? Does our graphic state
- * get saved for us?
- */
-
- Bitmap chal = origchal.copy(origchal.getConfig(), true);
- Canvas c = new Canvas(chal);
-
// Switch off the titlebar
// requestWindowFeature(Window.FEATURE_NO_TITLE);
- // Lock orientation
- setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
// Load layout
setContentView(R.layout.vcpact);
+ // Lock orientation
+ setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+
+ Bitmap chal = origchal.copy(origchal.getConfig(), true);
+ Canvas c = new Canvas(chal);
+
+ Paint imgupdp = new Paint();
+ imgupdp.setARGB(127,0,255,0);
+
+ for(int ix = 0; ix < response.length; ix++) {
+ if(response[ix] != -1) { update_canvas_touch(c,imgupdp,ix); }
+ }
ImageView imgview = (ImageView) findViewById(R.id.image);
imgview.setScaleType(ImageView.ScaleType.FIT_CENTER);
imgview.setImageBitmap(chal);
- imgview.setOnTouchListener(new VCPassTouchHandler(c, response));
+ imgview.setOnTouchListener(new VCPassTouchHandler(c, imgupdp, response));
imgview.getViewTreeObserver()
.addOnPreDrawListener(
new VCPassPreDrawListener(spawner, imgview, origchal));
}
- private final void
- _intent_createChallenge(final Intent spawner,
+ private static final void
+ _intent_createChallenge(final CalcState cs,
boolean quiet) {
- char[] useed = spawner.getCharArrayExtra(EXTRA_USER_SLIDE_SEED );
- char[] vseed = spawner.getCharArrayExtra(EXTRA_VOCABULARY_SEED );
- int minevt = spawner.getIntExtra (EXTRA_MINIMUM_EVENTS, -1);
+ char[] useed = cs.spawner.getCharArrayExtra(EXTRA_USER_SLIDE_SEED );
+ char[] vseed = cs.spawner.getCharArrayExtra(EXTRA_VOCABULARY_SEED );
+ int minevt = cs.spawner.getIntExtra (EXTRA_MINIMUM_EVENTS, -1);
if(useed == null || vseed == null) {
- postYieldError(spawner, "Null seed");
+ cs.e = "Null seed";
+ cs.calcThread = null;
+ synchronized(cs) {
+ cs.calcThread = null;
+ finishIfAnswered(cs);
+ }
return;
}
final VCGenerator.ProgCallback pcb
= quiet ? null : new VCGenerator.ProgCallback() {
public void progress(final int x) {
- runOnUiThread(new Runnable() {
- public final void run() {
- getWindow().setFeatureInt(
- Window.FEATURE_PROGRESS,
- x*9000
- /VCParameters.GRID_X
- /VCParameters.GRID_Y);
- }
- });
+ if(cs.uih != null) {
+ cs.uih.post(new Runnable() {
+ public final void run() {
+ cs.self.getWindow().setFeatureInt(
+ Window.FEATURE_PROGRESS,
+ x*9000
+ /VCParameters.GRID_X
+ /VCParameters.GRID_Y);
+ }
+ });
+ }
}
};
CreatedChallenge cc = do_createChallenge(useed, vseed, minevt, pcb);
if(cc.error != null) {
- postYieldError(spawner, cc.error);
- return;
+ cs.e = cc.error;
+ } else {
+ cs.spawner.putExtra(EXTRA_CHALLENGE, (Parcelable)cc.bm);
+ cs.spawner.putExtra(EXTRA_SECRET, cc.plain);
+ Log.d("VCPassAct_i_cC", cc.plain);
+ cs.calcThread = null;
}
- spawner.putExtra(EXTRA_CHALLENGE, (Parcelable)cc.bm);
- spawner.putExtra(EXTRA_SECRET, cc.plain);
-
- calcThread = null;
-
- runOnUiThread(new Runnable() {
- final public void run() {
- setResult(RESULT_OK, spawner);
- finish();
- }
- });
+ synchronized(cs) {
+ // This will either work or, if we're UIless, will get
+ // picked up on resume when the UI starts back up again
+ finishIfAnswered(cs);
+ }
}
private final void
setContentView(tv);
}
- calcThread = new Thread(new Runnable() {
- public final void run() {
- _intent_createChallenge(spawner, quiet);
- }
- });
-
- calcThread.start();
+ Object glnci = getLastNonConfigurationInstance();
+ if(glnci != null) {
+ Log.d(DBGN, "glnci is nonnull, checking...");
+ calcstate = (CalcState) glnci;
+ synchronized(calcstate) {
+ calcstate.uih = new Handler();
+ calcstate.self = this;
+ finishIfAnswered(calcstate);
+ }
+ } else {
+ calcstate = new CalcState();
+ calcstate.spawner = spawner;
+ calcstate.uih = new Handler();
+ calcstate.self = this;
+ calcstate.calcThread = new Thread(new Runnable() {
+ public final void run() {
+ _intent_createChallenge(calcstate, quiet);
+ }
+ });
+
+ calcstate.calcThread.start();
+ }
}
/* * * * * * Android interface core * * * * * */
+ private static void finishIfAnswered(final CalcState cs) {
+ Runnable r = null;
+ if(cs.uih == null) {
+ Log.d(DBGN, "Null cs.uih; can't finish now!");
+ return;
+ }
+ if(cs.e != null) {
+ r = new Runnable(){ public void run() {
+ cs.self.yieldError(cs.spawner,cs.e);
+ }};
+ } else if(cs.calcThread == null) {
+ r = new Runnable(){ public void run() {
+ cs.self.setResult(RESULT_OK, cs.spawner);
+ cs.self.finish();
+ }};
+ }
+ if(r != null) {
+ cs.uih.post(r);
+ }
+ }
+
@Override
public void onCreate(Bundle sis)
{
super.onCreate(sis);
- Log.i(DBGN, "CREATE");
-
res = getResources();
Intent spawner = getIntent();
public void onPause() {
super.onPause();
- if(isFinishing()) {
- if(calcThread != null) { calcThread.interrupt(); }
- }
+ Log.d(DBGN, "onPause");
+
+ if(calcstate != null) {
+ synchronized(calcstate) {
+ if(isFinishing()) {
+ if(calcstate.calcThread != null) {
+ calcstate.calcThread.interrupt();
+ }
+ }
+
+ finishIfAnswered(calcstate);
+ calcstate.uih = null;
+ }
+ }
}
@Override
outState.putIntArray(SAVED_STATE_KEY_RESPONSE, response);
}
}
+
+ @Override
+ public Object onRetainNonConfigurationInstance() {
+ Log.d(DBGN, "ornci!");
+ return calcstate;
+ }
}
VCPassImport
extends Activity
{
- private static final boolean TRY_QRCODE = false;
+ private static final boolean TRY_QRCODE = true;
/* * * * * * Protocol parameters * * * * * */
intent.putExtra(VCPassActivity.EXTRA_CHALLENGE,cfpc);
intent.putExtra(VCPassActivity.EXTRA_PROMPT_TEXT,
getString(R.string.import_test));
- Log.d("VCPass", intent.toString());
+ Log.d(DBGN, intent.toString());
startActivityForResult(intent, REQ_PRESENT);
}
finish();
}
+ @Override
public void onActivityResult(int req, int res, Intent data) {
+ super.onActivityResult(req, res, data);
+
if(res == RESULT_CANCELED && req != REQ_PRESENT) {
- setResult(RESULT_CANCELED, getIntent());
+ Log.d(DBGN, "CANCEL?");
+ setResult(RESULT_CANCELED, getIntent());
+ finish();
+ return;
}
if(data == null) {
// being canceled; report failure upstream
+ Log.d(DBGN, "Null data?");
setResult(RESULT_CANCELED, null);
finish();
return;
cfpc = (Bitmap) data.getParcelableExtra(
VCPassActivity.EXTRA_CHALLENGE);
cfps = data.getStringExtra(VCPassActivity.EXTRA_SECRET);
+
launchChallenger();
break;
case REQ_PRESENT:
Log.i(DBGN, "CREATE");
+ String act = getIntent().getAction();
+ Log.d(DBGN, act);
+
if(sis != null) {
useed = sis.getCharArray (SAVED_STATE_USEED);
vseed = sis.getCharArray (SAVED_STATE_VSEED);
cfpc = (Bitmap) sis.getParcelable(SAVED_STATE_CFPC );
cfps = sis.getString (SAVED_STATE_CFPS );
- }
-
- String act = getIntent().getAction();
- Log.d(DBGN, act);
-
- if (act.equals(ACTION_IMPORT_SEED)
- || act.equals(ACTION_IMPORT_AND_CREATE)) {
- importSeed();
- } else {
- setResult(RESULT_CANCELED, null);
- finish();
- }
+ } else {
+ if (act.equals(ACTION_IMPORT_SEED)
+ || act.equals(ACTION_IMPORT_AND_CREATE)) {
+ importSeed();
+ } else {
+ setResult(RESULT_CANCELED, null);
+ finish();
+ }
+ }
}
@Override
public void onSaveInstanceState(Bundle outState) {
+
+ Log.i(DBGN, "onSaveInstanceState");
+
outState.putCharArray (SAVED_STATE_USEED, useed);
outState.putCharArray (SAVED_STATE_VSEED, vseed);
outState.putParcelable(SAVED_STATE_CFPC , cfpc );
outState.putString (SAVED_STATE_CFPS , cfps );
}
-
}