Rafael T Rafael T - 3 months ago 26
Android Question

Android onActivityResult not called / triggered

I read much about issues with onActivityResult, but it seems that none of the described problems fit to mine, like putting a negative requestCode in startActivityForResult or something else.

I'm playing with the camera in my Activity, which keeps capturing photos from my webcam.
If I have desired content in my SurfaceView, I close cam releasing its resources, setResult(RESULT_OK, DataIntent) and hoping that onActivityResult is triggered in my parent. But it doesn't. If I set a result in onCreate of childActivity and finish child in onCreate the result is passed to onActivityResult.

What possible reason could it be that onActivityResult is NOT getting triggered?
I'll write some of my source down for understanding of what I'm doing...

public class MainActivity extends Activity {
Button mButtonScan;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);

mButtonScan = (Button)findViewById(R.id.main_btn_scan);
}

/**
* OnClick Event called from main.xml
* @param v View that called that onClickEvent
*/
public void btnCaptureClick(View v) {
Intent intent = new Intent(this, CaptureActivity.class);
startActivityForResult(intent, Constants.REQUEST_CODE_CAPTURE);
}

/**
* callback for this Activity. Called when an Activity which was started by
* this.startActivityForResult(intent, requestCode) sets its result and calls finish()
*/
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
String foo = "foo";
switch (requestCode) {
case Constants.REQUEST_CODE_CAPTURE:
switch (resultCode) {
case RESULT_FIRST_USER:
Toast.makeText(this, data.getStringExtra(Config.SCAN_RESULT_TEXT), Toast.LENGTH_LONG).show();
break;
case RESULT_CANCELED:
break;
default:
break;
}
break;

default:
super.onActivityResult(requestCode, resultCode, data);
break;
}
}
}


public class CaptureActivity extends Activity implements ActivityCallback, SurfaceHolder.Callback, PreviewCallback {

private Preview mPreview;
private Camera mCam;
private SurfaceHolder mHolder;
private Size size;

protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.capture);

mPreview = (Preview)findViewById(R.id.capture_preview);
}

@Override
public void onValidDecodeResult(Result rawResult, Bitmap barcode) {
Intent intent = new Intent();
if (rawResult != null && barcode != null) {
intent.putExtra(Config.SCAN_RESULT_TEXT, rawResult.getText());
intent.putExtra(Config.SCAN_RESULT_FORMAT, rawResult.getBarcodeFormat().getName());
intent.putExtra(Config.SCAN_RESULT_BMP, barcode);
} else {
intent.putExtra(Config.SCAN_RESULT_TEXT, "foo");
intent.putExtra(Config.SCAN_RESULT_FORMAT, "bar");
intent.putExtra(Config.SCAN_RESULT_BMP, "barcode");
}
mPreview = null;
setResult(Activity.RESULT_FIRST_USER, intent);
finish();
}

@Override
public void onPreviewFrame(byte[] data, Camera camera) {
MultiFormatReader reader = new MultiFormatReader();
PlanarYUVLuminanceSource source = new PlanarYUVLuminanceSource(data, size.width, size.height, 160, 60, 480, 360);
GlobalHistogramBinarizer binarizer = new GlobalHistogramBinarizer(source);
BinaryBitmap bb = new BinaryBitmap(binarizer);
Result result = null;
try {
result = reader.decode(bb);
} catch (NotFoundException e) {
//do NOTHING cause e == null
} catch (Exception e){
e.printStackTrace();
} finally {
reader.reset();
}
if (result != null) {
mCam.stopPreview();
releaseCameraResources();
onValidDecodeResult(result, source.renderCroppedGreyscaleBitmap());
} else {
camera.setOneShotPreviewCallback(this);
}
}

@Override
public void surfaceCreated(SurfaceHolder holder) {
try {
mCam = Camera.open();
mCam.setPreviewDisplay(mPreview.getHolder());
} catch (IOException e) {
releaseCameraResources();
e.printStackTrace();
}
}

private void releaseCameraResources() {
mCam.release();
mCam = null;
}

@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
//begin Preview
Camera.Parameters parameters = mCam.getParameters();

List<Size> sizes = parameters.getSupportedPreviewSizes();

size = getOptimalPreviewSize(sizes, width, height);
parameters.setPreviewSize(size.width, size.height);

mCam.setParameters(parameters);
mCam.startPreview();
mCam.setOneShotPreviewCallback(this);
}

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCam != null) {
mCam.stopPreview();
releaseCameraResources();
}
}

private Size getOptimalPreviewSize(List<Size> sizes, int width, int height) {
final double ASPECT_TOLERANCE = 0.05;
double targetRatio = (double) width / height;

if (sizes == null) return null;
Size optimalSize = null;
double minDiff = Double.MAX_VALUE;

int targetHeight = height;

for (Size size: sizes) {
double ratio = (double) size.width / size.height;
if(Math.abs(ratio - targetRatio) > ASPECT_TOLERANCE) {
continue;
}

if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}

if (optimalSize == null) {
//cannot find matching aspect-ratio
minDiff = Double.MAX_VALUE;
for (Size size : sizes) {
if (Math.abs(size.height - targetHeight) < minDiff) {
optimalSize = size;
minDiff = Math.abs(size.height - targetHeight);
}
}
}
return optimalSize;
}
}

Answer

I solved my problem myself now. After giving up to get onActivityResult triggering, I decided to 'hack' Androids encapsulating by passing a static reference from MainActivity to CaptureActivity, even if I know that this isn't a good idea. After the finish() call, MAGICALLY onActivityResult gets triggered with cancel Result... as expected because I don't call setResult anymore. getting onActivityResult triggered I investigated why it is working now. I found out, that it has something to do with the bitmap, passed to an Intent. If I put parcellable BitMap into my resultIntent onActivityResult never gets fired. So removing following line in the code above will work:

intent.putExtra(Config.SCAN_RESULT_BMP, barcode);

Thats ok for me, because I don't needed the BitMap really in this other Activity. It was more a 'feature' than a need. If some of You guys want to pass big Data into an Intent like a Bitmap, think about storing it somewhere on SD, passing the path in the Intent, while reading this Bitmap from SD in ParentActivity.