1/* 2 * Copyright (C) 2017 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package com.googlecode.android_scripting.facade; 18 19import android.app.Service; 20import android.content.Intent; 21import android.hardware.Camera; 22import android.hardware.Camera.AutoFocusCallback; 23import android.hardware.Camera.Parameters; 24import android.hardware.Camera.PictureCallback; 25import android.net.Uri; 26import android.os.Bundle; 27import android.provider.MediaStore; 28import android.view.SurfaceHolder; 29import android.view.SurfaceHolder.Callback; 30import android.view.SurfaceView; 31import android.view.WindowManager; 32 33import com.googlecode.android_scripting.BaseApplication; 34import com.googlecode.android_scripting.FileUtils; 35import com.googlecode.android_scripting.FutureActivityTaskExecutor; 36import com.googlecode.android_scripting.Log; 37import com.googlecode.android_scripting.future.FutureActivityTask; 38import com.googlecode.android_scripting.jsonrpc.RpcReceiver; 39import com.googlecode.android_scripting.rpc.Rpc; 40import com.googlecode.android_scripting.rpc.RpcDefault; 41import com.googlecode.android_scripting.rpc.RpcParameter; 42 43import java.io.File; 44import java.io.FileNotFoundException; 45import java.io.FileOutputStream; 46import java.io.IOException; 47import java.lang.reflect.Method; 48import java.util.concurrent.CountDownLatch; 49 50/** 51 * Access Camera functions. 52 * 53 */ 54public class CameraFacade extends RpcReceiver { 55 56 private final Service mService; 57 private final Parameters mParameters; 58 59 private class BooleanResult { 60 boolean mmResult = false; 61 } 62 63 public Camera openCamera(int cameraId) throws Exception { 64 int sSdkLevel = Integer.parseInt(android.os.Build.VERSION.SDK); 65 Camera result; 66 if (sSdkLevel < 9) { 67 result = Camera.open(); 68 } else { 69 Method openCamera = Camera.class.getMethod("open", int.class); 70 result = (Camera) openCamera.invoke(null, cameraId); 71 } 72 return result; 73 } 74 75 public CameraFacade(FacadeManager manager) throws Exception { 76 super(manager); 77 mService = manager.getService(); 78 Camera camera = openCamera(0); 79 try { 80 mParameters = camera.getParameters(); 81 } finally { 82 camera.release(); 83 } 84 } 85 86 @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.") 87 public Bundle cameraCapturePicture( 88 @RpcParameter(name = "targetPath") final String targetPath, 89 @RpcParameter(name = "useAutoFocus") @RpcDefault("true") Boolean useAutoFocus, 90 @RpcParameter(name = "cameraId", description = "Id of camera to use. SDK 9") @RpcDefault("0") Integer cameraId) 91 throws Exception { 92 final BooleanResult autoFocusResult = new BooleanResult(); 93 final BooleanResult takePictureResult = new BooleanResult(); 94 Camera camera = openCamera(cameraId); 95 camera.setParameters(mParameters); 96 97 try { 98 Method method = camera.getClass().getMethod("setDisplayOrientation", int.class); 99 method.invoke(camera, 90); 100 } catch (Exception e) { 101 Log.e(e); 102 } 103 104 try { 105 FutureActivityTask<SurfaceHolder> previewTask = setPreviewDisplay(camera); 106 camera.startPreview(); 107 if (useAutoFocus) { 108 autoFocus(autoFocusResult, camera); 109 } 110 takePicture(new File(targetPath), takePictureResult, camera); 111 previewTask.finish(); 112 } catch (Exception e) { 113 Log.e(e); 114 } finally { 115 camera.release(); 116 } 117 118 Bundle result = new Bundle(); 119 result.putBoolean("autoFocus", autoFocusResult.mmResult); 120 result.putBoolean("takePicture", takePictureResult.mmResult); 121 result.putInt("cameraId", cameraId); 122 return result; 123 } 124 125 private FutureActivityTask<SurfaceHolder> setPreviewDisplay(Camera camera) throws IOException, 126 InterruptedException { 127 FutureActivityTask<SurfaceHolder> task = new FutureActivityTask<SurfaceHolder>() { 128 @Override 129 public void onCreate() { 130 super.onCreate(); 131 final SurfaceView view = new SurfaceView(getActivity()); 132 getActivity().setContentView(view); 133 getActivity().getWindow().setSoftInputMode( 134 WindowManager.LayoutParams.SOFT_INPUT_STATE_UNCHANGED); 135 view.getHolder().setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); 136 view.getHolder().addCallback(new Callback() { 137 @Override 138 public void surfaceDestroyed(SurfaceHolder holder) { 139 } 140 141 @Override 142 public void surfaceCreated(SurfaceHolder holder) { 143 setResult(view.getHolder()); 144 } 145 146 @Override 147 public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { 148 } 149 }); 150 } 151 }; 152 FutureActivityTaskExecutor taskQueue = 153 ((BaseApplication) mService.getApplication()).getTaskExecutor(); 154 taskQueue.execute(task); 155 camera.setPreviewDisplay(task.getResult()); 156 return task; 157 } 158 159 private void takePicture(final File file, final BooleanResult takePictureResult, 160 final Camera camera) throws InterruptedException { 161 final CountDownLatch latch = new CountDownLatch(1); 162 camera.takePicture(null, null, new PictureCallback() { 163 @Override 164 public void onPictureTaken(byte[] data, Camera camera) { 165 if (!FileUtils.makeDirectories(file.getParentFile(), 0755)) { 166 takePictureResult.mmResult = false; 167 return; 168 } 169 try { 170 FileOutputStream output = new FileOutputStream(file); 171 output.write(data); 172 output.close(); 173 takePictureResult.mmResult = true; 174 } catch (FileNotFoundException e) { 175 Log.e("Failed to save picture.", e); 176 takePictureResult.mmResult = false; 177 return; 178 } catch (IOException e) { 179 Log.e("Failed to save picture.", e); 180 takePictureResult.mmResult = false; 181 return; 182 } finally { 183 latch.countDown(); 184 } 185 } 186 }); 187 latch.await(); 188 } 189 190 private void autoFocus(final BooleanResult result, final Camera camera) 191 throws InterruptedException { 192 final CountDownLatch latch = new CountDownLatch(1); 193 { 194 camera.autoFocus(new AutoFocusCallback() { 195 @Override 196 public void onAutoFocus(boolean success, Camera camera) { 197 result.mmResult = success; 198 latch.countDown(); 199 } 200 }); 201 latch.await(); 202 } 203 } 204 205 @Override 206 public void shutdown() { 207 // Nothing to clean up. 208 } 209 210 @Rpc(description = "Starts the image capture application to take a picture and saves it to the specified path.") 211 public void cameraInteractiveCapturePicture( 212 @RpcParameter(name = "targetPath") final String targetPath) { 213 Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); 214 File file = new File(targetPath); 215 intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file)); 216 AndroidFacade facade = mManager.getReceiver(AndroidFacade.class); 217 facade.startActivityForResult(intent); 218 } 219} 220