/*
* FreeOTP
*
* Authors: Nathaniel McCallum <npmccallum@redhat.com>
*
* Copyright (C) 2013 Nathaniel McCallum, Red Hat
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.fedorahosted.freeotp.add;
import java.util.List;
import org.fedorahosted.freeotp.R;
import org.fedorahosted.freeotp.Token;
import org.fedorahosted.freeotp.TokenPersistence;
import android.annotation.TargetApi;
import android.app.Activity;
import android.hardware.Camera;
import android.hardware.Camera.CameraInfo;
import android.hardware.Camera.Parameters;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Surface;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.View;
import android.widget.ImageView;
import android.widget.ProgressBar;
import com.squareup.picasso.Callback;
import com.squareup.picasso.Picasso;
public class ScanActivity extends Activity implements SurfaceHolder.Callback {
private final CameraInfo mCameraInfo = new CameraInfo();
private final ScanAsyncTask mScanAsyncTask;
private final int mCameraId;
private Handler mHandler;
private Camera mCamera;
private static class AutoFocusHandler extends Handler implements Camera.AutoFocusCallback {
private final Camera mCamera;
public AutoFocusHandler(Camera camera) {
mCamera = camera;
}
@Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
mCamera.autoFocus(this);
}
@Override
public void onAutoFocus(boolean success, Camera camera) {
sendEmptyMessageDelayed(0, 1000);
}
}
public static boolean haveCamera() {
return findCamera(new CameraInfo()) >= 0;
}
private static int findCamera(CameraInfo cameraInfo) {
int cameraId = Camera.getNumberOfCameras();
// Find a back-facing camera. Otherwise, use the first camera.
while (cameraId-- > 0) {
Camera.getCameraInfo(cameraId, cameraInfo);
if (cameraId == 0 || cameraInfo.facing == CameraInfo.CAMERA_FACING_BACK)
break;
}
return cameraId;
}
public ScanActivity() {
super();
mCameraId = findCamera(mCameraInfo);
assert mCameraId >= 0;
// Create the decoder thread
mScanAsyncTask = new ScanAsyncTask() {
@Override
protected void onPostExecute(String result) {
super.onPostExecute(result);
Token token = TokenPersistence.addWithToast(ScanActivity.this, result);
if (token == null || token.getImage() == null) {
finish();
return;
}
final ImageView image = (ImageView) findViewById(R.id.image);
Picasso.with(ScanActivity.this)
.load(token.getImage())
.placeholder(R.drawable.scan)
.into(image, new Callback() {
@Override
public void onSuccess() {
findViewById(R.id.progress).setVisibility(View.INVISIBLE);
image.setAlpha(0.9f);
image.postDelayed(new Runnable() {
@Override
public void run() {
finish();
}
}, 2000);
}
@Override
public void onError() {
finish();
}
});
}
};
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.scan);
mScanAsyncTask.execute();
}
@Override
public void onDestroy() {
super.onDestroy();
mScanAsyncTask.cancel(true);
}
@Override
protected void onStart() {
super.onStart();
((SurfaceView) findViewById(R.id.surfaceview)).getHolder().addCallback(this);
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
if (mCamera == null)
return;
// The code in this section comes from the developer docs. See:
// http://developer.android.com/reference/android/hardware/Camera.html#setDisplayOrientation(int)
int rotation = 0;
switch (getWindowManager().getDefaultDisplay().getRotation()) {
case Surface.ROTATION_0:
rotation = 0;
break;
case Surface.ROTATION_90:
rotation = 90;
break;
case Surface.ROTATION_180:
rotation = 180;
break;
case Surface.ROTATION_270:
rotation = 270;
break;
}
int result = 0;
switch (mCameraInfo.facing) {
case Camera.CameraInfo.CAMERA_FACING_FRONT:
result = (mCameraInfo.orientation + rotation) % 360;
result = (360 - result) % 360; // compensate the mirror
break;
case Camera.CameraInfo.CAMERA_FACING_BACK:
result = (mCameraInfo.orientation - rotation + 360) % 360;
break;
}
mCamera.setDisplayOrientation(result);
mCamera.startPreview();
if (mHandler != null)
mHandler.sendEmptyMessageDelayed(0, 100);
}
@Override
@TargetApi(14)
public void surfaceCreated(SurfaceHolder holder) {
surfaceDestroyed(holder);
try {
// Open the camera
mCamera = Camera.open(mCameraId);
mCamera.setPreviewDisplay(holder);
mCamera.setPreviewCallback(mScanAsyncTask);
} catch (Exception e) {
e.printStackTrace();
surfaceDestroyed(holder);
// Show error message
findViewById(R.id.surfaceview).setVisibility(View.INVISIBLE);
findViewById(R.id.progress).setVisibility(View.INVISIBLE);
findViewById(R.id.window).setVisibility(View.INVISIBLE);
findViewById(R.id.textview).setVisibility(View.VISIBLE);
return;
}
// Set auto-focus mode
Parameters params = mCamera.getParameters();
List<String> modes = params.getSupportedFocusModes();
if (modes.contains(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE))
params.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
else if (modes.contains(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO))
params.setFocusMode(Parameters.FOCUS_MODE_CONTINUOUS_VIDEO);
else if (modes.contains(Parameters.FOCUS_MODE_AUTO)) {
params.setFocusMode(Parameters.FOCUS_MODE_AUTO);
mHandler = new AutoFocusHandler(mCamera);
}
mCamera.setParameters(params);
}
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
if (mCamera == null)
return;
if (mHandler != null) {
mCamera.cancelAutoFocus();
mHandler.removeMessages(0);
mHandler = null;
}
mCamera.stopPreview();
mCamera.setPreviewCallback(null);
mCamera.release();
mCamera = null;
}
}