/*
* Copyright (C) 2017 The Android Open Source Project
*
* 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 com.googlecode.android_scripting.facade;
import android.app.Service;
import android.content.Intent;
import android.hardware.Camera;
import android.hardware.Camera.AutoFocusCallback;
import android.hardware.Camera.Parameters;
import android.hardware.Camera.PictureCallback;
import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.view.SurfaceHolder;
import android.view.SurfaceHolder.Callback;
import android.view.SurfaceView;
import android.view.WindowManager;
import com.googlecode.android_scripting.BaseApplication;
import com.googlecode.android_scripting.FileUtils;
import com.googlecode.android_scripting.FutureActivityTaskExecutor;
import com.googlecode.android_scripting.Log;
import com.googlecode.android_scripting.future.FutureActivityTask;
import com.googlecode.android_scripting.jsonrpc.RpcReceiver;
import com.googlecode.android_scripting.rpc.Rpc;
import com.googlecode.android_scripting.rpc.RpcDefault;
import com.googlecode.android_scripting.rpc.RpcParameter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.concurrent.CountDownLatch;
/**
* Access Camera functions.
*
*/
public class CameraFacade extends RpcReceiver {
private final Service mService;
private final Parameters mParameters;
private class BooleanResult {
boolean mmResult = false;
}
public Camera openCamera(int cameraId) throws Exception {
int sSdkLevel = Integer.parseInt(android.os.Build.VERSION.SDK);
Camera result;
if (sSdkLevel < 9) {
result = Camera.open();
} else {
Method openCamera = Camera.class.getMethod("open", int.class);
result = (Camera) openCamera.invoke(null, cameraId);
}
return result;
}
public CameraFacade(FacadeManager manager) throws Exception {
super(manager);
mService = manager.getService();
Camera camera = openCamera(0);
try {
mParameters = camera.getParameters();
} finally {
camera.release();
}
}
@Rpc(description = "Take a picture and save it to the specified path.", returns = "A map of Booleans autoFocus and takePicture where True indicates success. cameraId also included.")
public Bundle cameraCapturePicture(
@RpcParameter(name = "targetPath") final String targetPath,
@RpcParameter(name = "useAutoFocus") @RpcDefault("true") Boolean useAutoFocus,
@RpcParameter(name = "cameraId", description = "Id of camera to use. SDK 9") @RpcDefault("0") Integer cameraId)
throws Exception {
final BooleanResult autoFocusResult = new BooleanResult();
final BooleanResult takePictureResult = new BooleanResult();
Camera camera = openCamera(cameraId);
camera.setParameters(mParameters);
try {
Method method = camera.getClass().getMethod("setDisplayOrientation", int.class);
method.invoke(camera, 90);
} catch (Exception e) {
Log.e(e);
}
try {
FutureActivityTask<SurfaceHolder> previewTask = setPreviewDisplay(camera);
camera.startPreview();
if (useAutoFocus) {
autoFocus(autoFocusResult, camera);
}
takePicture(new File(targetPath), takePictureResult, camera);
previewTask.finish();
} catch (Exception e) {
Log.e(e);
} finally {
camera.release();
}
Bundle result = new Bundle();
result.putBoolean("autoFocus", autoFocusResult.mmResult);
result.putBoolean("takePicture", takePictureResult.mmResult);
result.putInt("cameraId", cameraId);
return result;
}
private FutureActivityTask<SurfaceHolder> setPreviewDisplay(Camera camera) throws IOException,
InterruptedException {
FutureActivityTask<SurfaceHolder> task = new FutureActivityTask<SurfaceHolder>() {
@Override
public void onCreate() {
super.onCreate();
final SurfaceView view = new SurfaceView(getActivity());
getActivity().setContentView(view);
getActivity().getWindow().setSoftInputMode(
WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED);
view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
view.getHolder().addCallback(new Callback() {
@Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
@Override
public void surfaceCreated(SurfaceHolder holder) {
setResult(view.getHolder());
}
@Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
});
}
};
FutureActivityTaskExecutor taskQueue =
((BaseApplication) mService.getApplication()).getTaskExecutor();
taskQueue.execute(task);
camera.setPreviewDisplay(task.getResult());
return task;
}
private void takePicture(final File file, final BooleanResult takePictureResult,
final Camera camera) throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
camera.takePicture(null, null, new PictureCallback() {
@Override
public void onPictureTaken(byte[] data, Camera camera) {
if (!FileUtils.makeDirectories(file.getParentFile(), 0755)) {
takePictureResult.mmResult = false;
return;
}
try {
FileOutputStream output = new FileOutputStream(file);
output.write(data);
output.close();
takePictureResult.mmResult = true;
} catch (FileNotFoundException e) {
Log.e("Failed to save picture.", e);
takePictureResult.mmResult = false;
return;
} catch (IOException e) {
Log.e("Failed to save picture.", e);
takePictureResult.mmResult = false;
return;
} finally {
latch.countDown();
}
}
});
latch.await();
}
private void autoFocus(final BooleanResult result, final Camera camera)
throws InterruptedException {
final CountDownLatch latch = new CountDownLatch(1);
{
camera.autoFocus(new AutoFocusCallback() {
@Override
public void onAutoFocus(boolean success, Camera camera) {
result.mmResult = success;
latch.countDown();
}
});
latch.await();
}
}
@Override
public void shutdown() {
// Nothing to clean up.
}
@Rpc(description = "Starts the image capture application to take a picture and saves it to the specified path.")
public void cameraInteractiveCapturePicture(
@RpcParameter(name = "targetPath") final String targetPath) {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File file = new File(targetPath);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
AndroidFacade facade = mManager.getReceiver(AndroidFacade.class);
facade.startActivityForResult(intent);
}
}